lib/crypto/wif.ex

defmodule BitcoinLib.Crypto.Wif do
  @moduledoc """
  WIF Bitcoin private key format management

  Inspired by https://learnmeabitcoin.com/technical/wif
  """

  alias BitcoinLib.Crypto

  @mainnet 0x80
  @testnet 0xEF

  @doc """
  Converts a raw private key to the WIF format

  ## Examples
      iex> <<10, 141, 40, 107, 17, 185, 143, 108, 178, 88, 91, 98, 127, 244, 77, 18, 5,
      ...> 149, 96, 172, 212, 48, 220, 250, 18, 96, 239, 43, 217, 86, 147, 115>>
      ...> |> BitcoinLib.Crypto.Wif.from_bitstring
      "KwaDo7PNi4XPMaABfSEo9rP6uviDUATAqvyjjWTcKp4fxdkVJWLe"
  """
  @spec from_bitstring(bitstring()) :: String.t()
  def from_bitstring(value) do
    value
    |> add_prefix
    |> add_compressed
    |> add_checksum
    |> Base58.encode()
  end

  @doc """
  Converts a WIF encoded private key into its binary format

  ## Examples
      iex> "cThjSL4HkRECuDxUTnfAmkXFBEg78cufVBy3ZfEhKoxZo6Q38R5L"
      ...> |> BitcoinLib.Crypto.Wif.to_private_key()
      <<0xb6a42d01917404b740f9ef9d5cef08e13f998011246874dd65c033c4669e7009::256>>
  """
  @spec to_private_key(binary()) :: bitstring()
  def to_private_key(wif) do
    wif
    |> Base58.decode()
    |> decode_private_key
  end

  defp add_prefix(key) do
    <<0x80>> <> key
  end

  defp add_compressed(key) do
    key <> <<1>>
  end

  defp add_checksum(key) do
    key <> Crypto.checksum(key)
  end

  defp decode_private_key(<<@mainnet::8, key::bitstring-256, _compressed?::8, _checksum::32>>) do
    key
  end

  defp decode_private_key(<<@testnet::8, key::bitstring-256, _compressed?::8, _checksum::32>>) do
    key
  end
end