Exemplo n.º 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"); }
        }