Creating Bitcoin custom contracts with Elixir
ArticleI’ve been busy with some new projects recently. Part of that has involved re-writing my Elixir Bitcoin library, BSV-ex. I’ll be releasing BSV-ex 2 soOoOn - it’s been entirely rewritten from the ground up with many improvements.
One thing I’ve spent time on, is thinking about how to define contracts and build transactions in a developer friendly way. I think I’ve come up with something pretty unique and I’m excited to share it.
Defining contracts
BSV-ex 2 ships with a Contract
behaviour module. To implement the behaviour, all we have to do is define a module with locking_script/2
and unlocking_script/2
functions. I’ll use a P2PKH
contract as a simple example.
defmodule P2PKH do
use BSV.Contract
def locking_script(ctx, %{address: address}) do
ctx
|> op_dup
|> op_hash160
|> push(address.pubkey_hash)
|> op_equalverify
|> op_checksig
end
def unlocking_script(ctx, %{keypair: keypair}) do
ctx
|> sig(keypair.privkey)
|> push(BSV.PubKey.to_binary(keypair.pubkey))
end
end
That’s pretty easy to follow along and reason about, right? This is just pure Elixir so we can easily add our own helper functions that act as macros for defining more complex but commonly repeated script templates. For example, it would be pretty trivial to define an op_push_tx()
function we could call in our unlocking scripts.
BSV-ex 2 also comes with a full-featured Bitcoin Script VM, and the Contract
module allows us to test and simulate our contracts.
keypair = BSV.KeyPair.new()
lock_params = %{address: BSV.Address.from_pubkey(keypair.pubkey)}
unlock_params = %{keypair: keypair}
with {:ok, vm} <- BSV.Contract.simulate(P2PKH, lock_params, unlock_params) do
BSV.VM.valid?(vm)
end
# -> true
Building transactions
Anyone familiar with my JavaScript library TxForge will recognise the influence here. This is how we build transactions in BSV-ex 2.
utxo = BSV.UTXO.from_params(utxo_params)
builder = %BSV.TxBuilder{
inputs: [
P2PKH.unlock(utxo, %{keypair: keypair})
],
outputs: [
P2PKH.lock(10000, %{address: address}),
OpReturn.lock(0, %{data: ["hello", "world"]})
]
}
tx = BSV.TxBuilder.to_tx(builder)
rawtx = BSV.Tx.to_binary(tx, encoding: :hex)
I hope you like the way this is heading. Elixir remains somewhat under-recognised as a language for Bitcoin development, but I can assure you the tools are there to consider it a top-tier language for Bitcoin and Bitcoin-app development. I hope I can convince a few more of you to try it out.
BSV-ex 2 will be released in the next 7-10 days.