/// <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"); } }
/** * 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. } } }