Algebra Puzzle - Legacy P2SH
To follow along this tutorial
|
Let’s create a simple math puzzle with a legacy P2SH transaction.
Creating and Funding the P2SH
const bitcoin = require('bitcoinjs-lib')
const { alice } = require('./wallets.json')
const network = bitcoin.networks.regtest
const redeemScript = bitcoin.script.compile([
bitcoin.opcodes.OP_ADD,
bitcoin.opcodes.OP_5,
bitcoin.opcodes.OP_EQUAL])
console.log('Redeem script:')
console.log(redeemScript.toString('hex'))
decodescript 935587
The p2sh
method will generate an object that contains the P2SH address.
const p2sh = bitcoin.payments.p2sh({redeem: {output: redeemScript, network}, network})
console.log('P2SH address:')
console.log(p2sh.address)
sendtoaddress 2N7WfHK1ftrTdhWej8rnFNR7guhvhfGWwFR 1
We can note that anyone can create this script and generate the corresponding address, it will always result in the same address. |
gettransaction TX_ID
Find the output index (or vout) under | .
Preparing the spending transaction
Let’s now prepare the spending transaction by setting input and output.
Alice_1 wants to send the funds to her P2WPKH address.
const psbt = new bitcoin.Psbt({network})
.addInput({
hash: 'TX_ID',
index: TX_VOUT,
nonWitnessUtxo: Buffer.from('TX_HEX', 'hex'),
redeemScript: Buffer.from(redeemScript, 'hex')
}) (1)
.addOutput({
address: alice[1].p2wpkh,
value: 999e5,
}) (2)
1 | Input referencing the outpoint of our P2SH funding transaction, the previous tx, and the redeem script |
2 | Output, leaving 100 000 satoshis as mining fees |
Creating the unlocking script
Now we can finalize the transaction with the unlocking script, providing a solution to the math problem.
We provide 02
and 03
as an answer satisfying the redeem script.
const getFinalScripts = (inputIndex, input, script) => {
// Step 1: Check to make sure the meaningful locking script matches what you expect.
const decompiled = bitcoin.script.decompile(script)
if (!decompiled || decompiled[0] !== bitcoin.opcodes.OP_ADD) {
throw new Error(`Can not finalize input #${inputIndex}`)
}
// Step 2: Create final scripts
const payment = bitcoin.payments.p2sh({
redeem: {
output: script,
input: bitcoin.script.compile([bitcoin.opcodes.OP_2, bitcoin.opcodes.OP_3]), (1)
},
})
return {
finalScriptSig: payment.input
}
}
psbt.finalizeInput(0, getFinalScripts)
1 | The condition that unlocks the redeem script |
We don’t need to sign this transaction since the redeem script doesn’t ask for a signature.
console.log('Transaction hexadecimal:')
console.log(psbt.extractTransaction().toHex())
decoderawtransaction TX_HEX
Broadcasting the transaction
sendrawtransaction TX_HEX
getrawtransaction TX_ID true
Observations
We can decrypt the unlocking script in Bitcoin Core CLI with decodescript
. You will notice that it is the concatenation of the corresponding hex value of the specified opcodes, OP_2
, OP_3
and the redeem script OP_ADD OP_5 OP_EQUAL
.
decodescript 5253935587
Be aware that the hex script is serialized and includes a <03> before the redeem script. In order to decode the script properly we need to remove this pushbyte. |