lib/script/opcodes/stack/two_dup.ex

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

  Word OP_2DUP
  Opcode 110
  Hex 0x6e
  Input x1 x2
  Output x1 x2 x1 x2
  Duplicates the top two stack items.
  """

  @behaviour BitcoinLib.Script.Opcode

  defstruct []

  alias BitcoinLib.Script.Opcodes.Stack.TwoDup

  @type t :: TwoDup

  @value 0x6E

  @doc """
  Returns 0x6e

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

  @doc """
  Returns <<0x6e>>

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

  @doc """
  Duplicates the top stack item.

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

  def execute(_opcode, [first, second] ++ remaining) do
    {:ok, [first, second, first, second] ++ remaining}
  end
end