Class: Sibit::Bech32
- Inherits:
-
Object
- Object
- Sibit::Bech32
- Defined in:
- lib/sibit/bech32.rb
Overview
Bech32 encoding and decoding for SegWit addresses.
Encodes witness programs to Bech32 addresses (bc1...) and decodes them back.
- Author
Yegor Bugayenko (yegor256@gmail.com)
- Copyright
Copyright (c) 2019-2026 Yegor Bugayenko
- License
MIT
Constant Summary collapse
- CHARSET =
'qpzry9x8gf2tvdw0s3jn54khce6mua7l'- GENERATOR =
[0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3].freeze
Class Method Summary collapse
- .bits(data, frombits, tobits, pad) ⇒ Object
- .checksum(hrp, data) ⇒ Object
- .encode(hrp, ver, prog) ⇒ Object
- .expanded(hrp) ⇒ Object
- .pm(values) ⇒ Object
Instance Method Summary collapse
-
#initialize(addr) ⇒ Bech32
constructor
A new instance of Bech32.
- #version ⇒ Object
- #witness ⇒ Object
Constructor Details
#initialize(addr) ⇒ Bech32
Returns a new instance of Bech32.
63 64 65 |
# File 'lib/sibit/bech32.rb', line 63 def initialize(addr) @addr = addr.downcase end |
Class Method Details
.bits(data, frombits, tobits, pad) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/sibit/bech32.rb', line 46 def self.bits(data, frombits, tobits, pad) acc = 0 num = 0 result = [] maxv = (1 << tobits) - 1 data.each do |v| acc = (acc << frombits) | v num += frombits while num >= tobits num -= tobits result << ((acc >> num) & maxv) end end result << ((acc << (tobits - num)) & maxv) if pad && num.positive? result end |
.checksum(hrp, data) ⇒ Object
27 28 29 30 |
# File 'lib/sibit/bech32.rb', line 27 def self.checksum(hrp, data) poly = pm((hrp) + data + [0, 0, 0, 0, 0, 0]) ^ 1 (0..5).map { |i| (poly >> (5 * (5 - i))) & 31 } end |
.encode(hrp, ver, prog) ⇒ Object
21 22 23 24 25 |
# File 'lib/sibit/bech32.rb', line 21 def self.encode(hrp, ver, prog) data = [ver] + bits([prog].pack('H*').bytes, 8, 5, true) chk = checksum(hrp, data) "#{hrp}1#{(data + chk).map { |d| CHARSET[d] }.join}" end |
.expanded(hrp) ⇒ Object
32 33 34 |
# File 'lib/sibit/bech32.rb', line 32 def self.(hrp) hrp.chars.map { |c| c.ord >> 5 } + [0] + hrp.chars.map { |c| c.ord & 31 } end |
.pm(values) ⇒ Object
36 37 38 39 40 41 42 43 44 |
# File 'lib/sibit/bech32.rb', line 36 def self.pm(values) chk = 1 values.each do |v| top = chk >> 25 chk = ((chk & 0x1ffffff) << 5) ^ v 5.times { |i| chk ^= GENERATOR[i] if (top >> i).allbits?(1) } end chk end |
Instance Method Details
#version ⇒ Object
84 85 86 87 |
# File 'lib/sibit/bech32.rb', line 84 def version _, data = parse data[0] end |
#witness ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/sibit/bech32.rb', line 67 def witness hrp, data = parse ver = data[0] unless ver.between?(0, 16) raise(Sibit::Error, "Unsupported witness version #{ver} in '#{@addr}'") end raise(Sibit::Error, "Invalid Bech32 checksum in '#{@addr}'") unless verified?(hrp, data) prog = convert(data[1..-7], 5, 8) if ver.zero? && [20, 32].none?(prog.length) raise(Sibit::Error, "Witness v0 program in '#{@addr}' must be 20 or 32 bytes") end unless prog.length.between?(2, 40) raise(Sibit::Error, "Witness program in '#{@addr}' must be 2 to 40 bytes") end prog.pack('C*').unpack1('H*') end |