P2SH (legacy)
P2SH (Pay-to-Script-Hash) locks coins to the hash of a script, the redeem script, which the spender reveals along with whatever that script needs. Bitcoin SV removed P2SH in the Genesis upgrade (February 2020). Nodes no longer accept new P2SH Outputs, and Outputs created since then are treated as plain hash checks. The short of it, is that you only need P2SH to spend Outputs created before Genesis.
The examples use the keys and the fundingTransaction helper described in About the examples.
Locking
Section titled “Locking”P2SHLockBuilder.fromScript hashes a redeem script, while P2SHLockBuilder(hashHex) takes the hash directly.
final redeemScript = ScriptBuilder() .addData(Uint8List.fromList(HEX.decode(aliceKey.publicKey.toHex()))) .opCode(OpCodes.OP_CHECKSIG) .build();
final locker = P2SHLockBuilder.fromScript(redeemScript); // hashes the redeem scriptlocker.getScriptPubkey().toString(type: 'asm'); // OP_HASH160 <20-byte script hash> OP_EQUAL
final address = Address.fromScript(redeemScript, NetworkType.TEST); // starts with 2Spending
Section titled “Spending”The Unlocking Script is the redeem script’s own unlocking data, followed by the serialised redeem script. For
a redeem script of <pubkey> OP_CHECKSIG that gives us <signature> <redeem script>:
/// Unlocking Script for a P2SH Output whose redeem script is `<pubkey> OP_CHECKSIG`:/// the Signature, followed by the serialised redeem script.class P2SHPubkeyUnlocker extends UnlockingScriptBuilder { final SVScript redeemScript; P2SHPubkeyUnlocker(this.redeemScript);
@override SVScript getScriptSig() { if (signatures.isEmpty) return SVScript(); return ScriptBuilder() .addData(Uint8List.fromList(HEX.decode(signatures[0].toTxFormat()))) .addData(Uint8List.fromList(redeemScript.buffer)) .build(); }
@override void parse(SVScript script) {}}The Signature commits to the redeem script, so the outpoint we give the signer carries the redeem script, and not
the P2SH Locking Script. We verify with VerifyFlag.P2SH and without UTXO_AFTER_GENESIS, since those are the
rules a pre-Genesis Output is spent under.
// The Signature commits to the redeem script, not to the P2SH Locking Script,// so the outpoint we hand to the signer carries the redeem script.final outpoint = TransactionOutpoint(fundingTx.id, 0, BigInt.from(50000), redeemScript);
final tx = TransactionBuilder() .spendFromOutpointWithSigner(DefaultTransactionSigner(sighashAll, aliceKey), outpoint, TransactionInput.MAX_SEQ_NUMBER, P2SHPubkeyUnlocker(redeemScript)) .spendToPKH(aliceKey.toAddress(networkType: NetworkType.TEST), BigInt.from(49000)) .build(false);
// Let's verify with the pre-Genesis P2SH rule switched on.Interpreter().correctlySpends(tx.inputs[0].script!, p2shScript, tx, 0, {VerifyFlag.P2SH, VerifyFlag.SIGHASH_FORKID}, Coin.ofSat(BigInt.from(50000)));
