Module: BSV::Wallet::Serializer::Common
- Defined in:
- lib/bsv/wallet/serializer/common.rb
Overview
Shared binary encoding helpers for BRC-103 per-call serializers.
Port of the shared helpers in go-sdk/wallet/serializer/serializer.go. All methods operate on BSV::Wallet::Wire::Writer / Reader instances.
Constant Summary collapse
- COUNTERPARTY_SELF =
0x0B
11- COUNTERPARTY_ANYONE =
0x0C
12- PUBKEY_SIZE =
compressed secp256k1 public key
33- SEND_WITH_STATUS_UNPROVEN =
Send-with result status codes (Go status.go).
1- SEND_WITH_STATUS_SENDING =
2- SEND_WITH_STATUS_FAILED =
3- SEND_WITH_STATUS_CODES =
{ unproven: SEND_WITH_STATUS_UNPROVEN, sending: SEND_WITH_STATUS_SENDING, failed: SEND_WITH_STATUS_FAILED }.freeze
- SEND_WITH_CODE_STATUSES =
SEND_WITH_STATUS_CODES.invert.freeze
Class Method Summary collapse
-
.decode_outpoints(bytes) ⇒ Array<String>?
Decode a list of outpoints from binary (encoded as encode_outpoints).
-
.encode_outpoints(outpoints) ⇒ String?
Encode a list of outpoints (varint count + 32-byte wire-order txid + varint vout each).
- .read_counterparty(reader) ⇒ Object
- .read_key_related_params(reader) ⇒ Object
- .read_privileged_params(reader) ⇒ Object
- .read_protocol(reader) ⇒ Object
-
.read_send_with_results(reader) ⇒ Array<Hash>?
Read a send_with_results array.
-
.to_binary(bytes) ⇒ Object
Coerce a byte payload to a binary String (ASCII-8BIT encoding).
-
.write_counterparty(writer, counterparty) ⇒ Object
Encode a counterparty: ‘self’ | ‘anyone’ | 66-char hex compressed pubkey.
-
.write_key_related_params(writer, protocol_id:, key_id:, counterparty:, privileged: nil, privileged_reason: nil) ⇒ Object
Encode key-related params: protocol + key_id + counterparty + privileged params.
-
.write_privileged_params(writer, privileged, privileged_reason) ⇒ Object
Encode privileged flag + privileged_reason.
-
.write_protocol(writer, protocol_id) ⇒ Object
Encode a BRC-43 protocol ID: [security_level (0-2), protocol_name].
-
.write_send_with_results(writer, results) ⇒ Object
Write a send_with_results array: varint count + txid (32 bytes) + status byte each.
Class Method Details
.decode_outpoints(bytes) ⇒ Array<String>?
Decode a list of outpoints from binary (encoded as encode_outpoints).
145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/bsv/wallet/serializer/common.rb', line 145 def decode_outpoints(bytes) return nil if bytes.nil? || bytes.b.empty? r = Wire::Reader.new(bytes) count = r.read_varint return nil if count == 0xFFFF_FFFF_FFFF_FFFF count.times.map do txid_hex = r.read_bytes(32).unpack1('H*') vout = r.read_varint "#{txid_hex}.#{vout}" end end |
.encode_outpoints(outpoints) ⇒ String?
Encode a list of outpoints (varint count + 32-byte wire-order txid + varint vout each). Returns nil bytes for nil input.
129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/bsv/wallet/serializer/common.rb', line 129 def encode_outpoints(outpoints) return nil if outpoints.nil? w = Wire::Writer.new w.write_varint(outpoints.length) outpoints.each do |op| txid_hex, vout = op.split('.') w.write_bytes([txid_hex].pack('H*')) w.write_varint(vout.to_i) end w.buf end |
.read_counterparty(reader) ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/bsv/wallet/serializer/common.rb', line 61 def read_counterparty(reader) first = reader.read_byte case first when COUNTERPARTY_SELF 'self' when COUNTERPARTY_ANYONE 'anyone' else rest = reader.read_bytes(PUBKEY_SIZE - 1) ([first].pack('C') + rest).unpack1('H*') end end |
.read_key_related_params(reader) ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/bsv/wallet/serializer/common.rb', line 111 def (reader) protocol_id = read_protocol(reader) key_id = reader.read_str_with_varint_len counterparty = read_counterparty(reader) privileged, reason = read_privileged_params(reader) { protocol_id: protocol_id, key_id: key_id, counterparty: counterparty, privileged: privileged, privileged_reason: reason } end |
.read_privileged_params(reader) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/bsv/wallet/serializer/common.rb', line 88 def read_privileged_params(reader) privileged = reader.read_optional_bool # 0xFF as the leading byte is the nil-reason sentinel. Otherwise the # bytes start a Bitcoin varint length prefix (which can be 0xFD or # 0xFE for reasons >= 253 bytes; 0xFF would only collide if the # reason exceeded 4 GiB, which the protocol does not permit). if reader.peek_byte == 0xFF reader.read_byte [privileged, nil] else [privileged, reader.read_str_with_varint_len] end end |
.read_protocol(reader) ⇒ Object
38 39 40 41 42 |
# File 'lib/bsv/wallet/serializer/common.rb', line 38 def read_protocol(reader) level = reader.read_byte name = reader.read_str_with_varint_len [level, name] end |
.read_send_with_results(reader) ⇒ Array<Hash>?
Read a send_with_results array.
191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/bsv/wallet/serializer/common.rb', line 191 def read_send_with_results(reader) count = reader.read_varint return nil if count.zero? count.times.map do txid_hex = reader.read_bytes(32).unpack1('H*') code = reader.read_byte status = SEND_WITH_CODE_STATUSES.fetch(code) do raise ArgumentError, "invalid send_with status code: #{code}" end { txid: txid_hex, status: status } end end |
.to_binary(bytes) ⇒ Object
Coerce a byte payload to a binary String (ASCII-8BIT encoding).
Accepts either an Array<Integer> (as returned by ProtoWallet) or a String. Serialisers use this so they remain compatible with both the in-process wallet interface (Arrays) and the wire interface (Strings).
22 23 24 25 26 27 |
# File 'lib/bsv/wallet/serializer/common.rb', line 22 def to_binary(bytes) return ''.b if bytes.nil? return bytes.pack('C*').b if bytes.is_a?(Array) bytes.b end |
.write_counterparty(writer, counterparty) ⇒ Object
Encode a counterparty: ‘self’ | ‘anyone’ | 66-char hex compressed pubkey.
Wire format:
0x0B — self
0x0C — anyone
02/03 <32 bytes> — specific pubkey (first byte is the prefix of the 33-byte key)
50 51 52 53 54 55 56 57 58 59 |
# File 'lib/bsv/wallet/serializer/common.rb', line 50 def write_counterparty(writer, counterparty) case counterparty when 'self' writer.write_byte(COUNTERPARTY_SELF) when 'anyone' writer.write_byte(COUNTERPARTY_ANYONE) else writer.write_bytes([counterparty].pack('H*')) end end |
.write_key_related_params(writer, protocol_id:, key_id:, counterparty:, privileged: nil, privileged_reason: nil) ⇒ Object
Encode key-related params: protocol + key_id + counterparty + privileged params.
103 104 105 106 107 108 109 |
# File 'lib/bsv/wallet/serializer/common.rb', line 103 def (writer, protocol_id:, key_id:, counterparty:, privileged: nil, privileged_reason: nil) write_protocol(writer, protocol_id) writer.write_str_with_varint_len(key_id.to_s) write_counterparty(writer, counterparty) write_privileged_params(writer, privileged, privileged_reason) end |
.write_privileged_params(writer, privileged, privileged_reason) ⇒ Object
Encode privileged flag + privileged_reason.
Wire format: optional_bool (0xFF=nil, 0x00=false, 0x01=true) + reason as varint-len string, or 0xFF if reason is nil/empty (NegativeOneByte sentinel).
78 79 80 81 82 83 84 85 86 |
# File 'lib/bsv/wallet/serializer/common.rb', line 78 def write_privileged_params(writer, privileged, privileged_reason) writer.write_optional_bool(privileged) reason = privileged_reason.to_s if reason.empty? writer.write_byte(0xFF) else writer.write_str_with_varint_len(reason) end end |
.write_protocol(writer, protocol_id) ⇒ Object
Encode a BRC-43 protocol ID: [security_level (0-2), protocol_name].
Wire format: 1-byte security level + varint-len string.
32 33 34 35 36 |
# File 'lib/bsv/wallet/serializer/common.rb', line 32 def write_protocol(writer, protocol_id) level, name = protocol_id writer.write_byte(level.to_i) writer.write_str_with_varint_len(name.to_s) end |
.write_send_with_results(writer, results) ⇒ Object
Write a send_with_results array: varint count + txid (32 bytes) + status byte each. nil or empty → writes 0 count.
176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/bsv/wallet/serializer/common.rb', line 176 def write_send_with_results(writer, results) arr = results || [] writer.write_varint(arr.length) arr.each do |res| writer.write_bytes([res[:txid]].pack('H*')) code = SEND_WITH_STATUS_CODES.fetch(res[:status]) do raise ArgumentError, "invalid send_with status: #{res[:status]}" end writer.write_byte(code) end end |