defmodule BSV.Contract.Helpers do
@moduledoc """
Collection of helpers functions for use in `BSV.Contract` modules.
"""
alias BSV.{Contract, OpCode, PrivKey, Sig, UTXO}
# Iterrates over all opcodes
# Defines a function to push the specified opcode onto the contract script
Enum.each(OpCode.all(), fn {op, _} ->
key = op
|> Atom.to_string()
|> String.downcase()
|> String.to_atom()
@doc "Pushes `#{op}` onto the script."
@spec unquote(key)(Contract.t()) :: Contract.t()
def unquote(key)(%Contract{} = contract) do
Contract.script_push(contract, unquote(op))
end
end)
@doc """
Pushes the given data onto the script.
"""
@spec push(Contract.t(), atom() | binary() | integer()) :: Contract.t()
def push(%Contract{} = contract, data) do
Contract.script_push(contract, data)
end
@doc """
Pushes the given list of data onto the script in seperate pushes.
"""
@spec push_all(Contract.t(), list(atom() | binary() | integer())) :: Contract.t()
def push_all(%Contract{} = contract, []), do: contract
def push_all(%Contract{} = contract, [data | rest]) do
contract
|> push(data)
|> push_all(rest)
end
@doc """
Signs the transaction [`context`](`t:BSV.Contract.ctx/0`) and pushes the
signature onto the stack.
If no context is available in the [`contract`](`t:BSV.Contract.t/0`), then
71 bytes of zeros are pushed onto the stack instead.
"""
@spec sig(Contract.t(), PrivKey.t()) :: Contract.t()
def sig(
%Contract{ctx: {tx, index}, opts: opts, subject: %UTXO{txout: txout}} = contract,
%PrivKey{} = privkey
) do
signature = Sig.sign(tx, index, txout, privkey, opts)
Contract.script_push(contract, signature)
end
def sig(%Contract{ctx: nil} = contract, %PrivKey{} = _privkey),
do: Contract.script_push(contract, <<0::568>>)
@doc """
Iterates over the given list of private keys and for each signs the
transaction [`context`](`t:BSV.Contract.ctx/0`) and pushes each signature onto
the stack.
If no context is available in the [`contract`](`t:BSV.Contract.t/0`), then for
each private key 71 bytes of zeros are pushed onto the stack instead.
"""
@spec multi_sig(Contract.t(), list(PrivKey.t())) :: Contract.t()
def multi_sig(ctx, []), do: ctx
def multi_sig(ctx, [privkey | privkeys]) do
ctx
|> sig(privkey)
|> multi_sig(privkeys)
end
end