Skip to content

Latest commit

 

History

History
168 lines (126 loc) · 5.43 KB

07_3_P2SH_P2WSH_Algebra_Puzzle.md

File metadata and controls

168 lines (126 loc) · 5.43 KB

7.3: Algebra Puzzle - Embedded Segwit P2SH-P2WSH

To follow along this tutorial and enter the commands step-by-step

  • Type node in a terminal after cd into ./code for a Javascript prompt
  • Open the Bitcoin Core GUI console or use bitcoin-cli for the Bitcoin Core commands
  • Use bx aka Libbitcoin-explorer as a handy complement

Let's create a simple maths puzzle with an embedded Segwit P2SH-P2WSH transaction.

Creating and Funding the P2SH-P2WSH

Import libraries, test wallets and set the network

const bitcoin = require('bitcoinjs-lib')
const { alice } = require('./wallets.json')
const network = bitcoin.networks.regtest

Create the redeem script and generate its address.

const redeemScript = bitcoin.script.compile([
  bitcoin.opcodes.OP_ADD,
  bitcoin.opcodes.OP_5,
  bitcoin.opcodes.OP_EQUAL])
  
console.log('redeemScript  ', redeemScript.toString('hex'))  

You can decode the script in Bitcoin Core CLI.

$ decodescript 935587

Put the p2wsh object into the p2sh redeem parameter.

const p2wsh = bitcoin.payments.p2wsh({redeem: {output: redeemScript, network}, network})
const p2sh = bitcoin.payments.p2sh({redeem: p2wsh, network: network})
console.log('p2sh.address:  ', p2sh.address)

Send 1 BTC to this P2SH-P2WSH address, which is the reward for whoever as the solution to the locking script.

$ sendtoaddress 2MwnRrQxKhCdr8e3vbL7ymhtzQFYPTx9xww 1

We can note that anyone can create this script and generate the corresponding address, it will always result in the same address.

Generate one block so that we can spend the UTXO.

$ generate 1

Get the output index so that we have the outpoint (txid / vout).

Find the output index (or vout) under details > vout.

$ gettransaction "txid"

Preparing the spending transaction

Now let's prepare the spending transaction by setting input and output.

Alice_0 wants to send the funds to her P2WPKH address.

const keyPairAlice0 = bitcoin.ECPair.fromWIF(alice[0].wif, network)
const p2wpkhAlice0 = bitcoin.payments.p2wpkh({pubkey: keyPairAlice0.publicKey, network})

Create a BitcoinJS transaction builder object.

const txb = new bitcoin.TransactionBuilder(network)

Create the input by referencing the outpoint of our P2SH funding transaction.

txb.addInput('TX_ID', TX_VOUT)

Create the output, leaving 100 000 satoshis as mining fees.

txb.addOutput(p2wpkhAlice0.address, 999e5)

Prepare the transaction.

const tx = txb.buildIncomplete()

Creating the witness script and witness stack

Now we can update the transaction with the witness script that will be placed in the scriptSig field, and the witness stack, composed of the solution to our maths problem and the maths problem itself (redeem script).

When we are spending from a P2WSH UTXO the redeem script hash is produced automatically. However, when we are spending from a P2SH UTXO (our P2SH-P2WSH is a regular P2SH UTXO), we need to place the redeem script hash ourselves in the scriptSig, preceded by a 0 version byte so that the interpreter recognizes that it actually is a witness program. If the version byte is 0 and the witness program is 32 bytes it is interpreted as a P2WSH program.

Create the input script.

const scriptSig = bitcoin.script.compile([p2wsh.output])
tx.setInputScript(0, scriptSig)

The compile method generates the serialized hex version of the witness script. The HASH160 of the asm version (without pushbytes(22)) of this witness script will be compared against the hash in the locking script of the P2SH UTXO we are spending.

bitcoin.crypto.hash160(scriptSig.slice(1)).toString('hex')
// '31c74d4132ecfdb577695cd23be18346f048cb24'

We create the witness stack, providing 02 and 03 as an answer, plus the redeem script.

Note that we are pushing the integer values, not the corresponding opcode values.

const witnessStack = [Buffer.from('02','hex'), Buffer.from('03','hex'), p2wsh.redeem.output]
tx.setWitness(0, witnessStack)

We don't need to sign this transaction since the redeem script doesn't ask for a signature.

Get the raw hex serialization.

No build step here as we have already called buildIncomplete

console.log('tx.toHex()  ', tx.toHex())

Inspect the raw transaction with Bitcoin Core CLI, check that everything is correct.

$ decoderawtransaction "hexstring"

Broadcasting the transaction

It's time to broadcast the transaction via Bitcoin Core CLI.

$ sendrawtransaction "hexstring"

Inspect the transaction.

$ getrawtransaction "txid" true

Observations

In the vin section, we note that the scriptSig contains the witness script, composed of a 0 version byte and a witness program, which is the SHA256 32-bytes hash of the redeem script.

ScriptSig (asm version) is hashed with HASH160 and compared against the 20-byte-hash in the locking script of the UTXO we are spending.

bitcoin.crypto.hash160(Buffer.from('00200afd85470f76425c9f81a91d37f9ee8ac0289d479a091af64787e0930eef3b5a', 'hex')).toString('hex')
// '31c74d4132ecfdb577695cd23be18346f048cb24'

ScriptSig is then interpreted as a P2WSH and triggers the witnessScript which is executed.

What's Next?

Continue "PART THREE: PAY TO SCRIPT HASH" with 7.4: Computational Puzzle: SHA-1 Collision - Legacy P2SH.