Reference
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:
-
tx
: This is a transaction being spent. It is object obtained by building a transaction or querying the node for a transaction. -
vout
: This is the output index being spent of the input. -
script_sig
: This is the witness script required to spend the UTXO identified by thetx
andvout
. This script_sig can be defined using high level constructs likep2wpkh
,sig
,multisig
or simple Bitcoin Script opcodes. This parameter is described in detail later. -
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:
-
amount
: The amount being spent. -
policy
ordescriptor
: 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
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]
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 |