lib/key/hd/fingerprint.ex

defmodule BitcoinLib.Key.HD.Fingerprint do
  @moduledoc """
  A fingerprint is a small hash of a public key
  """
  alias BitcoinLib.Crypto
  alias BitcoinLib.Key.{PrivateKey, PublicKey}

  @doc """
  Compute a private key's fingerprint

  ## Examples
      iex> %BitcoinLib.Key.PrivateKey {
      ...>   key: <<0xE8F32E723DECF4051AEFAC8E2C93C9C5B214313817CDB01A1494B917C8436B35::256>>,
      ...>   chain_code: <<0x873DFF81C02F525623FD1FE5167EAC3A55A049DE3D314BB42EE227FFED37D508::256>>
      ...> }
      ...> |> BitcoinLib.Key.HD.Fingerprint.compute()
      <<0x3442193E::32>>

      iex> %BitcoinLib.Key.PublicKey{
      ...>   key: <<0x252C616D91A2488C1FD1F0F172E98F7D1F6E51F8F389B2F8D632A8B490D5F6DA9::264>>,
      ...>   chain_code: <<0x463223AAC10FB13F291A1BC76BC26003D98DA661CB76DF61E750C139826DEA8B::256>>
      ...> }
      ...> |> BitcoinLib.Key.HD.Fingerprint.compute()
      <<0x18C1259::32>>
  """
  @spec compute(%PrivateKey{}) :: binary()
  def compute(%PrivateKey{} = private_key) do
    private_key
    |> to_public_key
    |> compute
  end

  @spec compute(%PublicKey{}) :: binary()
  def compute(%PublicKey{} = public_key) do
    <<raw_fingerprint::binary-4, _rest::binary>> =
      public_key.key
      |> Crypto.hash160()

    raw_fingerprint
  end

  @doc """
  Adds a fingerprint to a public key

  ## Examples
      iex> %BitcoinLib.Key.PrivateKey {
      ...>   key: <<0xE8F32E723DECF4051AEFAC8E2C93C9C5B214313817CDB01A1494B917C8436B35::256>>,
      ...>   chain_code: <<0x873DFF81C02F525623FD1FE5167EAC3A55A049DE3D314BB42EE227FFED37D508::256>>
      ...> }
      ...> |> BitcoinLib.Key.HD.Fingerprint.append()
      %BitcoinLib.Key.PrivateKey {
        fingerprint: <<0x3442193E::32>>,
        key: <<0xE8F32E723DECF4051AEFAC8E2C93C9C5B214313817CDB01A1494B917C8436B35::256>>,
        chain_code: <<0x873DFF81C02F525623FD1FE5167EAC3A55A049DE3D314BB42EE227FFED37D508::256>>
      }
    
      iex> %BitcoinLib.Key.PublicKey{
      ...>   key: <<0x252C616D91A2488C1FD1F0F172E98F7D1F6E51F8F389B2F8D632A8B490D5F6DA9::264>>,
      ...>   chain_code: <<0x463223AAC10FB13F291A1BC76BC26003D98DA661CB76DF61E750C139826DEA8B::256>>
      ...> }
      ...> |> BitcoinLib.Key.HD.Fingerprint.append()
      %BitcoinLib.Key.PublicKey{
        fingerprint: <<0x18C1259::32>>,
        key: <<0x252C616D91A2488C1FD1F0F172E98F7D1F6E51F8F389B2F8D632A8B490D5F6DA9::264>>,
        chain_code: <<0x463223AAC10FB13F291A1BC76BC26003D98DA661CB76DF61E750C139826DEA8B::256>>
      }
  """
  @spec append(%PrivateKey{}) :: %PrivateKey{}
  def append(%PrivateKey{} = private_key) do
    fingerprint =
      private_key
      |> compute()

    private_key
    |> Map.put(:fingerprint, fingerprint)
  end

  @spec append(%PublicKey{}) :: %PublicKey{}
  def append(%PublicKey{} = public_key) do
    fingerprint =
      public_key
      |> compute()

    public_key
    |> Map.put(:fingerprint, fingerprint)
  end

  defp to_public_key(%PrivateKey{} = private_key) do
    private_key
    |> PublicKey.from_private_key()
  end
end