lib/script/opcodes/stack/if_dup.ex

defmodule BitcoinLib.Script.Opcodes.Stack.IfDup do
  @moduledoc """
  Based on https://en.bitcoin.it/wiki/Script

  Word OP_IFDUP
  Opcode 115
  Hex 0x73
  Input x
  Output x / x x
  If the top stack value is not 0, duplicate it.
  """

  @behaviour BitcoinLib.Script.Opcode

  defstruct []

  alias BitcoinLib.Script.Opcodes.Stack.IfDup

  @type t :: IfDup

  @value 0x73

  @doc """
  Returns 0x73

  ## Examples
      iex> BitcoinLib.Script.Opcodes.Stack.IfDup.v()
      0x73
  """
  @spec v() :: 0x73
  def v do
    @value
  end

  @doc """
  Returns <<0x73>>

  ## Examples
      iex> BitcoinLib.Script.Opcodes.Stack.IfDup.encode()
      <<0x73>>
  """
  def encode() do
    <<@value::8>>
  end

  @doc """
  If the top stack value is not 0, duplicate it.

  ## Examples
      iex> stack = [4, 3]
      ...> %BitcoinLib.Script.Opcodes.Stack.IfDup{}
      ...> |> BitcoinLib.Script.Opcodes.Stack.IfDup.execute(stack)
      {:ok, [4, 4, 3]}
  """
  @spec execute(IfDup.t(), list()) :: {:ok, list()} | {:error, binary()}
  def execute(_opcode, []), do: {:error, "trying to execute OP_DROP on an empty stack"}

  def execute(_opcode, [0 | _rest] = stack), do: {:ok, stack}

  def execute(_opcode, [first_element | rest]) do
    {:ok, [first_element | [first_element | rest]]}
  end
end