lib/script/opcodes/stack/two_swap.ex

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

  Word OP_2SWAP
  Opcode 114
  Hex 0x72
  Input x1 x2 x3 x4
  Output x3 x4 x1 x2
  Swaps the top two pairs of items.
  """

  @behaviour BitcoinLib.Script.Opcode

  defstruct []

  alias BitcoinLib.Script.Opcodes.Stack.TwoSwap

  @type t :: TwoSwap

  @value 0x72

  @doc """
  Returns 0x72

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

  @doc """
  Returns <<0x72>>

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

  @doc """
  Swaps the top two pairs of items.

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

  def execute(_opcode, [_, _, _]),
    do: {:error, "trying to execute OP_2SWAP on a 3 elements stack"}

  def execute(_opcode, [x1, x2, x3, x4] ++ rest) do
    {:ok, [x3, x4, x1, x2] ++ rest}
  end
end