/** * Create a program that satisfies a pay-to-script hashed OP_CHECKMULTISIG program. If given * signature list is null, incomplete scriptSig will be created with OP_0 instead of signatures */ public static Script createP2SHMultiSigInputScript( @Nullable List<TransactionSignature> signatures, Script multisigProgram) { List<byte[]> sigs = new ArrayList<byte[]>(); if (signatures == null) { // create correct number of empty signatures int numSigs = multisigProgram.getNumberOfSignaturesRequiredToSpend(); for (int i = 0; i < numSigs; i++) sigs.add(new byte[] {}); } else { for (TransactionSignature signature : signatures) { sigs.add(signature.encodeToBitcoin()); } } return createMultiSigInputScriptBytes(sigs, multisigProgram.getProgram()); }
/** * Returns a copy of the given scriptSig with the signature inserted in the given position. * * <p>This function assumes that any missing sigs have OP_0 placeholders. If given scriptSig * already has all the signatures in place, IllegalArgumentException will be thrown. * * @param targetIndex where to insert the signature * @param sigsPrefixCount how many items to copy verbatim (e.g. initial OP_0 for multisig) * @param sigsSuffixCount how many items to copy verbatim at end (e.g. redeemScript for P2SH) */ public static Script updateScriptWithSignature( Script scriptSig, byte[] signature, int targetIndex, int sigsPrefixCount, int sigsSuffixCount) { ScriptBuilder builder = new ScriptBuilder(); List<ScriptChunk> inputChunks = scriptSig.getChunks(); int totalChunks = inputChunks.size(); // Check if we have a place to insert, otherwise just return given scriptSig unchanged. // We assume here that OP_0 placeholders always go after the sigs, so // to find if we have sigs missing, we can just check the chunk in latest sig position boolean hasMissingSigs = inputChunks.get(totalChunks - sigsSuffixCount - 1).equalsOpCode(OP_0); checkArgument(hasMissingSigs, "ScriptSig is already filled with signatures"); // copy the prefix for (ScriptChunk chunk : inputChunks.subList(0, sigsPrefixCount)) builder.addChunk(chunk); // copy the sigs int pos = 0; boolean inserted = false; for (ScriptChunk chunk : inputChunks.subList(sigsPrefixCount, totalChunks - sigsSuffixCount)) { if (pos == targetIndex) { inserted = true; builder.data(signature); pos++; } if (!chunk.equalsOpCode(OP_0)) { builder.addChunk(chunk); pos++; } } // add OP_0's if needed, since we skipped them in the previous loop while (pos < totalChunks - sigsPrefixCount - sigsSuffixCount) { if (pos == targetIndex) { inserted = true; builder.data(signature); } else { builder.addChunk(new ScriptChunk(OP_0, null)); } pos++; } // copy the suffix for (ScriptChunk chunk : inputChunks.subList(totalChunks - sigsSuffixCount, totalChunks)) builder.addChunk(chunk); checkState(inserted); return builder.build(); }
/** * Adds a copy of the given byte array as a data element (i.e. PUSHDATA) at the given index in the * program. */ public ScriptBuilder data(int index, byte[] data) { // implements BIP62 byte[] copy = Arrays.copyOf(data, data.length); int opcode; if (data.length == 0) { opcode = OP_0; } else if (data.length == 1) { byte b = data[0]; if (b >= 1 && b <= 16) opcode = Script.encodeToOpN(b); else opcode = 1; } else if (data.length < OP_PUSHDATA1) { opcode = data.length; } else if (data.length < 256) { opcode = OP_PUSHDATA1; } else if (data.length < 65536) { opcode = OP_PUSHDATA2; } else { throw new RuntimeException("Unimplemented"); } return addChunk(index, new ScriptChunk(opcode, copy)); }
/** Creates a fresh ScriptBuilder with the given program as the starting point. */ public ScriptBuilder(Script template) { chunks = new ArrayList<ScriptChunk>(template.getChunks()); }
/** Creates a scriptPubKey for the given redeem script. */ public static Script createP2SHOutputScript(Script redeemScript) { byte[] hash = Utils.Hash160(redeemScript.getProgram()); return ScriptBuilder.createP2SHOutputScript(hash); }
/** Adds the given number as a OP_N opcode to the given index in the program. */ public ScriptBuilder smallNum(int index, int num) { checkArgument(num >= 0, "Cannot encode negative numbers with smallNum"); checkArgument(num <= 16, "Cannot encode numbers larger than 16 with smallNum"); return addChunk(index, new ScriptChunk(Script.encodeToOpN(num), null)); }