Algebra Puzzle - Native Segwit P2WSH
To follow along this tutorial
|
To learn more about P2WSH:
Let’s create a simple maths puzzle with a native Segwit P2WSH transaction.
Creating and Funding the P2WSH
const bitcoin = require('bitcoinjs-lib')
const { alice } = require('./wallets.json')
const network = bitcoin.networks.regtest
const witnessScript = bitcoin.script.compile([
bitcoin.opcodes.OP_ADD,
bitcoin.opcodes.OP_5,
bitcoin.opcodes.OP_EQUAL
])
console.log('Witness script:')
console.log(witnessScript.toString('hex'))
// '935587'
In a P2WSH context, a redeem script is called a witness script. |
decodescript 935587
const p2wsh = bitcoin.payments.p2wsh({redeem: {output: witnessScript, network}, network})
console.log('P2WSH Address:')
console.log(p2wsh.address) (1)
1 | The p2wsh method will generate an object that contains the P2WSH address. |
sendtoaddress bcrt1qpt7c23c0wep9e8up4ywn070w3tqz3828ngy34aj8slsfxrh08ddq2d2pyu 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 | .
The output of our funding transaction should have a locking script composed as follow <00 version byte>
+ <32-byte hash witness program>
. The SHA256 of the witness script must match the 32-byte witness program.
bitcoin.crypto.sha256(Buffer.from('935587', 'hex')).toString('hex')
// '0afd85470f76425c9f81a91d37f9ee8ac0289d479a091af64787e0930eef3b5a'
Preparing the spending transaction
Now let’s prepare the spending transaction by setting input and output.
Alice_1 wants to send the funds to her P2WPKH address.
const keyPairAlice1 = bitcoin.ECPair.fromWIF(alice[1].wif, network)
const p2wpkhAlice1 = bitcoin.payments.p2wpkh({pubkey: keyPairAlice1.publicKey, network})
console.log('P2WPKH address')
console.log(p2wpkhAlice1.address)
const txb = new bitcoin.TransactionBuilder(network)
txb.addInput('TX_ID', TX_VOUT)
txb.addOutput(p2wpkhAlice1.address, 999e5)
const tx = txb.buildIncomplete()
Adding the witness data
Now we can update the transaction with the witness data, providing a solution to the maths problem plus the problem itself, effectively allowing us to spend from the P2WSH.
We provide 02
and 03
as an answer, plus the witness script.
const witness = [Buffer.from('02','hex'), Buffer.from('03','hex'), p2wsh.redeem.output]
tx.setWitness(0, witness)
Note that we are pushing the integer values, not the corresponding opcode values. |
We don’t need to sign this transaction since the witness script doesn’t ask for a signature.
No build
step here as we have already called buildIncomplete
console.log('Transaction hexadecimal:')
console.log(tx.toHex())
decoderawtransaction TX_HEX
Broadcasting the transaction
sendrawtransaction TX_HEX
getrawtransaction TX_ID true
Observations
In the vin section, we note that the scriptSig
field is empty, and that our solution data and witness script are located in the witness txinwitness
field.
The SHA256 hash of the witness script, last item in txinwitness
, is compared against the 32-byte hash located in the P2WSH UTXO we are spending.
The script is then executed with the remaining data from the witness txinwitness
field.