Reference

Variables

Variables being with the @ symbol and follow expected ruby variable semantics.

Generating Keys

The simple way to create a new key is to call key :new and assign it to a variable.

@alice = key :new

To generate a key using WIF, use the wif: keyword.

@alice = key wif: 'L1Rg5xdVZ9pZqn7s2UreiDxaFP81RHYL2DjFBUP6BfT9QRw8jERR'

Generate Coinbases and Spending Them

The DSL provides a convinient way to mine coins controlled by a key and then load the coinbase to spend in future transactions.

@alice = key wif: 'L1Rg5xdVZ9pZqn7s2UreiDxaFP81RHYL2DjFBUP6BfT9QRw8jERR'

# Generate a block with coinbase to Alice's P2WPKH address
extend_chain to: @alice

# Find a spendable coinbase controlled by Alice
@alice_coinbase_tx = spendable_coinbase_for @alice

# Build a transaction spending @tx
...

Transactions

To build a transaction use a transaction keyword with two parameters, inputs and outputs.

Inputs

Inputs parameter to transaction takes an array of dictionary objects. Each dictionary must have the following arguments:

  1. tx: This is a transaction being spent. It is object obtained by building a transaction or querying the node for a transaction.

  2. vout: This is the output index being spent of the input.

  3. script_sig: This is the witness script required to spend the UTXO identified by the tx and vout. This script_sig can be defined using high level constructs like p2wpkh, sig, multisig or simple Bitcoin Script opcodes. This parameter is described in detail later.

  4. sighash (optional, default ALL): The sighash to use when generating signatures for the input.

Outputs

The outputs parameter to transaction takes an array of dictionary objects. Each dictionary object must have the following arguments:

  1. amount: The amount being spent.

  2. policy or descriptor: The locking script can be specified in two ways - miniscript or bitcoin descriptors.

A simple transaction that spends the @alice_coinbase_tx fetched above is below. We sign the transaction declaratively by saying script_sig: 'p2wpkh:alice' and generate an output pubkey by saying descriptor: 'wpkh(@bob)'.

@alice_to_bob = transaction inputs: [
                              { tx: @alice_coinbase_tx, vout: 0, script_sig: 'p2wpkh:alice' }
                            ],
                            outputs: [
                              { descriptor: 'wpkh(@bob)', amount: 49.99.sats }
                            ]

Broadcast and Confirm Transactions

A transaction can be easily broadcast by using the broadcast command. Even though we can use low level JSON-API calls like sendrawtransaction, the DSL provides high level commands to make it easy to deal with broadcasting and confirming transactions.

@tx = transaction inputs: [...] outtputs: [...]

broadcast @tx

To broadcast multiple transactions list them in the order you want mempool to accept them. This order is important for certain contracts.

@tx_a = transaction inputs: [...] outtputs: [...]
@tx_b = transaction inputs: [...] outtputs: [...]

broadcast @tx_b, @tx_a

We can confirm transactions by the confirm command that mines one new block. We can optionally provide a key to which the coinbase of the new block should go to.

@alice = key :new
@tx = transaction inputs: [...] outtputs: [...]

broadcast @tx
confirm @tx, to: @alice

Contract Branch Execution

The DSL supports running various branches of contracts. The commands state_transition and run_transitions allow you to define state transitions and then execute them in a select order.

state_transition :create_coinbase do
 ... # Define any actions here
end

state_transition :alice_cant_spend do
 ... # Define any actions here
 ... # Optionally define any assertions here
end

# Run the transistions you want to run in any order
run_transitions :create_coinbase, :alice_cant_spent

There is one special state transition called reset that resets the regtest node to a prestine state. You don’t need to define the reset state, the DSL provides it.

# Reset bitcoin node before running transistions
run_transitions :reset, :create_coinbase, :alice_cant_spent

Node Interaction

The DSL supports all JSON-API commands supported by bitcoin. You don’t need to worry about (de)serialization of arguments. Just provide the transaction object or any other parameter directly from the DSL. Here are a few examples:

@address = ...

# Mine blocks to the address
generatetoaddress num_blocks: 100, to: @address

@tx = ...

# Send raw transaction
sendrawtransaction tx: @tx

The complete list of supported commands is the same as [bitcoin’s JSON-API](https://developer.bitcoin.org/reference/rpc/).

The DSL provides abstractions on some of the often used JSON-API commands to make it easy to talk to bitcoin nodes.

Extend Chain

Takes optional public key to and number of blocks num_blocks to mine.

If key is provided, the coinbase of all the blocks is created for P2WPKH of the key.

If key is not provided, extend_chain creates a throw away key and mines blocks using it for the coinbase.

Generates num_blocks number of blocks. The default is 1.

@alice = key :new
@bob = key :new

# Seed alice with some coins
extend_chain to: @alice

# Seed bob with some coins and make coinbase spendable
extend_chain num_blocks: 101, to: @bob

Reorganise Chain

You can also roll back a chain to a height or to a blockhash. You can also rollback the chain up to a point to unconfirm a given transaction - this enables you to broadcast and confirm the transaction again along a different chain fork.

reorg_chain height: 95

reorg_chain blockhash: @blockhash

reorg_chain unconfirm_tx: @reorg_to_tx

Find Spendable Coinbase Transactions

To look up a coinbase UTXO controlled by a key use spendable_coinbase_for and provide a key.

The command returns the oldest spendable transaction controlled by the key as a P2WPKH transaction.

@tx = spendable_coinbase_for @alice

Get Chain Height

get_height

Get Block at Height

@block = get_block_at_height 100

Get Coinbase at Height

@tx = get_coinbase_at_height 100

Get TxId for Transaction in Block

@txid = get_txid block: @block, tx_index: 5

Assertions

The DSL provides a number of assertions for verifying the state of transactions and chain.

Verify Signatures for a Transaction

verify_signature for_transaction: @alice_tx,
                 at_index: 0,
                 with_prevout: [coinbase_tx, 0]

Assert Mempool Will Accept a Transaction

assert_mempool_accept @alice_tx

Assert Mempool Will Not Accept a Transaction

assert_not_mempool_accept @alice_tx

Assert a Transaction is Confirmed

assert_confirmed transaction: @alice_tx

# Or provide a txid
assert_confirmed txid: @alice_tx.txid

Assert an Output is (not) Spent

assert_output_is_spent(transaction: @alice_tx, vout: 0)

assert_output_is_not_spent(transaction: @alice_tx, vout: 0)

assert_confirmed takes an optional at_height parameter to assert if the transaction has been confirmed in the block at that height.

Script Interpolation

The DSL provides a number of commands to interpolate in a Script.

Signature (sig)

'sig:(@alice)'

The sig command generates a signature for the transaction using the sighash keyword specified in the input. If no sighash keyword is specified, the DSL uses ALL by default.

Hashes

The DSL provides the various hashes that Bitcoin uses.

'hash160:xxx'
'sha256:xxx'
'double_sha256:xxx'

Multisig

Since multisig is an often used script, the DSL provides a convinient way to generate signatures and script required for these.

The script below with generate signatures for the keys specified and push them to the witness stack.

'multisig:(@alice,@bob)'

Interpolated Miniscript Policy

Miniscript is interpolated by replacing all references to variables and bitcoin hash functions by their corresponding computed hex values.

For example, the policy 'or(99@thresh(2,pk(@alice),pk(@asp)),and(older(10),pk(@asp_timelock)))' is processed by rust-miniscript after @alice, @asp and $asp_timelock have been replaced by hex formatted public keys.

Bitcoin Descriptors

The DSL supports Bitcoin descriptors to allow for easier definition of output scripts.

The descriptor key in the outputs object can specify a descriptor with a variable for the keys.

@opcodes_tx = transaction inputs: [
                            { tx: @alice_coinbase, vout: 0, script_sig: 'sig:@alice @alice' }
                          ],
                          outputs: [
                            {
                              descriptor: 'wpkh(@bob)', (1)
                              amount: 49.999.sats
                            }
                          ]
1 Use descriptor to define an output script

You can also use descriptor syntax to create signatures for descriptor outputs.

@spend_opcodes_tx = transaction inputs: [
                                  { tx: @opcodes_tx, vout: 0, script_sig: 'sig:wpkh(@bob)' } (1)
                                ],
                                outputs: [
                                  { descriptor: 'wpkh(@bob)', amount: 49.998.sats }
                                ]
1 Use descriptor DSL extension to provide a script sig to satisfy the descriptor.

Taproot Outputs

The Taproot Example provides a complete example on the DSL’s support for creating taproot outputs and spending the key and script paths.

We support building taproot outputs by supporting taproot key in the outputs objects. Each taproot key inturn has keypath and leaves keys.

outputs: [
  {
    taproot: { internal_key: @bob, (1)
	       leaves: ['pk(@carol)', 'pk(@alice)'] }, (2)
    amount: 49.999.sats
  }
1 Specify an internal key
2 Specify the leaf scripts

For spending taproot outputs, the DSL allows specifying the keypath, in which case the the internal key is automatially looked up, tweaked and used to create signatures.

inputs: [
  { tx: @taproot_keypath_tx,
    vout: 0,
    script_sig: { keypath: @bob }, (1)
    sighash: :all }
],
1 Use the tweaked @bob key to spend taproot output.

Alternatively, you can provide the script path leaf index and a script sig to spend along a scriptpath.

inputs: [
  { tx: @taproot_keypath_tx,
    vout: 0,
    script_sig: { leaf_index: 1, sig: 'sig:@alice' }, (1)
    sighash: :all }
],
1 Specify the leaf index and the signature script to spend a taproot output along scriptpath