lib/script/opcodes/bitwise_logic/equal_verify.ex

defmodule BitcoinLib.Script.Opcodes.BitwiseLogic.EqualVerify do
  @moduledoc """
  Based on https://en.bitcoin.it/wiki/Script#Bitwise_logic

  Word OP_EQUALVERIFY
  Opcode 136
  Hex 0x88
  Input x1 x2
  Output Nothing / fail
  Description Same as OP_EQUAL, but runs OP_VERIFY afterward.
  """

  @behaviour BitcoinLib.Script.Opcode

  alias BitcoinLib.Script.Opcodes.BitwiseLogic.EqualVerify

  defstruct []

  alias BitcoinLib.Script.Opcodes.BitwiseLogic.Equal
  alias BitcoinLib.Script.Opcodes.FlowControl.Verify

  @value 0x88

  @doc """
  Returns 0x88

  ## Examples
      iex> BitcoinLib.Script.Opcodes.BitwiseLogic.EqualVerify.v()
      0x88
  """
  @spec v() :: 0x88
  def v do
    @value
  end

  @doc """
  Returns <<0x88>>

  ## Examples
      iex> BitcoinLib.Script.Opcodes.BitwiseLogic.EqualVerify.encode()
      <<0x88>>
  """
  @spec encode() :: <<_::8>>
  def encode() do
    <<@value::8>>
  end

  @doc """
  Returns 1 if the inputs are exactly equal, 0 otherwise, and runs OP_VERIFY afterward.

  ## Examples
      iex> BitcoinLib.Script.Opcodes.BitwiseLogic.EqualVerify.execute(0x88, [3, 3, 4])
      {:ok, [4]}
  """
  @spec execute(%EqualVerify{}, list()) :: {:ok, list()} | {:error, binary()}
  def execute(_opcode, []), do: {:error, "trying to execute OP_EQUAL_VERIFY on an empty stack"}

  def execute(_opcode, [element | []]),
    do: {:error, "trying to execute OP_EQUAL_VERIFY on a stack with a single element #{element}"}

  def execute(_opcode, [_first_element | [_second_element | _]] = stack) do
    case Equal.execute(%Equal{}, stack) do
      {:ok, stack} -> Verify.execute(%Verify{}, stack)
      {:error, message} -> {:error, message}
    end
  end
end