BSV.Contract behaviour (BSV v2.1.0) View Source
A behaviour module for implementing Bitcoin transaction contracts.
A Bitcoin transaction contains two sides: inputs and outputs.
Transaction outputs are script puzzles, called "locking scripts" (sometimes also known as a "ScriptPubKey") which lock a number of satoshis. Transaction inputs are contain an "unlocking script" (or the "ScriptSig") and unlock the satoshis contained in the previous transaction's outputs.
Therefore, each locking script is unlocked by a corresponding unlocking script.
The BSV.Contract
module provides a way to define a locking script and
unlocking script in a plain Elixir function. Because it is just Elixir, it
is trivial to add helper functions and macros to reduce boilerplate and create
more complex contract types and scripts.
Defining a contract
The following module implements a Pay to Public Key Hash contract.
Implementing a contract is just a case of defining locking_script/2
and
unlocking_script/2
.
defmodule P2PKH do
@moduledoc "Pay to Public Key Hash contract."
use BSV.Contract
@impl true
def locking_script(ctx, %{address: address}) do
ctx
|> op_dup
|> op_hash160
|> push(address.pubkey_hash)
|> op_equalverify
|> op_checksig
end
@impl true
def unlocking_script(ctx, %{keypair: keypair}) do
ctx
|> signature(keypair.privkey)
|> push(BSV.PubKey.to_binary(keypair.pubkey))
end
end
Locking a contract
A contract locking script is initiated by calling lock/2
on the contract
module, passing the number of satoshis and a map of parameters expected by
locking_script/2
defined in the contract.
# Initiate the contract locking script
contract = P2PKH.lock(10_000, %{address: Address.from_pubkey(bob_pubkey)})
script = Contract.to_script(contract) # returns the locking script
txout = Contract.to_txout(contract) # returns the full txout
Unlocking a contract
To unlock and spend the contract, a BSV.UTXO.t/0
is passed to unlock/2
with the parameters expected by unlocking_script/2
defined in the contract.
# Initiate the contract unlocking script
contract = P2PKH.unlock(utxo, %{keypair: keypair})
Optionally the current transaction context
can be
given to the contract
. This allows the correct
sighash
to be calculated for any signatures.
# Pass the current transaction ctx
contract = Contract.put_ctx(contract, {tx, vin})
# returns the signed txin
txin = Contract.to_txin(contract)
Building transactions
The BSV.Contract
behaviour is taken advantage of in the BSV.TxBuilder
module, resulting in transaction building semantics that are easy to grasp and
pleasing to work with.
builder = %TxBuilder{
inputs: [
P2PKH.unlock(utxo, %{keypair: keypair})
],
outputs: [
P2PKH.lock(10_000, %{address: address})
]
}
# Returns a fully signed transaction
TxBuilder.to_tx(builder)
For more information, refer to BSV.TxBuilder
.
Link to this section Summary
Callbacks
Callback executed to generate the contract locking script.
Callback executed to generate the contract unlocking script.
Functions
Puts the given transaction context
(tx and vin)
onto the contract.
Appends the given value onto the end of the contract script.
Returns the size (in bytes) of the contract script.
Simulates the contract with the given locking and unlocking parameters.
Compiles the contract and returns the script.
Compiles the unlocking contract and returns the BSV.TxIn.t/0
.
Compiles the locking contract and returns the BSV.TxIn.t/0
.
Link to this section Types
Specs
ctx() :: {BSV.Tx.t(), non_neg_integer()}
Transaction context.
A tuple containing a BSV.Tx.t/0
and vin
. When
attached to a contract, the he correct sighash
to
be calculated for any signatures.
Specs
t() :: %BSV.Contract{ ctx: ctx() | nil, mfa: {module(), atom(), list()}, opts: keyword(), script: BSV.Script.t(), subject: non_neg_integer() | BSV.UTXO.t() }
BSV Contract struct
Link to this section Callbacks
Link to this section Functions
Specs
Puts the given transaction context
(tx and vin)
onto the contract.
When the transaction context is attached, the contract can generate valid signatures. If it is not attached, all signatures will be 71 bytes of zeros.
Specs
Appends the given value onto the end of the contract script.
Specs
script_size(t()) :: non_neg_integer()
Returns the size (in bytes) of the contract script.
Specs
Simulates the contract with the given locking and unlocking parameters.
Internally this works by creating a fake transaction containing the locking
script, and then attempts to spend that UTXO in a second fake transaction.
The entire script is concatenated and passed to VM.eval/2
.
Example
iex> alias BSV.Contract.P2PKH
iex> keypair = BSV.KeyPair.new()
iex> lock_params = %{address: BSV.Address.from_pubkey(keypair.pubkey)}
iex> unlock_params = %{keypair: keypair}
iex>
iex> {:ok, vm} = Contract.simulate(P2PKH, lock_params, unlock_params)
iex> BSV.VM.valid?(vm)
true
Specs
to_script(t()) :: BSV.Script.t()
Compiles the contract and returns the script.
Specs
to_txin(t()) :: BSV.TxIn.t()
Compiles the unlocking contract and returns the BSV.TxIn.t/0
.
Specs
to_txout(t()) :: BSV.TxOut.t()
Compiles the locking contract and returns the BSV.TxIn.t/0
.