Module: AdequateCryptoAddress::Utils::Bech32

Defined in:
lib/adequate_crypto_address/utils/bech32.rb

Constant Summary collapse

CHARSET =
'qpzry9x8gf2tvdw0s3jn54khce6mua7l'.unpack('C*')
CHARSET_REV =
[
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
  -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
  1,  0,  3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
  -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
  1,  0,  3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
].freeze
GENERATORS =
[0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3].freeze

Class Method Summary collapse

Class Method Details

.convert_bits(chunks, from_bits:, to_bits:, pad:) ⇒ Object

Utility for converting bytes of data between bases. These is used for BIP 173 address encoding/decoding to convert between sequences of bytes representing 8-bit values and groups of 5 bits. Conversions may be padded with trailing 0 bits to the nearest byte boundary. Returns nil if conversion requires padding and pad is false.

For example:

convert_bits("\xFF\xFF", from_bits: 8, to_bits: 5, pad: true)
=> "\x1F\x1F\x1F\10"

See https://github.com/bitcoin/bitcoin/blob/595a7bab23bc21049526229054ea1fff1a29c0bf/src/utilstrencodings.h#L154



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/adequate_crypto_address/utils/bech32.rb', line 58

def convert_bits(chunks, from_bits:, to_bits:, pad:)
  return nil unless valid_chunks?(chunks, from_bits)

  conversion = {
    from_bits: from_bits,
    to_bits: to_bits,
    output_mask: (1 << to_bits) - 1,
    buffer_mask: (1 << (from_bits + to_bits - 1)) - 1
  }
  buffer = 0
  bits = 0
  output = []

  chunks.each do |chunk|
    buffer, bits, converted = convert_chunk(chunk, buffer, bits, conversion)
    output.concat(converted)
  end

  remainder = conversion_remainder(buffer, bits, conversion, pad)
  return nil if remainder == :invalid

  output << remainder if remainder

  output
end

.decode(input, ignore_length: false, include_encoding: false) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/adequate_crypto_address/utils/bech32.rb', line 28

def decode(input, ignore_length: false, include_encoding: false)
  hrp_string, data_string = split_input(input, ignore_length: ignore_length)
  return nil unless hrp_string

  hrp, checksum, casing = decode_hrp(hrp_string)
  return nil unless hrp

  data, checksum, casing = decode_data(data_string, checksum, casing)
  return nil unless data
  return nil if casing.values.all?

  encoding = checksum_encoding(checksum)
  return nil unless encoding

  decoded = [hrp.pack('C*'), data]
  include_encoding ? decoded << encoding : decoded
end

.polymod_step(pre) ⇒ Object



21
22
23
24
25
26
# File 'lib/adequate_crypto_address/utils/bech32.rb', line 21

def polymod_step(pre)
  b = pre >> 25
  GENERATORS.each_with_index.reduce((pre & 0x1FFFFFF) << 5) do |checksum, (generator, index)|
    checksum ^ (-((b >> index) & 1) & generator)
  end
end