/** Create a program that satisfies an OP_CHECKMULTISIG program. */ public static Script createMultiSigInputScript(List<TransactionSignature> signatures) { List<byte[]> sigs = new ArrayList<byte[]>(signatures.size()); for (TransactionSignature signature : signatures) { sigs.add(signature.encodeToPeercoin()); } return createMultiSigInputScriptBytes(sigs, null); }
/** * 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.encodeToPeercoin()); } } return createMultiSigInputScriptBytes(sigs, multisigProgram.getProgram()); }
@Override public boolean signInputs(ProposedTransaction propTx, KeyBag keyBag) { Transaction tx = propTx.partialTx; int numInputs = tx.getInputs().size(); for (int i = 0; i < numInputs; i++) { TransactionInput txIn = tx.getInput(i); if (txIn.getConnectedOutput() == null) { log.warn("Missing connected output, assuming input {} is already signed.", i); continue; } try { // We assume if its already signed, its hopefully got a SIGHASH type that will not // invalidate when // we sign missing pieces (to check this would require either assuming any signatures are // signing // standard output types or a way to get processed signatures out of script execution) txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey()); log.warn( "Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i); continue; } catch (ScriptException e) { // Expected. } RedeemData redeemData = txIn.getConnectedRedeemData(keyBag); Script scriptPubKey = txIn.getConnectedOutput().getScriptPubKey(); // For P2SH inputs we need to share derivation path of the signing key with other signers, so // that they // use correct key to calculate their signatures. // Married keys all have the same derivation path, so we can safely just take first one here. ECKey pubKey = redeemData.keys.get(0); if (pubKey instanceof DeterministicKey) propTx.keyPaths.put(scriptPubKey, (((DeterministicKey) pubKey).getPath())); ECKey key; // locate private key in redeem data. For pay-to-address and pay-to-key inputs RedeemData will // always contain // only one key (with private bytes). For P2SH inputs RedeemData will contain multiple keys, // one of which MAY // have private bytes if (redeemData == null || (key = redeemData.getFullKey()) == null) { log.warn("No local key found for input {}", i); continue; } Script inputScript = txIn.getScriptSig(); // script here would be either a standard CHECKSIG program for pay-to-address or pay-to-pubkey // inputs or // a CHECKMULTISIG program for P2SH inputs byte[] script = redeemData.redeemScript.getProgram(); try { TransactionSignature signature = tx.calculateSignature(i, key, script, Transaction.SigHash.ALL, false); // at this point we have incomplete inputScript with OP_0 in place of one or more // signatures. We already // have calculated the signature using the local key and now need to insert it in the // correct place // within inputScript. For pay-to-address and pay-to-key script there is only one signature // and it always // goes first in an inputScript (sigIndex = 0). In P2SH input scripts we need to figure out // our relative // position relative to other signers. Since we don't have that information at this point, // and since // we always run first, we have to depend on the other signers rearranging the signatures as // needed. // Therefore, always place as first signature. int sigIndex = 0; inputScript = scriptPubKey.getScriptSigWithSignature( inputScript, signature.encodeToPeercoin(), sigIndex); txIn.setScriptSig(inputScript); } catch (ECKey.KeyIsEncryptedException e) { throw e; } catch (ECKey.MissingPrivateKeyException e) { log.warn("No private key in keypair for input {}", i); } } return true; }
/** * Creates a scriptSig that can redeem a pay-to-pubkey output. If given signature is null, * incomplete scriptSig will be created with OP_0 instead of signature */ public static Script createInputScript(@Nullable TransactionSignature signature) { byte[] sigBytes = signature != null ? signature.encodeToPeercoin() : new byte[] {}; return new ScriptBuilder().data(sigBytes).build(); }