Beispiel #1
0
        /// <summary>
        /// <exception cref="ScriptException"></exception>
        /// </summary>
        /// <param name="txContainingThis"></param>
        /// <param name="index"></param>
        /// <param name="script"></param>
        /// <param name="stack"></param>
        private static void executeScript(Transaction txContainingThis,long index,Script script,Stack<byte[]> stack)
        {
            int opCount=0;
            int lastCodeSepLocation=0;

            Stack<byte[]> altstack=new Stack<byte[]>();
            Stack<bool> ifStack=new Stack<bool>();

            foreach(ScriptChunk chunk in script.chunks)
            {
                bool shouldExecute=!ifStack.Contains(false);

                if(!chunk.IsOpCode)
                {
                    if(chunk.Data.Length>520)
                    { throw new ScriptException("Attempted to push a data string larger than 520 bytes"); }

                    if(!shouldExecute)
                    { continue; }

                    stack.Push(chunk.Data);
                }
                else
                {
                    int opcode=0xFF & chunk.Data[0];
                    if(opcode>OP_16)
                    {
                        opCount++;
                        if(opCount>201)
                        { throw new ScriptException("More script operations than is allowed"); }
                    }

                    if(opcode==OP_VERIF || opcode==OP_VERNOTIF)
                    { throw new ScriptException("Script included OP_VERIF or OP_VERNOTIF"); }

                    if(opcode==OP_CAT || opcode==OP_SUBSTR || opcode==OP_LEFT || opcode==OP_RIGHT || opcode==OP_INVERT || opcode==OP_AND || opcode==OP_OR || opcode==OP_XOR || opcode==OP_2MUL || opcode==OP_2DIV || opcode==OP_MUL || opcode==OP_DIV || opcode==OP_MOD || opcode==OP_LSHIFT || opcode==OP_RSHIFT)
                    { throw new ScriptException("Script included a disabled Script Op."); }

                    switch (opcode)
                    {
                        case OP_IF:
                            if(!shouldExecute)
                            {
                                ifStack.Push(false);
                                continue;
                            }
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_IF on an empty stack"); }
                            ifStack.Push(castToBool(stack.Pop()));
                            continue;

                        case OP_NOTIF:
                            if(!shouldExecute)
                            {
                                ifStack.Push(false);
                                continue;
                            }
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_NOTIF on an empty stack"); }
                            ifStack.Push(!castToBool(stack.Pop()));
                            continue;

                        case OP_ELSE:
                            if(ifStack.Count==0)
                            { throw new ScriptException("Attempted OP_ELSE without OP_IF/NOTIF"); }
                            ifStack.Push(!ifStack.Pop());
                            continue;

                        case OP_ENDIF:
                            if(ifStack.Count==0)
                            { throw new ScriptException("Attempted OP_ENDIF without OP_IF/NOTIF"); }
                            ifStack.Pop();
                            continue;
                    }

                    if(!shouldExecute)
                    { continue; }

                    switch(opcode)
                    {
                        //case OP_0: dont know why this isnt also here in the reference client
                        case OP_1NEGATE:
                            stack.Push(MPIHelper.Encode(BigInteger.One.Negate(),false).ReverseBytes());
                            break;

                        case OP_1:
                        case OP_2:
                        case OP_3:
                        case OP_4:
                        case OP_5:
                        case OP_6:
                        case OP_7:
                        case OP_8:
                        case OP_9:
                        case OP_10:
                        case OP_11:
                        case OP_12:
                        case OP_13:
                        case OP_14:
                        case OP_15:
                        case OP_16:
                            stack.Push(MPIHelper.Encode(BigInteger.ValueOf(getOpNValue(opcode)),false).ReverseBytes());
                            break;

                        case OP_NOP:
                            break;

                        case OP_VERIFY:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_VERIFY on an empty stack"); }
                            if(!castToBool(stack.Pop()))
                            { throw new ScriptException("OP_VERIFY failed"); }
                            break;

                        case OP_RETURN:
                            throw new ScriptException("Script called OP_RETURN");

                        case OP_TOALTSTACK:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_TOALTSTACK on an empty stack"); }
                            altstack.Push(stack.Pop());
                            break;

                        case OP_FROMALTSTACK:
                            if(altstack.Count<1)
                            { throw new ScriptException("Attempted OP_TOALTSTACK on an empty altstack"); }
                            stack.Push(altstack.Pop());
                            break;

                        case OP_2DROP:
                            if(stack.Count<2)
                            { throw new ScriptException("Attempted OP_2DROP on a stack with size<2"); }
                            stack.Pop();
                            stack.Pop();
                            break;

                        case OP_2DUP:
                            if(stack.Count<2)
                            { throw new ScriptException("Attempted OP_2DUP on a stack with size<2"); }

                            Iterator<byte[]> it2DUP=stack.descendingIterator();
                            byte[] OP2DUPtmpChunk2=it2DUP.next();
                            stack.Push(it2DUP.next());
                            stack.Push(OP2DUPtmpChunk2);
                            break;

                        case OP_3DUP:
                            if(stack.Count<3)
                            { throw new ScriptException("Attempted OP_3DUP on a stack with size<3"); }

                            Iterator<byte[]> it3DUP=stack.descendingIterator();
                            byte[] OP3DUPtmpChunk3=it3DUP.next();
                            byte[] OP3DUPtmpChunk2=it3DUP.next();
                            stack.Push(it3DUP.next());
                            stack.Push(OP3DUPtmpChunk2);
                            stack.Push(OP3DUPtmpChunk3);
                            break;

                        case OP_2OVER:
                            if(stack.Count<4)
                            { throw new ScriptException("Attempted OP_2OVER on a stack with size<4"); }

                            Iterator<byte[]> it2OVER=stack.descendingIterator();
                            it2OVER.next();
                            it2OVER.next();
                            byte[] OP2OVERtmpChunk2=it2OVER.next();
                            stack.Push(it2OVER.next());
                            stack.Push(OP2OVERtmpChunk2);
                            break;

                        case OP_2ROT:
                            if(stack.Count<6)
                            { throw new ScriptException("Attempted OP_2ROT on a stack with size<6"); }

                            byte[] OP2ROTtmpChunk6=stack.Pop();
                            byte[] OP2ROTtmpChunk5=stack.Pop();
                            byte[] OP2ROTtmpChunk4=stack.Pop();
                            byte[] OP2ROTtmpChunk3=stack.Pop();
                            byte[] OP2ROTtmpChunk2=stack.Pop();
                            byte[] OP2ROTtmpChunk1=stack.Pop();
                            stack.Push(OP2ROTtmpChunk3);
                            stack.Push(OP2ROTtmpChunk4);
                            stack.Push(OP2ROTtmpChunk5);
                            stack.Push(OP2ROTtmpChunk6);
                            stack.Push(OP2ROTtmpChunk1);
                            stack.Push(OP2ROTtmpChunk2);
                            break;

                        case OP_2SWAP:
                            if(stack.Count<4)
                            { throw new ScriptException("Attempted OP_2SWAP on a stack with size<4"); }

                            byte[] OP2SWAPtmpChunk4=stack.Pop();
                            byte[] OP2SWAPtmpChunk3=stack.Pop();
                            byte[] OP2SWAPtmpChunk2=stack.Pop();
                            byte[] OP2SWAPtmpChunk1=stack.Pop();
                            stack.Push(OP2SWAPtmpChunk3);
                            stack.Push(OP2SWAPtmpChunk4);
                            stack.Push(OP2SWAPtmpChunk1);
                            stack.Push(OP2SWAPtmpChunk2);
                            break;

                        case OP_IFDUP:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_IFDUP on an empty stack"); }
                            if(castToBool(stack.getLast()))
                            { stack.Push(stack.getLast()); }
                            break;

                        case OP_DEPTH:
                            stack.Push(Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(stack.Count), false)));
                            break;

                        case OP_DROP:
                            if(stack.Count<1)
                                throw new ScriptException("Attempted OP_DROP on an empty stack");
                            stack.Pop();
                            break;

                        case OP_DUP:
                            if(stack.Count<1)
                                throw new ScriptException("Attempted OP_DUP on an empty stack");
                            stack.Push(stack.getLast());
                            break;

                        case OP_NIP:
                            if(stack.Count<2)
                                throw new ScriptException("Attempted OP_NIP on a stack with size < 2");
                            byte[] OPNIPtmpChunk=stack.Pop();
                            stack.Pop();
                            stack.Push(OPNIPtmpChunk);
                            break;

                        case OP_OVER:
                            if(stack.Count<2)
                                throw new ScriptException("Attempted OP_OVER on a stack with size < 2");
                            Iterator<byte[]> itOVER=stack.descendingIterator();
                            itOVER.next();
                            stack.Push(itOVER.next());
                            break;

                        case OP_PICK:
                        case OP_ROLL:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_PICK/OP_ROLL on an empty stack"); }

                            long val=castToBigInteger(stack.Pop()).longValue();
                            if(val<0 || val >= stack.Count)
                            { throw new ScriptException("OP_PICK/OP_ROLL attempted to get data deeper than stack size"); }
                            Iterator<byte[]> itPICK=stack.descendingIterator();
                            for(long i=0; i<val; i++)
                            { itPICK.next(); }
                            byte[] OPROLLtmpChunk=itPICK.next();
                            if(opcode == OP_ROLL)
                            { itPICK.remove(); }
                            stack.Push(OPROLLtmpChunk);
                            break;

                        case OP_ROT:
                            if(stack.Count<3)
                            { throw new ScriptException("Attempted OP_ROT on a stack with size<3"); }

                            byte[] OPROTtmpChunk3=stack.Pop();
                            byte[] OPROTtmpChunk2=stack.Pop();
                            byte[] OPROTtmpChunk1=stack.Pop();
                            stack.Push(OPROTtmpChunk2);
                            stack.Push(OPROTtmpChunk3);
                            stack.Push(OPROTtmpChunk1);
                            break;

                        case OP_SWAP:
                        case OP_TUCK:
                            if(stack.Count<2)
                            { throw new ScriptException("Attempted OP_SWAP on a stack with size<2"); }

                            byte[] OPSWAPtmpChunk2=stack.Pop();
                            byte[] OPSWAPtmpChunk1=stack.Pop();
                            stack.Push(OPSWAPtmpChunk2);
                            stack.Push(OPSWAPtmpChunk1);
                            if(opcode == OP_TUCK)
                            { stack.Push(OPSWAPtmpChunk2); }
                            break;

                        case OP_CAT:
                        case OP_SUBSTR:
                        case OP_LEFT:
                        case OP_RIGHT:
                            throw new ScriptException("Attempted to use disabled Script Op");

                        case OP_SIZE:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_SIZE on an empty stack"); }
                            stack.Push(Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(stack.getLast().Length), false)));
                            break;

                        case OP_INVERT:
                        case OP_AND:
                        case OP_OR:
                        case OP_XOR:
                            throw new ScriptException("Attempted to use disabled Script Op.");
                        case OP_EQUAL:
                            if(stack.Count<2)
                                throw new ScriptException("Attempted OP_EQUALVERIFY on a stack with size<2");
                            stack.Push(Arrays.Equals(stack.Pop(), stack.Pop()) ? new byte[] {1} : new byte[] {0});
                            break;
                        case OP_EQUALVERIFY:
                            if(stack.Count<2)
                                throw new ScriptException("Attempted OP_EQUALVERIFY on a stack with size<2");
                            if(!Arrays.Equals(stack.Pop(), stack.Pop()))
                                throw new ScriptException("OP_EQUALVERIFY: non-equal data");
                            break;
                        case OP_1ADD:
                        case OP_1SUB:
                        case OP_NEGATE:
                        case OP_ABS:
                        case OP_NOT:
                        case OP_0NOTEQUAL:
                            if(stack.Count<1)
                                throw new ScriptException("Attempted a numeric op on an empty stack");
                            BigInteger numericOPnum=castToBigInteger(stack.Pop());

                            switch (opcode) {
                            case OP_1ADD:
                                numericOPnum=numericOPnum.Add(BigInteger.One);
                                break;
                            case OP_1SUB:
                                numericOPnum=numericOPnum.Subtract(BigInteger.One);
                                break;
                            case OP_NEGATE:
                                numericOPnum=numericOPnum.negate();
                                break;
                            case OP_ABS:
                                if(numericOPnum.CompareTo(BigInteger.Zero)<0)
                                    numericOPnum=numericOPnum.negate();
                                break;
                            case OP_NOT:
                                if(numericOPnum.Equals(BigInteger.Zero))
                                    numericOPnum=BigInteger.One;
                                else
                                    numericOPnum=BigInteger.Zero;
                                break;
                            case OP_0NOTEQUAL:
                                if(numericOPnum.Equals(BigInteger.Zero))
                                    numericOPnum=BigInteger.Zero;
                                else
                                    numericOPnum=BigInteger.One;
                                break;
                            }

                            stack.Push(Utils.reverseBytes(Utils.encodeMPI(numericOPnum, false)));
                            break;
                        case OP_2MUL:
                        case OP_2DIV:
                            throw new ScriptException("Attempted to use disabled Script Op.");
                        case OP_ADD:
                        case OP_SUB:
                        case OP_BOOLAND:
                        case OP_BOOLOR:
                        case OP_NUMEQUAL:
                        case OP_NUMNOTEQUAL:
                        case OP_LESSTHAN:
                        case OP_GREATERTHAN:
                        case OP_LESSTHANOREQUAL:
                        case OP_GREATERTHANOREQUAL:
                        case OP_MIN:
                        case OP_MAX:
                            if(stack.Count<2)
                            { throw new ScriptException("Attempted a numeric op on a stack with size<2"); }
                            BigInteger numericOPnum2=castToBigInteger(stack.Pop());
                            BigInteger numericOPnum1=castToBigInteger(stack.Pop());

                            BigInteger numericOPresult;
                            switch (opcode)
                            {
                                case OP_ADD:
                                    numericOPresult=numericOPnum1.Add(numericOPnum2);
                                    break;

                                case OP_SUB:
                                    numericOPresult=numericOPnum1.Subtract(numericOPnum2);
                                    break;

                                case OP_BOOLAND:
                                    if(!numericOPnum1.Equals(BigInteger.Zero) && !numericOPnum2.Equals(BigInteger.Zero))
                                    { numericOPresult=BigInteger.One; }
                                    else
                                    { numericOPresult=BigInteger.Zero; }
                                    break;

                                case OP_BOOLOR:
                                    if(!numericOPnum1.Equals(BigInteger.Zero) || !numericOPnum2.Equals(BigInteger.Zero))
                                    { numericOPresult=BigInteger.One; }
                                    else
                                    { numericOPresult=BigInteger.Zero; }
                                    break;

                                case OP_NUMEQUAL:
                                    if(numericOPnum1.Equals(numericOPnum2))
                                    { numericOPresult=BigInteger.One; }
                                    else
                                    { numericOPresult=BigInteger.Zero; }
                                    break;

                                case OP_NUMNOTEQUAL:
                                    if(!numericOPnum1.Equals(numericOPnum2))
                                    { numericOPresult=BigInteger.One; }
                                    else
                                    { numericOPresult=BigInteger.Zero; }
                                    break;

                                case OP_LESSTHAN:
                                    if(numericOPnum1.CompareTo(numericOPnum2)<0)
                                    { numericOPresult=BigInteger.One; }
                                    else
                                    { numericOPresult=BigInteger.Zero; }
                                    break;

                                case OP_GREATERTHAN:
                                    if(numericOPnum1.CompareTo(numericOPnum2)>0)
                                    { numericOPresult=BigInteger.One; }
                                    else
                                    { numericOPresult=BigInteger.Zero; }
                                    break;

                                case OP_LESSTHANOREQUAL:
                                    if(numericOPnum1.CompareTo(numericOPnum2)<=0)
                                        numericOPresult=BigInteger.One;
                                    else
                                        numericOPresult=BigInteger.Zero;
                                    break;

                                case OP_GREATERTHANOREQUAL:
                                    if(numericOPnum1.CompareTo(numericOPnum2)>=0)
                                    { numericOPresult=BigInteger.One; }
                                    else
                                    { numericOPresult=BigInteger.Zero; }
                                    break;

                                case OP_MIN:
                                    if(numericOPnum1.CompareTo(numericOPnum2)<0)
                                    { numericOPresult=numericOPnum1; }
                                    else
                                    { numericOPresult=numericOPnum2; }
                                    break;

                                case OP_MAX:
                                    if(numericOPnum1.CompareTo(numericOPnum2) > 0)
                                    { numericOPresult=numericOPnum1; }
                                    else
                                    { numericOPresult=numericOPnum2; }
                                    break;

                                default:
                                    throw new RuntimeException("Opcode switched at runtime?");
                            }

                            stack.Push(Utils.reverseBytes(Utils.encodeMPI(numericOPresult, false)));
                            break;
                        case OP_MUL:
                        case OP_DIV:
                        case OP_MOD:
                        case OP_LSHIFT:
                        case OP_RSHIFT:
                            throw new ScriptException("Attempted to use disabled Script Op.");
                        case OP_NUMEQUALVERIFY:
                            if(stack.Count<2)
                            { throw new ScriptException("Attempted OP_NUMEQUALVERIFY on a stack with size<2"); }
                            BigInteger OPNUMEQUALVERIFYnum2=castToBigInteger(stack.Pop());
                            BigInteger OPNUMEQUALVERIFYnum1=castToBigInteger(stack.Pop());

                            if(!OPNUMEQUALVERIFYnum1.Equals(OPNUMEQUALVERIFYnum2))
                            { throw new ScriptException("OP_NUMEQUALVERIFY failed"); }
                            break;
                        case OP_WITHIN:
                            if(stack.Count<3)
                            { throw new ScriptException("Attempted OP_WITHIN on a stack with size<3"); }
                            BigInteger OPWITHINnum3=castToBigInteger(stack.Pop());
                            BigInteger OPWITHINnum2=castToBigInteger(stack.Pop());
                            BigInteger OPWITHINnum1=castToBigInteger(stack.Pop());
                            if(OPWITHINnum2.CompareTo(OPWITHINnum1) <= 0 && OPWITHINnum1.CompareTo(OPWITHINnum3)<0)
                            { stack.Push(Utils.reverseBytes(Utils.encodeMPI(BigInteger.One, false))); }
                            else
                            { stack.Push(Utils.reverseBytes(Utils.encodeMPI(BigInteger.Zero, false))); }
                            break;

                        case OP_RIPEMD160:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_RIPEMD160 on an empty stack"); }

                            RIPEMD160Digest digest=new RIPEMD160Digest();
                            byte[] dataToHash=stack.Pop();
                            digest.update(dataToHash, 0, dataToHash.Length);
                            byte[] ripmemdHash=new byte[20];
                            digest.doFinal(ripmemdHash, 0);
                            stack.Push(ripmemdHash);
                            break;

                        case OP_SHA1:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_SHA1 on an empty stack"); }
                            stack.Push(MessageDigest.getInstance("SHA-1").digest(stack.Pop()));
                            break;

                        case OP_SHA256:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_SHA256 on an empty stack"); }
                            stack.Push(MessageDigest.getInstance("SHA-256").digest(stack.Pop()));
                            break;

                        case OP_HASH160:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_HASH160 on an empty stack"); }
                            stack.Push(Utils.sha256hash160(stack.Pop()));
                            break;

                        case OP_HASH256:
                            if(stack.Count<1)
                            { throw new ScriptException("Attempted OP_SHA256 on an empty stack"); }
                            stack.Push(Utils.doubleDigest(stack.Pop()));
                            break;

                        case OP_CODESEPARATOR:
                            lastCodeSepLocation=chunk.startLocationInProgram + 1;
                            break;

                        case OP_CHECKSIG:
                        case OP_CHECKSIGVERIFY:
                            if(stack.Count<2)
                            { throw new ScriptException("Attempted OP_CHECKSIG(VERIFY) on a stack with size<2"); }
                            byte[] CHECKSIGpubKey=stack.Pop();
                            byte[] CHECKSIGsig=stack.Pop();

                            byte[] CHECKSIGconnectedScript=Arrays.copyOfRange(script.program, lastCodeSepLocation, script.program.Length);

                            UnsafeByteArrayOutputStream OPCHECKSIGOutStream=new UnsafeByteArrayOutputStream(CHECKSIGsig.Length + 1);
                            writeBytes(OPCHECKSIGOutStream,CHECKSIGsig);
                            CHECKSIGconnectedScript=removeAllInstancesOf(CHECKSIGconnectedScript, OPCHECKSIGOutStream.toByteArray());

                            // TODO: Use int for indexes everywhere, we can't have that many inputs/outputs
                            Sha256Hash CHECKSIGhash=txContainingThis.hashTransactionForSignature((int)index,CHECKSIGconnectedScript,CHECKSIGsig[CHECKSIGsig.Length-1]);

                            bool CHECKSIGsigValid;
                            try
                            {
                                CHECKSIGsigValid=ECKey.verify(CHECKSIGhash.getBytes(), Arrays.copyOf(CHECKSIGsig, CHECKSIGsig.Length - 1), CHECKSIGpubKey);
                            }
                            catch(Exception)
                            {
                                // There is (at least) one exception that could be hit here (EOFException, if the sig is too short)
                                // Because I can't verify there aren't more, we use a very generic Exception catch
                                CHECKSIGsigValid=false;
                            }

                            if(opcode==OP_CHECKSIG)
                            { stack.Push(CHECKSIGsigValid?new byte[] {1}:new byte[] {0}); }
                            else if(opcode==OP_CHECKSIGVERIFY)
                            {
                                if(!CHECKSIGsigValid)
                                { throw new ScriptException("Script failed OP_CHECKSIGVERIFY"); }
                            }
                            break;
                        case OP_CHECKMULTISIG:
                        case OP_CHECKMULTISIGVERIFY:
                            if(stack.Count<2)
                            { throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size<2"); }
                            int CHECKMULTISIGpubKeyCount=castToBigInteger(stack.Pop()).intValue();
                            if(CHECKMULTISIGpubKeyCount<0 || CHECKMULTISIGpubKeyCount > 20)
                            { throw new ScriptException("OP_CHECKMULTISIG(VERIFY) with pubkey count out of range"); }
                            opCount+=CHECKMULTISIGpubKeyCount;

                            if(opCount>201)
                            { throw new ScriptException("Total op count > 201 during OP_CHECKMULTISIG(VERIFY)"); }

                            if(stack.Count<CHECKMULTISIGpubKeyCount+1)
                            { throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size<num_of_pubkeys + 2"); }

                            LinkedList<byte[]> CHECKMULTISIGpubkeys=new LinkedList<byte[]>();
                            for(int i=0; i<CHECKMULTISIGpubKeyCount; i++)
                                CHECKMULTISIGpubkeys.Add(stack.Pop());

                            int CHECKMULTISIGsigCount=castToBigInteger(stack.Pop()).intValue();
                            if(CHECKMULTISIGsigCount<0 || CHECKMULTISIGsigCount > CHECKMULTISIGpubKeyCount)
                                throw new ScriptException("OP_CHECKMULTISIG(VERIFY) with sig count out of range");
                            if(stack.Count<CHECKMULTISIGsigCount + 1)
                                throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size<num_of_pubkeys + num_of_signatures + 3");

                            LinkedList<byte[]> CHECKMULTISIGsigs=new LinkedList<byte[]>();
                            for(int i=0; i<CHECKMULTISIGsigCount; i++)
                                CHECKMULTISIGsigs.Add(stack.Pop());

                            byte[] CHECKMULTISIGconnectedScript=Arrays.copyOfRange(script.program, lastCodeSepLocation, script.program.Length);

                            foreach(byte[] CHECKMULTISIGsig in CHECKMULTISIGsigs)
                            {
                                UnsafeByteArrayOutputStream OPCHECKMULTISIGOutStream=new UnsafeByteArrayOutputStream(CHECKMULTISIGsig.Length + 1);
                                writeBytes(OPCHECKMULTISIGOutStream,CHECKMULTISIGsig);
                                CHECKMULTISIGconnectedScript=removeAllInstancesOf(CHECKMULTISIGconnectedScript, OPCHECKMULTISIGOutStream.toByteArray());
                            }

                            bool CHECKMULTISIGValid=true;
                            while (CHECKMULTISIGsigs.Count > 0) {
                                byte[] CHECKMULTISIGsig=CHECKMULTISIGsigs.getFirst();
                                byte[] CHECKMULTISIGpubKey=CHECKMULTISIGpubkeys.pollFirst();

                                // We could reasonably move this out of the loop,
                                // but because signature verification is significantly more expensive than hashing, its not a big deal
                                Sha256Hash CHECKMULTISIGhash=txContainingThis.hashTransactionForSignature((int)index, CHECKMULTISIGconnectedScript,
                                        CHECKMULTISIGsig[CHECKMULTISIGsig.Length - 1]);
                                try {
                                    if(ECKey.verify(CHECKMULTISIGhash.getBytes(), Arrays.copyOf(CHECKMULTISIGsig, CHECKMULTISIGsig.Length - 1), CHECKMULTISIGpubKey))
                                        CHECKMULTISIGsigs.pollFirst();
                                } catch (Exception e) {
                                    // There is (at least) one exception that could be hit here (EOFException, if the sig is too short)
                                    // Because I can't verify there aren't more, we use a very generic Exception catch
                                }

                                if(CHECKMULTISIGsigs.Count > CHECKMULTISIGpubkeys.Count)
                                {
                                    CHECKMULTISIGValid=false;
                                    break;
                                }
                            }

                            // We uselessly remove a stack object to emulate a reference client bug
                            stack.Pop();

                            if(opcode == OP_CHECKMULTISIG)
                            { stack.Push(CHECKMULTISIGValid?new byte[] {1}:new byte[] {0}); }
                            else if(opcode==OP_CHECKMULTISIGVERIFY)
                            {
                                if(!CHECKMULTISIGValid)
                                { throw new ScriptException("Script failed OP_CHECKMULTISIGVERIFY"); }
                            }
                            break;

                        case OP_NOP1:
                        case OP_NOP2:
                        case OP_NOP3:
                        case OP_NOP4:
                        case OP_NOP5:
                        case OP_NOP6:
                        case OP_NOP7:
                        case OP_NOP8:
                        case OP_NOP9:
                        case OP_NOP10:
                            break;

                        default:
                            throw new ScriptException("Script used a reserved Op Code");
                    }
                }

                if(stack.Count+altstack.Count>1000 || stack.Count+altstack.Count<0)
                { throw new ScriptException("Stack size exceeded range"); }
            }

            if(ifStack.Count!=0)
            { throw new ScriptException("OP_IF/OP_NOTIF without OP_ENDIF"); }
        }
Beispiel #2
0
		/**
		 * This is required for signatures which use a sigHashType which cannot be represented using SigHash and anyoneCanPay
		 * See transaction c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73, which has sigHashType 0
		 * throws ScriptException
		 */
		private Sha256Hash hashTransactionForSignature(int inputIndex,byte[] connectedScript,byte sigHashType)
		{
			lock(this)
			{
				// TODO: This whole separate method should be un-necessary if we fix how we deserialize sighash flags.
				// The SIGHASH flags are used in the design of contracts, please see this page for a further understanding of
				// the purposes of the code in this method:
				//
				//   https://en.bitcoin.it/wiki/Contracts
			
				try
				{
					// Store all the input scripts and clear them in preparation for signing. If we're signing a fresh
					// transaction that step isn't very helpful, but it doesn't add much cost relative to the actual
					// EC math so we'll do it anyway.
					//
					// Also store the input sequence numbers in case we are clearing them with SigHash.NONE/SINGLE
				
					byte[][] inputScripts = new byte[inputs.size()][];
					long[] inputSequenceNumbers = new long[inputs.size()];
					for (int i = 0; i < inputs.size(); i++)
					{
						inputScripts[i] = inputs[i].getScriptBytes();
						inputSequenceNumbers[i] = inputs[i].getSequenceNumber();
						inputs[i].setScriptBytes(TransactionInput.EMPTY_ARRAY);
					}
				
					// This step has no purpose beyond being synchronized with the reference clients bugs. OP_CODESEPARATOR
					// is a legacy holdover from a previous, broken design of executing scripts that shipped in Bitcoin 0.1.
					// It was seriously flawed and would have let anyone take anyone elses money. Later versions switched to
					// the design we use today where scripts are executed independently but share a stack. This left the
					// OP_CODESEPARATOR instruction having no purpose as it was only meant to be used internally, not actually
					// ever put into scripts. Deleting OP_CODESEPARATOR is a step that should never be required but if we don't
					// do it, we could split off the main chain.
				
					connectedScript = Script.removeAllInstancesOfOp(connectedScript, Script.OP_CODESEPARATOR);
				
					// Set the input to the script of its output. Satoshi does this but the step has no obvious purpose as
					// the signature covers the hash of the prevout transaction which obviously includes the output script
					// already. Perhaps it felt safer to him in some way, or is another leftover from how the code was written.
				
					TransactionInput input = inputs.get(inputIndex);
					input.setScriptBytes(connectedScript);
					ArrayList<TransactionOutput> outputs = this.outputs;
					if((sigHashType & 0x1f) == (SigHash.NONE.ordinal() + 1))
					{
						// SIGHASH_NONE means no outputs are signed at all - the signature is effectively for a "blank cheque".
						this.outputs = new ArrayList<TransactionOutput>(0);
					
						// The signature isn't broken by new versions of the transaction issued by other parties.
					
						for(int i=0;i<inputs.size();i++)
						{
							if (i != inputIndex)
							{ inputs[i].setSequenceNumber(0); }
						}
					}
					else if((sigHashType & 0x1f) == (SigHash.SINGLE.ordinal() + 1))
					{
						// SIGHASH_SINGLE means only sign the output at the same index as the input (ie, my output).
					
						if (inputIndex >= this.outputs.size())
						{
							// The input index is beyond the number of outputs, it's a buggy signature made by a broken
							// Bitcoin implementation. The reference client also contains a bug in handling this case:
							// any transaction output that is signed in this case will result in both the signed output
							// and any future outputs to this public key being steal-able by anyone who has
							// the resulting signature and the public key (both of which are part of the signed tx input).
							// Put the transaction back to how we found it.
							// TODO: Only allow this to happen if we are checking a signature, not signing a transactions
						
							for(int i=0;i<inputs.size();i++)
							{
								inputs[i].setScriptBytes(inputScripts[i]);
								inputs[i].setSequenceNumber(inputSequenceNumbers[i]);
							}
						
							this.outputs = outputs;
						
							// Satoshis bug is that SignatureHash was supposed to return a hash and on this codepath it
							// actually returns the constant "1" to indicate an error, which is never checked for. Oops.
							return new Sha256Hash("0100000000000000000000000000000000000000000000000000000000000000");
						}
					
						// In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs before
						// that position are "nulled out". Unintuitively, the value in a "null" transaction is set to -1.
					
						this.outputs = new ArrayList<TransactionOutput>(this.outputs.subList(0, inputIndex + 1));
						for (int i = 0; i < inputIndex; i++)
						{ this.outputs.set(i, new TransactionOutput(params, this, BigInteger.valueOf(-1), new byte[] {})); }
					
						// The signature isn't broken by new versions of the transaction issued by other parties.
						for(int i=0;i<inputs.size();i++)
						{
							if (i != inputIndex)
							{ inputs[i].setSequenceNumber(0); }
						}
					}
				
					ArrayList<TransactionInput> inputs = this.inputs;
				
					if((sigHashType & 0x80) == 0x80)
					{
						// SIGHASH_ANYONECANPAY means the signature in the input is not broken by changes/additions/removals
						// of other inputs. For example, this is useful for building assurance contracts.
					
						this.inputs = new ArrayList<TransactionInput>();
						this.inputs.add(input);
					}
				
					ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(length == UNKNOWN_LENGTH ? 256 : length + 4);
					bitcoinSerialize(bos);
				
					// We also have to write a hash type (sigHashType is actually an unsigned char)
					uint32ToByteStreamLE(0x000000ff & sigHashType, bos);
				
					// Note that this is NOT reversed to ensure it will be signed correctly. If it were to be printed out
					// however then we would expect that it is IS reversed.
					Sha256Hash hash = new Sha256Hash(doubleDigest(bos.toByteArray()));
					bos.close();
				
					// Put the transaction back to how we found it.
					this.inputs = inputs;
					for (int i = 0; i < inputs.size(); i++)
					{
						inputs[i].setScriptBytes(inputScripts[i]);
						inputs[i].setSequenceNumber(inputSequenceNumbers[i]);
					}
				
					this.outputs = outputs;
					return hash;
				}
				catch(IOException e)
				{
					throw new RuntimeException(e);  // Cannot happen.
				}
			}
		}