Module: Philiprehberger::BaseConvert::Base58

Defined in:
lib/philiprehberger/base_convert/base58.rb

Overview

Base58 encoding and decoding using the Bitcoin alphabet

Alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

Constant Summary collapse

ALPHABET =
'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
BASE =
ALPHABET.length
DECODE_MAP =
ALPHABET.each_char.with_index.to_h.freeze

Class Method Summary collapse

Class Method Details

.decode(string) ⇒ String

Decode a Base58 string

Parameters:

  • string (String)

    the Base58-encoded string

Returns:

  • (String)

    the decoded string

Raises:

  • (Error)

    if the string contains invalid characters



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/philiprehberger/base_convert/base58.rb', line 39

def self.decode(string)
  return '' if string.empty?

  leading_ones = string.each_char.take_while { |c| c == ALPHABET[0] }.length

  num = 0
  string.each_char do |char|
    value = DECODE_MAP[char]
    raise Error, "invalid Base58 character: #{char}" if value.nil?

    num = (num * BASE) + value
  end

  result = []
  while num.positive?
    num, byte = num.divmod(256)
    result << byte
  end

  ("\x00" * leading_ones) + result.reverse.pack('C*')
end

.decode_int(string) ⇒ Integer

Decode a Base58 string to an integer

Parameters:

  • string (String)

    the Base58-encoded string

Returns:

  • (Integer)

    the decoded integer

Raises:

  • (ArgumentError)

    if the string is empty or contains invalid characters



86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/philiprehberger/base_convert/base58.rb', line 86

def self.decode_int(string)
  raise ArgumentError, 'input must be a non-empty string' if string.nil? || string.empty?

  num = 0
  string.each_char do |char|
    value = DECODE_MAP[char]
    raise ArgumentError, "invalid Base58 character: #{char}" if value.nil?

    num = (num * BASE) + value
  end

  num
end

.encode(string) ⇒ String

Encode a string to Base58

Parameters:

  • string (String)

    the input string

Returns:

  • (String)

    the Base58-encoded string



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/philiprehberger/base_convert/base58.rb', line 17

def self.encode(string)
  return '' if string.empty?

  bytes = string.bytes
  leading_zeros = bytes.take_while(&:zero?).length

  num = bytes.inject(0) { |acc, byte| (acc << 8) | byte }

  result = []
  while num.positive?
    num, remainder = num.divmod(BASE)
    result << ALPHABET[remainder]
  end

  (ALPHABET[0] * leading_zeros) + result.reverse.join
end

.encode_int(integer) ⇒ String

Encode a non-negative integer to Base58

Parameters:

  • integer (Integer)

    the input integer (must be >= 0)

Returns:

  • (String)

    the Base58-encoded string

Raises:

  • (ArgumentError)

    if the input is negative or not an integer



66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/philiprehberger/base_convert/base58.rb', line 66

def self.encode_int(integer)
  raise ArgumentError, 'input must be a non-negative integer' unless integer.is_a?(Integer) && integer >= 0

  return ALPHABET[0] if integer.zero?

  result = []
  num = integer
  while num.positive?
    num, remainder = num.divmod(BASE)
    result << ALPHABET[remainder]
  end

  result.reverse.join
end