Script with CHECKSEQUENCEVERIFY - Native Segwit P2WSH

To follow along this tutorial

  • Clone the Github repository

  • cd code

  • npm install or yarn install

  • Execute the transaction code by typing node tx_filename.js

  • Alternatively you can enter the commands step-by-step by cd into ./code then type node in a terminal to open the Node.js REPL

  • 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 native Segwit P2WSH transaction with a script that contains the OP_CHECKSEQUENCEVERIFY relative timelock opcode. The script is almost the same as Script with CHECKLOCKTIMEVERIFY - Native Segwit P2WSH but with a relative timelock of 5 blocks.


Learn more about P2WSH:

Either alice_1 can spend the P2WSH UTXO but only when 5 blocks have been mined after the funding transaction is first confirmed, or bob_1 and alice_1 can redeem the funds at any time.

function csvCheckSigOutput(aQ, bQ, timelock) {
  return bitcoin.script.compile([



Creating and Funding the P2WSH

Import libraries, test wallets and set the network and sighash type.
const bitcoin = require('bitcoinjs-lib')
const { alice, bob } = require('./wallets.json')
const network = bitcoin.networks.regtest
const hashType = bitcoin.Transaction.SIGHASH_ALL
We also need an additional library to help us with BIP68 relative timelock encoding.
const bip68 = require('bip68')
Alice_1 and bob_1 are the signers.
const keyPairAlice1 = bitcoin.ECPair.fromWIF(alice[1].wif, network)
const keyPairBob1 = bitcoin.ECPair.fromWIF(bob[1].wif, network)

In both scenarios alice_1 P2WPKH address will get back the funds.

const p2wpkhAlice1 = bitcoin.payments.p2wpkh({pubkey: keyPairAlice1.publicKey, network})
console.log('P2WPKH address')
Set the relative timelock to 5 blocks (to be mined on top of the funding transaction confirmation).
const timelock = bip68.encode({blocks: 5}) (1)
1 We encode the sequence value according to BIP68 specification.
Generate the witness script with CSV 5 blocks from now.
const witnessScript = csvCheckSigOutput(keyPairAlice1, keyPairBob1, timelock)
console.log('Witness script:')
In a P2WSH context, a redeem script is called a witness script.

We can decode the script in Bitcoin Core CLI with decodescript.

Generate the P2WSH address.
const p2wsh = bitcoin.payments.p2wsh({redeem: {output: witnessScript, network}, network})
console.log('P2WSH address:')
Send 1 BTC to this P2WSH address.
sendtoaddress bcrt1qjnc0eeslkedv2le9q4t4gak98ygtfx69dlfchlurkyw9rauhuy0qgmazhq 1
Note that our redeem script doesn’t contain any variable data so the P2WSH will always be the same.
Get the output index so that we have the outpoint (txid / vout).
getrawtransaction TX_ID true

The output script of our funding transaction is a versioned witness program. It is composed as follow: <00 version byte> + <32-byte hash witness program>.
The SHA256 hash of the witness script (in the witness of the spending tx) must match the 32-byte witness program (in prevTxOut).




Preparing the spending transaction

Now let’s prepare the spending transaction by setting input and output, as well as the nSequence value for the first scenario.

Create a BitcoinJS transaction builder object.
const txb = new bitcoin.TransactionBuilder(network)
Create the input by referencing the outpoint of our P2WSH funding transaction.
// txb.addInput(prevTx, vout, sequence, prevTxScript)
txb.addInput('TX_ID', TX_VOUT, timelock) (1)
1 We set the sequence field as the timelock value only if we want to run the first scenario.
Alice_1 will redeem the fund to her P2WPKH address, leaving 100 000 satoshis for the mining fees.
txb.addOutput(p2wpkhAlice1.address, 999e5)
Prepare the transaction.
const tx = txb.buildIncomplete()

Adding the witness stack

Now we can update the transaction with the witness stack (txinwitness field), providing a solution to the locking script.

We generate the hash that will be used to produce the signatures.
// hashForWitnessV0(inIndex, prevOutScript, value, hashType)
const signatureHash = tx.hashForWitnessV0(0, witnessScript, 1e8, hashType)
Note that we use a special method hashForWitnessV0 for Segwit transactions.

There are two ways to redeem the funds, either alice_1 after the timelock expiry or alice_1 and bob_1 at any time. We control which branch of the script we want to run by ending our unlocking script with a boolean value.

First branch: {Alice’s signature} OP_TRUE
const witnessStackFirstBranch = bitcoin.payments.p2wsh({
  redeem: {
    input: bitcoin.script.compile([
      bitcoin.script.signature.encode(keyPairAlice1.sign(signatureHash), hashType),
    output: witnessScript

console.log('First branch witness stack:')
console.log( => x.toString('hex')))
Second branch: {Alice’s signature} {Bob’s signature} OP_FALSE
const witnessStackSecondBranch = bitcoin.payments.p2wsh({
  redeem: {
    input: bitcoin.script.compile([
      bitcoin.script.signature.encode(keyPairAlice1.sign(signatureHash), hashType),
      bitcoin.script.signature.encode(keyPairBob1.sign(signatureHash), hashType),
    output: witnessScript

console.log('Second branch witness stack:')
console.log( => x.toString('hex')))
We provide the witness stack.
tx.setWitness(0, witnessStackFirstBranch)
//tx.setWitness(0, witnessStackSecondBranch)

No build step here as we have already called buildIncomplete

Get the raw hex serialization.
console.log('Transaction hexadecimal:')
Inspect the raw transaction with Bitcoin Core CLI, check that everything is correct.
decoderawtransaction TX_HEX

Broadcasting the transaction

If we run the first scenario we need 5 blocks to be mined so that the timelock will expire.

generatetoaddress 5 bcrt1qnqud2pjfpkqrnfzxy4kp5g98r8v886wgvs9e7r
It’s time to broadcast the transaction via Bitcoin Core CLI.
sendrawtransaction TX_HEX
Inspect the transaction.
getrawtransaction TX_ID true


For both scenarios we note that our scriptSig is empty.

For the first scenario, we note that our witness stack contains:

  • Alice_1 signature

  • 01, which is equivalent to OP_TRUE

  • the witness script, that we can decode with decodescript

For the second scenario, we note that our witness stack contains:

  • Alice_1 signature

  • Bob_1 signature

  • an empty string, which is equivalent to OP_FALSE

  • the witness script, that we can decode with decodescript