defmodule BitcoinLib.Key.HD.DerivationPath.Parser.Purpose do @moduledoc """ Determines which address space is being used, either bip44, bip49 or bip84. This is a hardened value. See: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#purpose """ # https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#purpose @bip44_purpose "44'" @bip44_value 44 @bip44_atom :bip44 # https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki#public-key-derivation @bip49_purpose "49'" @bip49_value 49 @bip49_atom :bip49 # https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki#public-key-derivation @bip84_purpose "84'" @bip84_value 84 @bip84_atom :bip84 @doc """ Converts a list of path levels managed up until the purpose value, extracts the purpose and returns the remaining levels ## Examples iex> ["84'", "0", "1", "2", "3"] ...> |> BitcoinLib.Key.HD.DerivationPath.Parser.Purpose.extract() {:ok, :bip84, ["0", "1", "2", "3"]} """ @spec extract(list()) :: {:ok, nil, []} | {:ok, atom(), list()} | {:error, binary()} def extract([]), do: {:ok, nil, []} def extract([purpose | rest]) do purpose |> String.trim() |> convert(rest) end def get_atom(@bip44_value), do: @bip44_atom def get_atom(@bip49_value), do: @bip49_atom def get_atom(@bip84_value), do: @bip84_atom def get_atom(unknown), do: {:error, "#{unknown} is an invalid purpose"} defp convert(@bip44_purpose, rest), do: {:ok, @bip44_atom, rest} defp convert(@bip49_purpose, rest), do: {:ok, @bip49_atom, rest} defp convert(@bip84_purpose, rest), do: {:ok, @bip84_atom, rest} defp convert(purpose, _rest) do message = case String.ends_with?(purpose, "'") do true -> "#{purpose} is an invalid purpose" false -> "purpose must be a hardened value" end {:error, message} end end