area58check v0.1.0 Area58check View Source

This module includes the needed functions to encode or decode base58check strings.

Base58check is used in Bitcoin whenever there is a need for a user to read or transcribe a number, such a bitcoin adresses, encrypted key, private key, or script hash.

Prefixed versions

The prefixed version parameter indicates what kind of binary is being encoded. E.g. If you are encoding a private key use :wif, if you are encoding a bitcoin address use :p2pkh. The version prefix will be embeded in the encoded version. You can also use other types described in prefix_version_type/0

Checksum

The encoded versions will include a few bytes to ensure that its correct and no transcribing errors were present.

Usage

To know the decoded binary of any encoded string just use the encode/2 function indicating the prefix version you want:

iex> privkey = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" |> Base.decode16!()
iex> %{decoded: _decoded} = encode(privkey, <<128>>)
%Area58check{
  encoded: "5HpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb",
  decoded: <<1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239>>,
  version: :wif,
  version_bin: <<128>>
}

To know the encoded string of any binary use decode/1 function:

iex> {:ok, %{encoded: _encoded}} = decode("5HpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb")
{:ok, %Area58check{
  encoded: "5HpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb",
  decoded: <<1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239>>,
  version: :wif,
  version_bin: <<128>>
}}

Link to this section Summary

Types

Indicates the type of bitcoin string being encoded/decoded. E.g.: mainet WalletImportFormat (WIF), bitcoin address, testnet wif, etc

t()

A result returned by both encode/2 and decode/1 with the prefixed version, the encoded base58check string, and the decoded binary

Functions

Decodes a previously encoded string using base58check returning the used version prefix, the original binary

Encodes a string using base58check with the provided version prefix

Link to this section Types

Link to this type prefix_version_type() View Source
prefix_version_type() ::
  atom() |
  binary() |
  pos_integer() |
  [0..255, ...]

Indicates the type of bitcoin string being encoded/decoded. E.g.: mainet WalletImportFormat (WIF), bitcoin address, testnet wif, etc.

The prefixed version can be provided in several formats:

  • An atom. E.g.: :testnet_p2sh
  • The hexadecimal or numeric representation. E.g.: 0x043587CF
  • Character list: E.g.: [4, 136, 178, 30]
  • Binary string: E.g.: <<4, 136, 178, 30>>
Link to this type t() View Source
t() :: %Area58check{decoded: binary(), encoded: String.t(), version: atom(), version_bin: binary()}

A result returned by both encode/2 and decode/1 with the prefixed version, the encoded base58check string, and the decoded binary.

Link to this section Functions

Link to this function decode(payload) View Source
decode(String.t()) :: {:ok, Area58check.t()} | {:error, atom()}

Decodes a previously encoded string using base58check returning the used version prefix, the original binary.

An error message will be returned in case there was a checksum error or a character is not valid.

Examples:

Decode WIP key and get the hexadecimal private key

iex> {:ok, %{decoded: decoded} } = decode("5HpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb")
{:ok, %Area58check{
  encoded: "5HpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb",
  decoded: <<1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239>>,
  version: :wif,
  version_bin: <<128>>}}
iex> decoded |> Base.encode16
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"

If any character of the key is wrong (J), checksum will be invalid:

iex> decode("5JpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb")
{:error, :checksum_incorrect}

The null string means the checksum is nor present, nor valid:

iex> decode("")
{:error, :checksum_incorrect}

If any character is not a base58 character (E.g.: 0)

iex> decode("50pneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb")
{:error, :incorrect_base58}
Link to this function encode(payload, version_prefix) View Source
encode(String.t(), prefix_version_type()) :: t()

Encodes a string using base58check with the provided version prefix .

The argument version_prefix can be a binary, integer or a list of bytes (see examples next paragraph). All available prefixes are coded in the module Area58check.Prefixes.

Examples

iex> privkey = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" |> Base.decode16!()
iex> encode(privkey, :wif)
%Area58check{
  encoded: "5HpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb",
  decoded: <<1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239>>,
  version: :wif,
  version_bin: <<128>>}

The version prefix can be indicated in several alternative ways:

iex> privkey = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" |> Base.decode16!()
iex> encode(privkey, <<128>>)
%Area58check{
  encoded: "5HpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb",
  decoded: <<1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239>>,
  version: :wif,
  version_bin: <<128>>
}
iex> encode(privkey, 0x80)
%Area58check{
  encoded: "5HpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb",
  decoded: <<1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239>>,
  version: :wif,
  version_bin: <<128>>
}
iex> encode(privkey, [128])
%Area58check{
  encoded: "5HpneLQNKrcznVCQpzodYwAmZ4AoHeyjuRf9iAHAa498rP5kuWb",
  decoded: <<1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239>>,
  version: :wif,
  version_bin: <<128>>
}

Example to generate an uncompresed bitcoin address from public key

iex> privkey = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" |> Base.decode16!()
iex> {uncompressed_pubkey, _priv_key} = :crypto.generate_key(:ecdh, :crypto.ec_curve(:secp256k1), privkey)
iex> uncompressed_pubkey = :crypto.hash(:ripemd160, :crypto.hash(:sha256, uncompressed_pubkey))
iex> encode(uncompressed_pubkey, <<0>>)
%Area58check{
  encoded: "1CLrrRUwXswyF2EVAtuXyqdk4qb8DSUHCX",
  decoded: <<124, 106, 230, 190, 9, 150, 81, 133, 169, 75, 13, 161, 139, 201, 42, 157, 252, 238, 97, 23>>,
  version: :p2pkh,
  version_bin: <<0>>}

If version is an atom, and is not recognized returns error:

iex> encode("Any string", :sahdkjfhkjasdfhksldjf)
** (ArgumentError) Version prefix :sahdkjfhkjasdfhksldjf is not a recognized version. You can either pass a charlist (ex: [4, 136, 178, 30]), number (ex: 70617039), hexadecimal (ex: 0x043587CF), binary version (ex: <<4, 136, 178, 30>>), or a recognized atom like any of: :bip32_privkey, :bip32_pubkey, :p2pkh, :p2sh, :tesnet_bip32_privkey, :tesnet_bip32_pubkey, :tesnet_p2pkh, :tesnet_p2sh, :tesnet_wif, :wif