/** * Serializes the transaction for use in a signature * * @param index Current transaction index * @param sigHashType Signature hash type * @param subScriptBytes Replacement script for the current input * @param outBuffer Output buffer * @throws ScriptException Transaction index out-of-range */ public final void serializeForSignature( int index, int sigHashType, byte[] subScriptBytes, SerializedBuffer outBuffer) throws ScriptException { int hashType; boolean anyoneCanPay; // // The transaction input must be within range // if (index < 0 || index >= txInputs.size()) throw new ScriptException("Transaction input index is not valid"); // // Check for a valid hash type // // Note that SIGHASH_ANYONE_CAN_PAY is or'ed with one of the other hash types. So we need // to remove it when checking for a valid signature. // // SIGHASH_ALL: This is the default. It indicates that everything about the transaction is // signed // except for the input scripts. Signing the input scripts as well would // obviously make // it impossible to construct a transaction. // SIGHASH_NONE: The outputs are not signed and can be anything. This mode allows others to // update // the transaction by changing their inputs sequence numbers. This means that // all // input sequence numbers are set to 0 except for the current input. // SIGHASH_SINGLE: Outputs up to and including the current input index number are included. // Outputs // before the current index have a -1 value and an empty script. All input // sequence // numbers are set to 0 except for the current input. // // The SIGHASH_ANYONE_CAN_PAY modifier can be combined with the above three modes. When set, // only that // input is signed and the other inputs can be anything. // // In all cases, the script for the current input is replaced with the script from the connected // output. All other input scripts are set to an empty script. // // The reference client accepts an invalid hash types and treats it as SIGHASH_ALL. So we need // to // do the same. // anyoneCanPay = ((sigHashType & ScriptOpCodes.SIGHASH_ANYONE_CAN_PAY) != 0); hashType = sigHashType & (255 - ScriptOpCodes.SIGHASH_ANYONE_CAN_PAY); if (hashType != ScriptOpCodes.SIGHASH_ALL && hashType != ScriptOpCodes.SIGHASH_NONE && hashType != ScriptOpCodes.SIGHASH_SINGLE) hashType = ScriptOpCodes.SIGHASH_ALL; // // Serialize the version // outBuffer.putInt(txVersion); // // Serialize the inputs // // For SIGHASH_ANYONE_CAN_PAY, only the current input is included in the signature. // Otherwise, all inputs are included. // List<TransactionInput> sigInputs; if (anyoneCanPay) { sigInputs = new ArrayList<>(1); sigInputs.add(txInputs.get(index)); } else { sigInputs = txInputs; } outBuffer.putVarInt(sigInputs.size()); byte[] emptyScriptBytes = new byte[0]; for (TransactionInput txInput : sigInputs) txInput.serializeForSignature( index, hashType, (txInput.getIndex() == index ? subScriptBytes : emptyScriptBytes), outBuffer); // // Serialize the outputs // if (hashType == ScriptOpCodes.SIGHASH_NONE) { // // There are no outputs for SIGHASH_NONE // outBuffer.putVarInt(0); } else if (hashType == ScriptOpCodes.SIGHASH_SINGLE) { // // The output list is resized to the input index+1 // if (txOutputs.size() <= index) throw new ScriptException("Input index out-of-range for SIGHASH_SINGLE"); outBuffer.putVarInt(index + 1); for (TransactionOutput txOutput : txOutputs) { if (txOutput.getIndex() > index) break; txOutput.serializeForSignature(index, hashType, outBuffer); } } else { // // All outputs are serialized for SIGHASH_ALL // outBuffer.putVarInt(txOutputs.size()); for (TransactionOutput txOutput : txOutputs) txOutput.serializeForSignature(index, hashType, outBuffer); } // // Serialize the lock time // outBuffer.putUnsignedInt(txLockTime); }