defmodule BitcoinLib.Script do @moduledoc """ Script manipulation module """ alias BitcoinLib.Script.{Analyzer, Encoder, Parser, Runner} alias BitcoinLib.Signing.Psbt.CompactInteger @doc """ Transforms a script in the bitstring form into a list of opcodes ## Examples iex> <<0x76a914fde0a08625e327ba400644ad62d5c571d2eec3de88ac::200>> ...> |> BitcoinLib.Script.parse() { :ok, [ %BitcoinLib.Script.Opcodes.Stack.Dup{}, %BitcoinLib.Script.Opcodes.Crypto.Hash160{}, %BitcoinLib.Script.Opcodes.Data{ value: <<0xfde0a08625e327ba400644ad62d5c571d2eec3de::160>> }, %BitcoinLib.Script.Opcodes.BitwiseLogic.EqualVerify{}, %BitcoinLib.Script.Opcodes.Crypto.CheckSig{ script: <<0x76a914fde0a08625e327ba400644ad62d5c571d2eec3de88ac::200>> } ] } """ @spec parse(bitstring()) :: {:ok, list()} | {:error, binary()} def parse(script) when is_bitstring(script) do script |> Parser.parse() end @doc """ Transforms a script in the bitstring form into a list of opcodes ## Examples iex> <<0x76a914fde0a08625e327ba400644ad62d5c571d2eec3de88ac::200>> ...> |> BitcoinLib.Script.parse!() [ %BitcoinLib.Script.Opcodes.Stack.Dup{}, %BitcoinLib.Script.Opcodes.Crypto.Hash160{}, %BitcoinLib.Script.Opcodes.Data{ value: <<0xfde0a08625e327ba400644ad62d5c571d2eec3de::160>> }, %BitcoinLib.Script.Opcodes.BitwiseLogic.EqualVerify{}, %BitcoinLib.Script.Opcodes.Crypto.CheckSig{ script: <<0x76a914fde0a08625e327ba400644ad62d5c571d2eec3de88ac::200>> } ] """ @spec parse!(bitstring()) :: list() def parse!(script) when is_bitstring(script) do parse(script) |> elem(1) end @doc """ Transforms a list of opcodes into a compact integer reprensenting the size of the script, and the script itself ## Examples iex> [ ...> %BitcoinLib.Script.Opcodes.Stack.Dup{}, ...> %BitcoinLib.Script.Opcodes.Crypto.Hash160{}, ...> %BitcoinLib.Script.Opcodes.Data{ ...> value: <<0xfde0a08625e327ba400644ad62d5c571d2eec3de::160>> ...> }, ...> %BitcoinLib.Script.Opcodes.BitwiseLogic.EqualVerify{}, ...> %BitcoinLib.Script.Opcodes.Crypto.CheckSig{} ...> ] |> BitcoinLib.Script.encode {<<0x19>>, <<0x76a914fde0a08625e327ba400644ad62d5c571d2eec3de88ac::200>>} """ @spec encode(list()) :: {bitstring(), bitstring()} def encode(script) when is_list(script) do encoded_script = script |> Encoder.to_bitstring() encoded_script_size = byte_size(encoded_script) |> CompactInteger.encode() {encoded_script_size, encoded_script} end @spec execute(bitstring(), list()) :: {:ok, boolean()} | {:error, binary()} def execute(script, stack) when is_bitstring(script) do case Parser.parse(script) do {:ok, parsed_script} -> execute(parsed_script, stack) {:error, message} -> {:error, message} end end @spec execute(list(), list()) :: {:ok, boolean()} | {:error, binary()} def execute(script, stack) when is_list(script) do script |> Runner.execute(stack) end @spec identify(bitstring() | list()) :: {:unknown} | {:p2pk | :p2pkh | :p2sh | :p2wsh | :p2wpkh, bitstring()} def identify(script) do script |> Analyzer.identify() end end