Packages
elixium_core
0.4.2
0.6.3
0.6.2
0.6.1
0.6.0
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.1
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.4
0.1.3
The core package for the Elixium blockchain, containing all the modules needed to run the chain
Current section
Files
Jump to
Current section
Files
lib/encoding/mnemonic.ex
defmodule Elixium.Mnemonic do
alias Elixium.Utilities
@leading_zeros_for_mnemonic 8
@leading_zeros_of_mnemonic 11
@regex_chunk_from_entropy Regex.compile!(".{1,#{@leading_zeros_of_mnemonic}}")
@regex_chunk_to_entropy Regex.compile!(".{1,#{@leading_zeros_for_mnemonic}}")
@words File.stream!("priv/words.txt")
|> Stream.map(&String.trim/1)
|> Enum.to_list()
@doc """
Gets the correct checksum of a binary
"""
@spec checksum_length(binary) :: Integer.t()
def checksum_length(entropy_bytes) do
entropy_bytes
|> bit_size()
|> div(32)
end
@doc """
Verifies if the binary needs to be normalized
"""
@spec maybe_normalize(binary) :: binary
def maybe_normalize(binary) do
binary
|> String.valid?()
|> normalize(binary)
end
@doc """
Using a private address generate a mnemonic seed
"""
@spec from_entropy(binary) :: String.t()
def from_entropy(binary) do
binary
|> maybe_normalize()
|> append_checksum()
|> mnemonic()
end
@doc """
Using a mnemonic seed generate a private seed
"""
@spec to_entropy(String.t()) :: binary
def to_entropy(mnemonic) do
mnemonic
|> indicies()
|> bytes()
|> entropy()
end
defp append_checksum(bytes) do
bytes
|> checksum()
|> append(bytes)
end
defp checksum(entropy) do
entropy
|> Utilities.sha256()
|> to_binary_string()
|> take_first(entropy)
end
defp to_binary_string(bytes) do
bytes
|> :binary.bin_to_list()
|> Enum.map(&binary_for_mnemonic/1)
|> Enum.join()
end
defp binary_for_mnemonic(byte), do: to_binary(byte, @leading_zeros_for_mnemonic)
defp to_binary(byte, leading_zeros) do
byte
|> Integer.to_string(2)
|> String.pad_leading(leading_zeros, "0")
end
defp take_first(binary_string, bytes) do
bytes
|> checksum_range()
|> slice(binary_string)
end
defp checksum_range(bytes) do
bytes
|> checksum_length()
|> range()
end
defp range(length), do: Range.new(0, length - 1)
defp slice(range, binary_string), do: String.slice(binary_string, range)
defp append(checksum, bytes), do: to_binary_string(bytes) <> checksum
defp mnemonic(entropy) do
@regex_chunk_from_entropy
|> Regex.scan(entropy)
|> List.flatten()
|> Enum.map(&word/1)
|> Enum.join(" ")
end
defp word(binary) do
binary
|> String.to_integer(2)
|> pick_word()
end
defp pick_word(index), do: Enum.at(@words, index)
defp indicies(mnemonic) do
mnemonic
|> String.split()
|> Enum.map(&word_binary_index/1)
|> Enum.join()
end
defp word_binary_index(word) do
@words
|> Enum.find_index(&(&1 == word))
|> binary_of_index()
end
defp binary_of_index(index), do: to_binary(index, @leading_zeros_of_mnemonic)
defp bytes(bits) do
bits
|> String.length()
|> div(33)
|> Kernel.*(32)
|> range()
|> slice(bits)
end
defp entropy(entropy_bits) do
@regex_chunk_to_entropy
|> Regex.scan(entropy_bits)
|> List.flatten()
|> Enum.map(&String.to_integer(&1, 2))
|> :binary.list_to_bin()
end
defp normalize(true, string), do: Base.decode16!(string, case: :mixed)
defp normalize(false, binary), do: binary
end