P2SH
Pay-to-script-hash locks an Output to the hash of a redeem script. The spender reveals the script and then satisfies it. The standard script templates page in the Developer Guide describes the mechanism.
Creating the Locking Script
Section titled “Creating the Locking Script”// The redeem script. Here it is an ordinary P2PKH script for Alice.Script redeemScript = new P2PKHLockBuilder(aliceAddress).getLockingScript();
// The Output commits to HASH160(redeemScript), as OP_HASH160 <hash> OP_EQUAL.P2SHLockBuilder locker = new P2SHLockBuilder(redeemScript);Transaction fundingTx = Fixtures.fundingTransaction(locker, 100_000);TransactionOutput utxo = fundingTx.getOutputs().get(0);
new P2SHLockBuilder(ByteBuffer.wrap(hash))takes a hash we already have.
Spending under the P2SH rules
Section titled “Spending under the P2SH rules”Two things differ from a direct spend:
- the Signature covers the redeem script, not the P2SH Output’s script, so we sign against an Output made from the redeem script,
- the Unlocking Script ends with the serialised redeem script.
P2SHUnlockBuilder takes the finished Unlocking Script, so the Signature has to exist first. We build with a
placeholder unlocker, sign, and then replace the Input with one that holds the Unlocking Script.
Transaction tx = new TransactionBuilder() .spendFromTransaction(fundingTx, 0, TransactionInput.MAX_SEQ_NUMBER, new DefaultUnlockBuilder()) .spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(50_000)) .sendChangeTo(aliceAddress) .build(true);
// We sign over the redeem script, not over the P2SH Locking Script.TransactionSigner signer = new TransactionSigner(SIGHASH, alice);signer.sign(tx, new TransactionOutput(utxo.getAmount(), redeemScript), 0);
// The Unlocking Script is <sig> <pubkey> <redeemScript>.Script unlockingScript = new ScriptBuilder() .data(signer.getSignature().toTxFormat()) .data(alice.getPublicKey().getPubKeyBytes()) .data(redeemScript.getProgram()) .build();TransactionInput input = tx.getInputs().get(0);tx.replaceInput(0, new TransactionInput(input.getPrevTxnId(), input.getPrevTxnOutputIndex(), input.getSequenceNumber(), new P2SHUnlockBuilder(unlockingScript)));Verifying the spend
Section titled “Verifying the spend”// P2SH evaluation only happens under the pre-Genesis rules, i.e. the P2SH flag without UTXO_AFTER_GENESIS.Set<Script.VerifyFlag> legacyFlags = EnumSet.of( Script.VerifyFlag.P2SH, Script.VerifyFlag.SIGHASH_FORKID, Script.VerifyFlag.STRICTENC);assertDoesNotThrow(() -> new Interpreter().correctlySpends( tx.getInputs().get(0).getScriptSig(), utxo.getScript(), tx, 0, legacyFlags, Coin.valueOf(100_000)));What happens after Genesis
Section titled “What happens after Genesis”With the post-Genesis flags, the interpreter only checks that the pushed data hashes to the right value.
// Under the post-Genesis rules the Output is a plain hash puzzle.// Pushing the redeem script alone, with no Signature, satisfies it.Script redeemScriptOnly = new ScriptBuilder().data(redeemScript.getProgram()).build();assertDoesNotThrow(() -> new Interpreter().correctlySpends( redeemScriptOnly, utxo.getScript(), tx, 0, Fixtures.FLAGS, Coin.valueOf(100_000)));
