Module: BSV::Auth::AuthPayload
- Defined in:
- lib/bsv/auth/auth_payload.rb
Overview
BRC-104 binary serialisation and deserialisation of HTTP requests and responses for signing/verification.
The wire format is byte-compatible with the TS and Go SDKs, enabling cross-SDK signature verification.
Absent optional fields (nil path, nil query, nil body) are encoded as ABSENT (VarInt MAX_UINT64 = 9 bytes of 0xFF). Zero-length fields are encoded as varint(0) followed by zero bytes — a distinct encoding from absent.
All varint operations delegate to Transaction::VarInt.
Constant Summary collapse
- ABSENT =
Sentinel value for absent optional fields (Bitcoin VarInt MAX_UINT64).
BSV::Transaction::VarInt::MAX_UINT64
- METHODS_WITH_BODY =
HTTP methods that typically carry a request body.
%w[POST PUT PATCH DELETE].freeze
Class Method Summary collapse
-
.deserialize_request(data) ⇒ Hash
Deserialises a binary request payload into a Hash.
-
.deserialize_response(data) ⇒ Hash
Deserialises a binary response payload into a Hash.
-
.serialize_request(request_nonce:, method:, path:, query:, headers:, body:) ⇒ String
Serialises an HTTP request into a binary payload for signing.
-
.serialize_response(request_id:, status:, headers:, body:) ⇒ String
Serialises an HTTP response into a binary payload for signing.
Class Method Details
.deserialize_request(data) ⇒ Hash
Deserialises a binary request payload into a Hash.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/bsv/auth/auth_payload.rb', line 78 def deserialize_request(data) bin = data.b pos = 0 # 32-byte request nonce request_nonce = bin.byteslice(pos, 32) pos += 32 # Method method_len, consumed = BSV::Transaction::VarInt.decode(bin, pos) pos += consumed method = bin.byteslice(pos, method_len).force_encoding('UTF-8') pos += method_len # Path (optional) path, pos = decode_optional_string(bin, pos) # Query (optional) query, pos = decode_optional_string(bin, pos) # Headers n_headers, consumed = BSV::Transaction::VarInt.decode(bin, pos) pos += consumed headers = [] n_headers.times do k_len, consumed = BSV::Transaction::VarInt.decode(bin, pos) pos += consumed k = bin.byteslice(pos, k_len).force_encoding('UTF-8') pos += k_len v_len, consumed = BSV::Transaction::VarInt.decode(bin, pos) pos += consumed v = bin.byteslice(pos, v_len).force_encoding('UTF-8') pos += v_len headers << [k, v] end # Body (optional) body, _pos = decode_optional_bytes(bin, pos) { request_nonce: request_nonce, method: method, path: path, query: query, headers: headers, body: body } end |
.deserialize_response(data) ⇒ Hash
Deserialises a binary response payload into a Hash.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/bsv/auth/auth_payload.rb', line 161 def deserialize_response(data) bin = data.b pos = 0 # 32-byte request ID request_id = bin.byteslice(pos, 32) pos += 32 # Status code status, consumed = BSV::Transaction::VarInt.decode(bin, pos) pos += consumed # Headers n_headers, consumed = BSV::Transaction::VarInt.decode(bin, pos) pos += consumed headers = [] n_headers.times do k_len, consumed = BSV::Transaction::VarInt.decode(bin, pos) pos += consumed k = bin.byteslice(pos, k_len).force_encoding('UTF-8') pos += k_len v_len, consumed = BSV::Transaction::VarInt.decode(bin, pos) pos += consumed v = bin.byteslice(pos, v_len).force_encoding('UTF-8') pos += v_len headers << [k, v] end # Body (optional) body, _pos = decode_optional_bytes(bin, pos) { request_id: request_id, status: status, headers: headers, body: body } end |
.serialize_request(request_nonce:, method:, path:, query:, headers:, body:) ⇒ String
Serialises an HTTP request into a binary payload for signing.
36 37 38 39 40 41 42 43 44 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 70 71 |
# File 'lib/bsv/auth/auth_payload.rb', line 36 def serialize_request(request_nonce:, method:, path:, query:, headers:, body:) buf = ''.b # 32-byte request nonce — written raw, no length prefix buf << request_nonce.b # Method — always present; varint(length) + UTF-8 bytes effective_method = method.nil? || method.empty? ? 'GET' : method method_bytes = effective_method.encode('UTF-8').b buf << BSV::Transaction::VarInt.encode(method_bytes.bytesize) buf << method_bytes # Path — optional buf << encode_optional_string(path) # Query — optional buf << encode_optional_string(query) # Headers — varint count + key/value pairs pairs = headers || [] buf << BSV::Transaction::VarInt.encode(pairs.length) pairs.each do |k, v| key_bytes = k.to_s.encode('UTF-8').b val_bytes = v.to_s.encode('UTF-8').b buf << BSV::Transaction::VarInt.encode(key_bytes.bytesize) buf << key_bytes buf << BSV::Transaction::VarInt.encode(val_bytes.bytesize) buf << val_bytes end # Body — apply defaults for body-carrying methods, then encode optionally effective_body = resolve_body(method: effective_method, headers: pairs, body: body) buf << encode_optional_bytes(effective_body) buf end |
.serialize_response(request_id:, status:, headers:, body:) ⇒ String
Serialises an HTTP response into a binary payload for signing.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/bsv/auth/auth_payload.rb', line 130 def serialize_response(request_id:, status:, headers:, body:) buf = ''.b # 32-byte request ID — written raw buf << request_id.b # Status code as varint buf << BSV::Transaction::VarInt.encode(status) # Headers pairs = headers || [] buf << BSV::Transaction::VarInt.encode(pairs.length) pairs.each do |k, v| key_bytes = k.to_s.encode('UTF-8').b val_bytes = v.to_s.encode('UTF-8').b buf << BSV::Transaction::VarInt.encode(key_bytes.bytesize) buf << key_bytes buf << BSV::Transaction::VarInt.encode(val_bytes.bytesize) buf << val_bytes end # Body — write length + bytes; ABSENT if nil buf << encode_optional_bytes(body) buf end |