Module: BSV::Primitives::Base58
- Defined in:
- lib/bsv/primitives/base58.rb
Overview
Base58 and Base58Check encoding/decoding.
Implements the Base58 alphabet used throughout Bitcoin for addresses, WIF keys, and extended keys. Base58Check adds a 4-byte double-SHA-256 checksum for error detection.
Defined Under Namespace
Classes: ChecksumError
Constant Summary collapse
- ALPHABET =
The Base58 alphabet (no 0, O, I, l to avoid visual ambiguity).
'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'- BASE =
The base (58).
ALPHABET.length
- DECODE_MAP =
Reverse lookup table mapping ASCII byte values to Base58 digit indices.
Array.new(256, -1).tap do |map| ALPHABET.each_char.with_index { |c, i| map[c.ord] = i } end.freeze
Class Method Summary collapse
-
.check_decode(string, prefix_length: 0) ⇒ String, Hash
Decode a Base58Check string and verify its checksum.
-
.check_encode(payload, prefix: nil) ⇒ String
Encode binary data with a 4-byte double-SHA-256 checksum appended.
-
.decode(string) ⇒ String
Decode a Base58 string to binary data.
-
.encode(bytes) ⇒ String
Encode binary data as a Base58 string.
Class Method Details
.check_decode(string, prefix_length: 0) ⇒ String, Hash
Decode a Base58Check string and verify its checksum.
When prefix_length is greater than zero, the decoded payload is split into a prefix and data portion. The returned value is then a Hash with :prefix and :data keys. When prefix_length is zero (default), the raw payload is returned unchanged for backwards compatibility.
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/bsv/primitives/base58.rb', line 115 def check_decode(string, prefix_length: 0) data = decode(string) raise ChecksumError, 'input too short for checksum' if data.length < 4 payload = data[0...-4] checksum = data[-4..] expected = Digest.sha256d(payload)[0, 4] raise ChecksumError, 'checksum mismatch' unless checksum == expected return payload if prefix_length.zero? raise ArgumentError, 'prefix_length exceeds payload' if prefix_length > payload.length { prefix: payload[0, prefix_length], data: payload[prefix_length..] } end |
.check_encode(payload, prefix: nil) ⇒ String
Encode binary data with a 4-byte double-SHA-256 checksum appended.
When prefix is given, it is prepended to the payload before checksumming. The checksum covers the full prefix payload+ concatenation.
98 99 100 101 102 |
# File 'lib/bsv/primitives/base58.rb', line 98 def check_encode(payload, prefix: nil) full = (prefix || ''.b) + payload checksum = Digest.sha256d(full)[0, 4] encode(full + checksum) end |
.decode(string) ⇒ String
Decode a Base58 string to binary data.
Leading ‘1’ characters are decoded as zero bytes.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/bsv/primitives/base58.rb', line 65 def decode(string) raise ArgumentError, 'cannot decode empty string' if string.empty? # Count leading '1' characters (representing zero bytes) leading_ones = 0 string.each_char { |c| c == ALPHABET[0] ? leading_ones += 1 : break } # Convert from base58 to integer n = 0 string.each_char do |c| digit = DECODE_MAP[c.ord] raise ArgumentError, "invalid Base58 character: #{c.inspect}" if digit == -1 n = (n * BASE) + digit end # Convert integer to bytes hex = n.zero? ? '' : n.to_s(16) hex = "0#{hex}" if hex.length.odd? result = [hex].pack('H*') # Prepend zero bytes for leading '1' characters (("\x00" * leading_ones) + result).b end |
.encode(bytes) ⇒ String
Encode binary data as a Base58 string.
Leading zero bytes are preserved as ‘1’ characters.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/bsv/primitives/base58.rb', line 37 def encode(bytes) return '' if bytes.empty? # Count leading zero bytes leading_zeros = 0 bytes.each_byte { |b| b.zero? ? leading_zeros += 1 : break } # Convert to big integer and repeatedly divide by 58 n = bytes.unpack1('H*').to_i(16) result = +'' while n.positive? n, remainder = n.divmod(BASE) result << ALPHABET[remainder] end # Preserve leading zeros as '1' characters result << (ALPHABET[0] * leading_zeros) result.reverse! result end |