elixirbitcoinbase58

Base58 returns incorrect value in elixir


I'm trying to encode hex to base58. It works with js library base-x

with hex = 1777c7ba65e23151ec09125011dd25c28998c70230e7b89ca6

Expected

base58 = AShDKgLSuCjGZr8Fs5SRLSYvmcSV7S4zwX

Got

base58 = cAvfov2bvPACeGktuSEtz6G526UBfCwpia1354fp5bYJwP2rhhnxqYkRcwRoDrmgqZaG

defmodule Base58 do
  @alphabet '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

  def encode(data, hash \\ "")

  def encode(data, hash) when is_binary(data) do
    encode_zeros(data) <> encode(:binary.decode_unsigned(data), hash)
  end

  def encode(0, hash), do: hash

  def encode(data, hash) do
    character = <<Enum.at(@alphabet, rem(data, 58))>>
    encode(div(data, 58), character <> hash)
  end

  defp encode_zeros(data) do
    <<Enum.at(@alphabet, 0)>>
    |> String.duplicate(leading_zeros(data))
  end

  defp leading_zeros(data) do
    :binary.bin_to_list(data)
    |> Enum.find_index(&(&1 != 0))
  end
end

Solution

  • What's happening is that the input is supposed to be the chunk of binary data represented by those hexadecimal digits, but you passed the string containing the hexadecimal digits to Base58.encode instead. You get the expected result if you pass the input as an integer (using the 0x prefix to mark it as a hexadecimal literal integer):

    iex(2)> Base58.encode("1777c7ba65e23151ec09125011dd25c28998c70230e7b89ca6")
    "cAvfov2bvPACeGktuSEtz6G526UBfCwpia1354fp5bYJwP2rhhnxqYkRcvTMFJ2ouddX"
    iex(3)> Base58.encode(0x1777c7ba65e23151ec09125011dd25c28998c70230e7b89ca6)
    "AShDKgLSuCjGZr8Fs5SRLSYvmcSV7S4zwX"
    

    If you already have this data in a string, you can use Integer.parse/2 to convert it to an integer:

    iex(5)> with {integer, _} <- Integer.parse("1777c7ba65e23151ec09125011dd25c28998c70230e7b89ca6", 16) do
    ...(5)>   Base58.encode(integer)
    ...(5)> end
    "AShDKgLSuCjGZr8Fs5SRLSYvmcSV7S4zwX"