Class: Stellar::Util::Base58

Inherits:
Object
  • Object
show all
Defined in:
lib/stellar/util/base58.rb

Constant Summary collapse

STELLAR_ALPHABET =
"gsphnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCr65jkm8oFqi1tuvAxyz"
BITCOIN_ALPHABET =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
BASE =
58
VERSION_BYTES =

TODO: improve the conversion to bitstring, perhaps ‘Fixnum#to_byte`?

{
  none:       [1].pack("C").encode("BINARY"),
  account_id: [0].pack("C").encode("BINARY"),
  seed:       [33].pack("C").encode("BINARY"),
}

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(alphabet) ⇒ Base58

Returns a new instance of Base58.

Raises:

  • (ArgumentError)


23
24
25
26
# File 'lib/stellar/util/base58.rb', line 23

def initialize(alphabet)
  raise ArgumentError, "Invalid alphabet length" if alphabet.length != BASE
  @alphabet = alphabet
end

Class Method Details

.bitcoinObject



19
20
21
# File 'lib/stellar/util/base58.rb', line 19

def self.bitcoin
  Thread.current[:bitcoin_base58] ||= new(BITCOIN_ALPHABET)
end

.stellarObject



15
16
17
# File 'lib/stellar/util/base58.rb', line 15

def self.stellar
  Thread.current[:stellar_base58] ||= new(STELLAR_ALPHABET)
end

Instance Method Details

#check_decode(expected_version, str) ⇒ Object

Raises:

  • (ArgumentError)


52
53
54
55
56
57
58
59
60
61
62
# File 'lib/stellar/util/base58.rb', line 52

def check_decode(expected_version, str)
  decoded      = decode(str)
  version_byte = decoded[0]
  payload      = decoded[1...-4]
  check        = decoded[-4..-1]
  version      = VERSION_BYTES.key(version_byte)

  raise ArgumentError, "Unexpected version: #{version.inspect}" if version != expected_version
  raise ArgumentError, "Invalid checksum" if check != checksum(decoded[0...-4])
  payload
end

#check_encode(version, byte_str) ⇒ Object

Raises:

  • (ArgumentError)


37
38
39
40
41
42
43
44
# File 'lib/stellar/util/base58.rb', line 37

def check_encode(version, byte_str)
  version_byte = VERSION_BYTES[version]
  raise ArgumentError, "Invalid version: #{version}" if version_byte.blank?

  payload = version_byte + byte_str.dup.force_encoding("BINARY")
  check   = checksum(payload)
  encode(payload + check)
end

#checksum(bytes) ⇒ Object



64
65
66
67
# File 'lib/stellar/util/base58.rb', line 64

def checksum(bytes)
  inner = Digest::SHA256.digest(bytes)
  Digest::SHA256.digest(inner)[0...4]
end

#decode(str) ⇒ Object



46
47
48
49
50
# File 'lib/stellar/util/base58.rb', line 46

def decode(str)
  leading_zeros = str.each_char.take_while{|c| c == @alphabet[0]}.length

  ("\x00" * leading_zeros) + decode_int(str)
end

#encode(byte_str) ⇒ Object



28
29
30
31
32
33
34
35
# File 'lib/stellar/util/base58.rb', line 28

def encode(byte_str)
  return "" if byte_str.nil? || byte_str.empty?

  leading_zeros = byte_str.each_byte.take_while{|b| b == 0}.length
  int           = bytes_to_int(byte_str) # step 1

  encode_int(int, leading_zeros)
end