Class: BSV::Wallet::Wire::Reader

Inherits:
Object
  • Object
show all
Defined in:
lib/bsv/wallet_interface/wire/reader.rb

Overview

Consumes a binary byte string using BRC-100 wire protocol decoding conventions.

All multi-byte integers use little-endian order unless stated otherwise. VarInts follow Bitcoin encoding; the 9-byte MaxUint64 sentinel decodes as -1.

Constant Summary collapse

MAX_UINT64 =

The 64-bit sentinel value that encodes -1 in a signed VarInt.

0xFFFF_FFFF_FFFF_FFFF

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data) ⇒ Reader

Returns a new instance of Reader.



14
15
16
17
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 14

def initialize(data)
  @data = data.b
  @offset = 0
end

Instance Attribute Details

#offsetObject (readonly)

Current read position (bytes consumed so far).



20
21
22
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 20

def offset
  @offset
end

Instance Method Details

#read_byteInteger

Reads a single unsigned byte (0–255).

Returns:

  • (Integer)


25
26
27
28
29
30
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 25

def read_byte
  require_bytes(1)
  byte = @data.getbyte(@offset)
  @offset += 1
  byte
end

#read_byte_arrayString

Reads a VarInt-prefixed byte array.

Returns:

  • (String)

    binary string



102
103
104
105
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 102

def read_byte_array
  len = read_varint
  read_bytes(len)
end

#read_bytes(n) ⇒ String

Reads exactly n raw bytes.

Parameters:

  • n (Integer)

Returns:

  • (String)

    binary string



83
84
85
86
87
88
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 83

def read_bytes(n)
  require_bytes(n)
  slice = @data.byteslice(@offset, n)
  @offset += n
  slice
end

#read_counterpartyString?

Reads a counterparty value using the first-byte dispatch scheme.

Returns:

  • (String, nil)

    ‘self’, ‘anyone’, nil, or 66-char hex pubkey



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 158

def read_counterparty
  flag = read_byte
  case flag
  when 11
    'self'
  when 12
    'anyone'
  when 0
    nil
  else
    # First byte is part of the 33-byte compressed pubkey; read 32 more
    remaining = read_bytes(32)
    ([flag].pack('C') + remaining).unpack1('H*')
  end
end

#read_int8Integer

Reads a signed 8-bit integer (-128–127).

Returns:

  • (Integer)


35
36
37
38
39
40
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 35

def read_int8
  require_bytes(1)
  val = @data.byteslice(@offset, 1).unpack1('c')
  @offset += 1
  val
end

#read_mapHash?

Reads an optional string→string map: VarInt count + key/value pairs, or nil.

Returns:

  • (Hash, nil)


196
197
198
199
200
201
202
203
204
205
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 196

def read_map
  count = read_signed_varint
  return nil if count == -1

  count.times.each_with_object({}) do |_, hash|
    key = read_utf8_string
    val = read_utf8_string
    hash[key] = val
  end
end

#read_optional_boolBoolean?

Reads a signed Int8 optional boolean: 1=true, 0=false, -1=nil.

Returns:

  • (Boolean, nil)


138
139
140
141
142
143
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 138

def read_optional_bool
  val = read_int8
  return nil if val == -1

  val == 1
end

#read_optional_byte_arrayString?

Reads an optional VarInt-prefixed byte array; returns nil for the -1 sentinel.

Returns:

  • (String, nil)


110
111
112
113
114
115
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 110

def read_optional_byte_array
  len = read_signed_varint
  return nil if len == -1

  read_bytes(len)
end

#read_optional_utf8_stringString?

Reads an optional VarInt-prefixed UTF-8 string; returns nil for the -1 sentinel.

Returns:

  • (String, nil)


128
129
130
131
132
133
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 128

def read_optional_utf8_string
  len = read_signed_varint
  return nil if len == -1

  read_bytes(len).force_encoding('UTF-8')
end

#read_outpointArray(String, Integer)

Reads an outpoint: 32 bytes (txid hex in display order) + VarInt index.

Returns:

  • (Array(String, Integer))
    txid_hex, index


148
149
150
151
152
153
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 148

def read_outpoint
  txid_bytes = read_bytes(32)
  txid_hex = txid_bytes.unpack1('H*')
  index = read_varint
  [txid_hex, index]
end

#read_privilegedArray(Boolean|nil, String|nil)

Reads privileged parameters: optional bool + Int8-length reason string.

Returns:

  • (Array(Boolean|nil, String|nil))
    privileged, privileged_reason


210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 210

def read_privileged
  privileged = read_optional_bool
  reason_len = read_int8
  privileged_reason = if reason_len == -1
                        nil
                      elsif reason_len.negative?
                        raise ArgumentError, "invalid privileged_reason length: #{reason_len}"
                      else
                        read_bytes(reason_len).force_encoding('UTF-8')
                      end
  [privileged, privileged_reason]
end

#read_protocol_idArray(Integer, String)

Reads a protocol ID: UInt8 security level + VarInt-prefixed UTF-8 name.

Returns:

  • (Array(Integer, String))
    level, name


177
178
179
180
181
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 177

def read_protocol_id
  level = read_byte
  name = read_utf8_string
  [level, name]
end

#read_remainingString

Reads all remaining bytes from the current offset.

Returns:

  • (String)

    binary string



93
94
95
96
97
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 93

def read_remaining
  slice = @data.byteslice(@offset, @data.bytesize - @offset) || ''.b
  @offset = @data.bytesize
  slice
end

#read_signed_varintInteger

Reads a signed VarInt, returning -1 for the MaxUint64 sentinel.

Returns:

  • (Integer)

    non-negative value or -1



74
75
76
77
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 74

def read_signed_varint
  val = read_varint
  val == MAX_UINT64 ? -1 : val
end

#read_string_arrayArray<String>?

Reads an optional string array: VarInt count + strings, or nil for the -1 sentinel.

Returns:

  • (Array<String>, nil)


186
187
188
189
190
191
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 186

def read_string_array
  count = read_signed_varint
  return nil if count == -1

  count.times.map { read_utf8_string }
end

#read_utf8_stringString

Reads a VarInt-prefixed UTF-8 string.

Returns:

  • (String)


120
121
122
123
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 120

def read_utf8_string
  len = read_varint
  read_bytes(len).force_encoding('UTF-8')
end

#read_varintInteger

Reads an unsigned Bitcoin VarInt.

Returns:

  • (Integer)


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/bsv/wallet_interface/wire/reader.rb', line 45

def read_varint
  require_bytes(1)
  first = @data.getbyte(@offset)

  case first
  when 0..0xFC
    @offset += 1
    first
  when 0xFD
    require_bytes(3)
    val = @data.byteslice(@offset + 1, 2).unpack1('v')
    @offset += 3
    val
  when 0xFE
    require_bytes(5)
    val = @data.byteslice(@offset + 1, 4).unpack1('V')
    @offset += 5
    val
  else # 0xFF
    require_bytes(9)
    val = @data.byteslice(@offset + 1, 8).unpack1('Q<')
    @offset += 9
    val
  end
end