Module: Bech32

Defined in:
lib/bech32.rb,
lib/bech32/nostr.rb,
lib/bech32/version.rb,
lib/bech32/nostr/nip19.rb,
lib/bech32/segwit_addr.rb,
lib/bech32/nostr/entity.rb,
lib/bech32/silent_payment_addr.rb,
sig/bech32/bech32.rbs,
sig/bech32/nostr/nip19.rbs,
sig/bech32/segwit_addr.rbs,
sig/bech32/nostr/entity.rbs,
sig/bech32/silent_payment_addr.rbs

Defined Under Namespace

Modules: Encoding, Nostr Classes: SegwitAddr, SilentPaymentAddr

Constant Summary collapse

SEPARATOR =

Returns:

  • (String)
'1'
CHARSET =

Returns:

  • (Array[String])
%w(q p z r y 9 x 8 g f 2 t v d w 0 s 3 j n 5 4 k h c e 6 m u a 7 l)
BECH32M_CONST =

Returns:

  • (Integer)
0x2bc830a3
VERSION =

Returns:

  • (String)
'1.5.1'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.convert_bits(data, from, to, padding = true) ⇒ Array[Integer]?

Convert bits from one base to another.

Parameters:

  • data (Array[Integer])
  • from (Integer)
  • to (Integer)
  • padding (Boolean) (defaults to: true)

Returns:

  • (Array[Integer], nil)


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/bech32.rb', line 97

def convert_bits(data, from, to, padding=true)
  acc = 0
  bits = 0
  ret = []
  maxv = (1 << to) - 1
  max_acc = (1 << (from + to - 1)) - 1
  data.each do |v|
    return nil if v < 0 || (v >> from) != 0
    acc = ((acc << from) | v) & max_acc
    bits += from
    while bits >= to
      bits -= to
      ret << ((acc >> bits) & maxv)
    end
  end
  if padding
    ret << ((acc << (to - bits)) & maxv) unless bits == 0
  elsif bits >= from || ((acc << (to - bits)) & maxv) != 0
    return nil
  end
  ret
end

.create_checksum(hrp, data, spec) ⇒ Array[Integer]

Create checksum for hrp and data.

Parameters:

  • hrp (String)
  • data (Array[Integer])
  • spec (Integer)

Returns:

  • (Array[Integer])


65
66
67
68
69
70
# File 'lib/bech32.rb', line 65

def create_checksum(hrp, data, spec)
  values = expand_hrp(hrp) + data
  const = (spec == Bech32::Encoding::BECH32M ? Bech32::BECH32M_CONST : 1)
  polymod = polymod(values + [0, 0, 0, 0, 0, 0]) ^ const
  (0..5).map{|i|(polymod >> 5 * (5 - i)) & 31}
end

.decode(bech, max_length = 90) ⇒ [String, Array[Integer], Integer]?

Decode bech32/bech32m string. Returns nil if invalid.

Parameters:

  • bech (String)
  • max_length (Integer) (defaults to: 90)

Returns:

  • ([String, Array[Integer], Integer], nil)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/bech32.rb', line 46

def decode(bech, max_length = 90)
  # check uppercase/lowercase
  return nil if bech.bytes.index{|x| x < 33 || x > 126}
  return nil if (bech.downcase != bech && bech.upcase != bech)
  bech = bech.downcase
  # check data length
  pos = bech.rindex(SEPARATOR)
  return nil if pos.nil? || pos < 1 || pos + 7 > bech.length || bech.length > max_length
  # check valid charset
  bech[pos+1..-1].each_char{|c|return nil unless CHARSET.include?(c)}
  # split hrp and data
  hrp = bech[0..pos-1]
  data = bech[pos+1..-1].each_char.map{|c|CHARSET.index(c)}
  # check checksum
  spec = verify_checksum(hrp, data)
  spec ? [hrp, data[0..-7], spec] : nil
end

.encode(hrp, data, spec) ⇒ String

Encode hrp and data to bech32/bech32m string.

Parameters:

  • hrp (String)
  • data (Array[Integer])
  • spec (Integer)

Returns:

  • (String)


29
30
31
32
# File 'lib/bech32.rb', line 29

def encode(hrp, data, spec)
  checksummed = data + create_checksum(hrp, data, spec)
  hrp + SEPARATOR + checksummed.map{|i|CHARSET[i]}.join
end

.verify_checksum(hrp, data) ⇒ Integer?

Verify checksum. Returns encoding spec if valid, nil otherwise.

Parameters:

  • hrp (String)
  • data (Array[Integer])

Returns:

  • (Integer, nil)


76
77
78
79
80
81
82
83
84
# File 'lib/bech32.rb', line 76

def verify_checksum(hrp, data)
  const = polymod(expand_hrp(hrp) + data)
  case const
  when 1
    Encoding::BECH32
  when BECH32M_CONST
    Encoding::BECH32M
  end
end

Instance Method Details

#convert_bits(data, from, to, padding = true) ⇒ Array[Integer]?

Convert bits from one base to another.

Parameters:

  • data (Array[Integer])
  • from (Integer)
  • to (Integer)
  • padding (Boolean) (defaults to: true)

Returns:

  • (Array[Integer], nil)


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/bech32.rb', line 97

def convert_bits(data, from, to, padding=true)
  acc = 0
  bits = 0
  ret = []
  maxv = (1 << to) - 1
  max_acc = (1 << (from + to - 1)) - 1
  data.each do |v|
    return nil if v < 0 || (v >> from) != 0
    acc = ((acc << from) | v) & max_acc
    bits += from
    while bits >= to
      bits -= to
      ret << ((acc >> bits) & maxv)
    end
  end
  if padding
    ret << ((acc << (to - bits)) & maxv) unless bits == 0
  elsif bits >= from || ((acc << (to - bits)) & maxv) != 0
    return nil
  end
  ret
end

#create_checksum(hrp, data, spec) ⇒ Array[Integer]

Create checksum for hrp and data.

Parameters:

  • hrp (String)
  • data (Array[Integer])
  • spec (Integer)

Returns:

  • (Array[Integer])


65
66
67
68
69
70
# File 'lib/bech32.rb', line 65

def create_checksum(hrp, data, spec)
  values = expand_hrp(hrp) + data
  const = (spec == Bech32::Encoding::BECH32M ? Bech32::BECH32M_CONST : 1)
  polymod = polymod(values + [0, 0, 0, 0, 0, 0]) ^ const
  (0..5).map{|i|(polymod >> 5 * (5 - i)) & 31}
end

#decode(bech, max_length = 90) ⇒ [String, Array[Integer], Integer]?

Decode bech32/bech32m string. Returns nil if invalid.

Parameters:

  • bech (String)
  • max_length (Integer) (defaults to: 90)

Returns:

  • ([String, Array[Integer], Integer], nil)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/bech32.rb', line 46

def decode(bech, max_length = 90)
  # check uppercase/lowercase
  return nil if bech.bytes.index{|x| x < 33 || x > 126}
  return nil if (bech.downcase != bech && bech.upcase != bech)
  bech = bech.downcase
  # check data length
  pos = bech.rindex(SEPARATOR)
  return nil if pos.nil? || pos < 1 || pos + 7 > bech.length || bech.length > max_length
  # check valid charset
  bech[pos+1..-1].each_char{|c|return nil unless CHARSET.include?(c)}
  # split hrp and data
  hrp = bech[0..pos-1]
  data = bech[pos+1..-1].each_char.map{|c|CHARSET.index(c)}
  # check checksum
  spec = verify_checksum(hrp, data)
  spec ? [hrp, data[0..-7], spec] : nil
end

#encode(hrp, data, spec) ⇒ String

module_function methods are also available as instance methods

Parameters:

  • hrp (String)
  • data (Array[Integer])
  • spec (Integer)

Returns:

  • (String)


29
30
31
32
# File 'lib/bech32.rb', line 29

def encode(hrp, data, spec)
  checksummed = data + create_checksum(hrp, data, spec)
  hrp + SEPARATOR + checksummed.map{|i|CHARSET[i]}.join
end

#expand_hrp(hrp) ⇒ Array[Integer]

Expand HRP for checksum computation.

Parameters:

  • hrp (String)

Returns:

  • (Array[Integer])


87
88
89
# File 'lib/bech32.rb', line 87

def expand_hrp(hrp)
  hrp.each_char.map{|c|c.ord >> 5} + [0] + hrp.each_char.map{|c|c.ord & 31}
end

#polymod(values) ⇒ Integer

Compute polymod checksum.

Parameters:

  • values (Array[Integer])

Returns:

  • (Integer)


121
122
123
124
125
126
127
128
129
130
# File 'lib/bech32.rb', line 121

def polymod(values)
  generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
  chk = 1
  values.each do |v|
    top = chk >> 25
    chk = (chk & 0x1ffffff) << 5 ^ v
    (0..4).each{|i|chk ^= ((top >> i) & 1) == 0 ? 0 : generator[i]}
  end
  chk
end

#verify_checksum(hrp, data) ⇒ Integer?

Verify checksum. Returns encoding spec if valid, nil otherwise.

Parameters:

  • hrp (String)
  • data (Array[Integer])

Returns:

  • (Integer, nil)


76
77
78
79
80
81
82
83
84
# File 'lib/bech32.rb', line 76

def verify_checksum(hrp, data)
  const = polymod(expand_hrp(hrp) + data)
  case const
  when 1
    Encoding::BECH32
  when BECH32M_CONST
    Encoding::BECH32M
  end
end