bsv-sdk 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1993 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BSV
4
+ module Wallet
5
+ module Wire
6
+ # Serialises and deserialises all 28 BRC-100 wire protocol method calls.
7
+ #
8
+ # Frame layout:
9
+ # Request: [UInt8 call_code] [UInt8 originator_length] [UTF-8 originator] [params…]
10
+ # Response: [UInt8 error_code] [result…]
11
+ # Error: [UInt8 error_code ≠ 0] [VarInt msg_len] [UTF-8 message] [VarInt stack_len] [UTF-8 stack]
12
+ #
13
+ # For every method:
14
+ # serialize_request(method_name, args, originator:) → binary String
15
+ # deserialize_request(data) → [method_name, args, originator]
16
+ # serialize_response(method_name, result) → binary String (error_code=0 prefix)
17
+ # deserialize_response(method_name, data) → result Hash (raises on error_code≠0)
18
+ # serialize_error(code, message, stack_trace:) → binary String
19
+ module Serializer
20
+ # Maps method name symbols to their wire call codes (1-28).
21
+ CALL_CODES = {
22
+ create_action: 1,
23
+ sign_action: 2,
24
+ abort_action: 3,
25
+ list_actions: 4,
26
+ internalize_action: 5,
27
+ list_outputs: 6,
28
+ relinquish_output: 7,
29
+ get_public_key: 8,
30
+ reveal_counterparty_key_linkage: 9,
31
+ reveal_specific_key_linkage: 10,
32
+ encrypt: 11,
33
+ decrypt: 12,
34
+ create_hmac: 13,
35
+ verify_hmac: 14,
36
+ create_signature: 15,
37
+ verify_signature: 16,
38
+ acquire_certificate: 17,
39
+ list_certificates: 18,
40
+ prove_certificate: 19,
41
+ relinquish_certificate: 20,
42
+ discover_by_identity_key: 21,
43
+ discover_by_attributes: 22,
44
+ is_authenticated: 23,
45
+ wait_for_authentication: 24,
46
+ get_height: 25,
47
+ get_header_for_height: 26,
48
+ get_network: 27,
49
+ get_version: 28
50
+ }.freeze
51
+
52
+ # Inverse mapping: call code Integer → method name Symbol.
53
+ METHODS_BY_CODE = CALL_CODES.invert.freeze
54
+
55
+ module_function
56
+
57
+ # Serialises a method call request into a binary frame.
58
+ #
59
+ # @param method_name [Symbol]
60
+ # @param args [Hash]
61
+ # @param originator [String, nil] FQDN of the calling application
62
+ # @return [String] binary frame (ASCII-8BIT)
63
+ def serialize_request(method_name, args, originator: nil)
64
+ code = CALL_CODES.fetch(method_name) do
65
+ raise ArgumentError, "unknown method: #{method_name}"
66
+ end
67
+
68
+ w = Writer.new
69
+ w.write_byte(code)
70
+
71
+ orig = originator.to_s
72
+ orig_bytes = orig.encode('UTF-8').b
73
+ w.write_byte(orig_bytes.bytesize)
74
+ w.write_bytes(orig_bytes)
75
+
76
+ params_writer = Writer.new
77
+ send(:"write_#{method_name}_params", params_writer, args)
78
+ w.write_bytes(params_writer.to_binary)
79
+
80
+ w.to_binary
81
+ end
82
+
83
+ # Deserialises a binary request frame.
84
+ #
85
+ # @param data [String] binary frame
86
+ # @return [Array(Symbol, Hash, String)] [method_name, args, originator]
87
+ def deserialize_request(data)
88
+ r = Reader.new(data)
89
+ code = r.read_byte
90
+ method_name = METHODS_BY_CODE.fetch(code) do
91
+ raise ArgumentError, "unknown call code: #{code}"
92
+ end
93
+
94
+ orig_len = r.read_byte
95
+ originator = r.read_bytes(orig_len).force_encoding('UTF-8')
96
+
97
+ args = send(:"read_#{method_name}_params", r)
98
+ [method_name, args, originator]
99
+ end
100
+
101
+ # Serialises a successful response.
102
+ #
103
+ # @param method_name [Symbol]
104
+ # @param result [Hash]
105
+ # @return [String] binary frame
106
+ def serialize_response(method_name, result)
107
+ w = Writer.new
108
+ w.write_byte(0) # error_code = 0
109
+ result_writer = Writer.new
110
+ send(:"write_#{method_name}_result", result_writer, result)
111
+ w.write_bytes(result_writer.to_binary)
112
+ w.to_binary
113
+ end
114
+
115
+ # Serialises an error response.
116
+ #
117
+ # @param code [Integer] non-zero error code
118
+ # @param message [String]
119
+ # @param stack_trace [String, nil]
120
+ # @return [String] binary frame
121
+ def serialize_error(code, message, stack_trace: nil)
122
+ w = Writer.new
123
+ w.write_byte(code)
124
+ w.write_utf8_string(message.to_s)
125
+ w.write_utf8_string(stack_trace.to_s)
126
+ w.to_binary
127
+ end
128
+
129
+ # Deserialises a response frame, raising a WalletError on non-zero error code.
130
+ #
131
+ # @param method_name [Symbol]
132
+ # @param data [String] binary frame
133
+ # @return [Hash] result
134
+ def deserialize_response(method_name, data)
135
+ r = Reader.new(data)
136
+ error_code = r.read_byte
137
+
138
+ unless error_code.zero?
139
+ message = r.read_utf8_string
140
+ raise BSV::Wallet::WalletError.new(message, error_code)
141
+ end
142
+
143
+ send(:"read_#{method_name}_result", r)
144
+ end
145
+
146
+ # -----------------------------------------------------------------------
147
+ # Private helpers — all defined via module_function so they become
148
+ # private module-level methods callable from within the module.
149
+ # -----------------------------------------------------------------------
150
+
151
+ # --- write_outpoint / read_outpoint helpers ---
152
+
153
+ def write_outpoint_str(w, outpoint_str)
154
+ txid, index = outpoint_str.split('.')
155
+ w.write_outpoint(txid, index.to_i)
156
+ end
157
+
158
+ def read_outpoint_str(r)
159
+ txid, index = r.read_outpoint
160
+ "#{txid}.#{index}"
161
+ end
162
+
163
+ # --- key-related params (shared by encrypt/decrypt/hmac/signature/revealSpecific) ---
164
+
165
+ def write_key_related_params(w, args)
166
+ w.write_protocol_id(args[:protocol_id])
167
+ w.write_utf8_string(args[:key_id].to_s)
168
+ w.write_counterparty(args[:counterparty])
169
+ w.write_privileged(args[:privileged], args[:privileged_reason])
170
+ end
171
+
172
+ # Reads key-related params: protocolID, keyID, counterparty, privileged, privilegedReason.
173
+ def read_key_related_from_reader(r)
174
+ protocol_id = r.read_protocol_id
175
+ key_id = r.read_utf8_string
176
+ counterparty = r.read_counterparty
177
+ privileged, privileged_reason = r.read_privileged
178
+ {
179
+ protocol_id: protocol_id,
180
+ key_id: key_id,
181
+ counterparty: counterparty,
182
+ privileged: privileged,
183
+ privileged_reason: privileged_reason
184
+ }
185
+ end
186
+
187
+ # --- certificate binary encoding helpers ---
188
+
189
+ # Writes a certificate struct to the writer (without length prefix).
190
+ def write_certificate_fields(w, cert)
191
+ # type: 32 bytes (base64-decoded)
192
+ w.write_bytes(decode_base64_32(cert[:type]))
193
+ # subject: 33 bytes (hex-decoded compressed pubkey)
194
+ w.write_bytes([cert[:subject]].pack('H*'))
195
+ # serial_number: 32 bytes (base64-decoded)
196
+ w.write_bytes(decode_base64_32(cert[:serial_number]))
197
+ # certifier: 33 bytes
198
+ w.write_bytes([cert[:certifier]].pack('H*'))
199
+ # revocation_outpoint
200
+ write_outpoint_str(w, cert[:revocation_outpoint])
201
+ # signature: VarInt-prefixed hex
202
+ sig_bytes = [cert[:signature]].pack('H*')
203
+ w.write_varint(sig_bytes.bytesize)
204
+ w.write_bytes(sig_bytes)
205
+ # fields: map
206
+ fields = cert[:fields] || {}
207
+ w.write_varint(fields.length)
208
+ fields.each do |k, v|
209
+ w.write_utf8_string(k.to_s)
210
+ w.write_utf8_string(v.to_s)
211
+ end
212
+ end
213
+
214
+ # Reads a certificate struct from the reader (without length prefix).
215
+ def read_cert_from_reader(r)
216
+ require 'base64'
217
+ type = encode_base64(r.read_bytes(32))
218
+ subject = r.read_bytes(33).unpack1('H*')
219
+ serial_number = encode_base64(r.read_bytes(32))
220
+ certifier = r.read_bytes(33).unpack1('H*')
221
+ revocation_outpoint = read_outpoint_str(r)
222
+ sig_len = r.read_varint
223
+ signature = r.read_bytes(sig_len).unpack1('H*')
224
+ fields_count = r.read_varint
225
+ fields = {}
226
+ fields_count.times do
227
+ k = r.read_utf8_string
228
+ v = r.read_utf8_string
229
+ fields[k] = v
230
+ end
231
+ {
232
+ type: type,
233
+ subject: subject,
234
+ serial_number: serial_number,
235
+ certifier: certifier,
236
+ revocation_outpoint: revocation_outpoint,
237
+ signature: signature,
238
+ fields: fields
239
+ }
240
+ end
241
+
242
+ def decode_base64_32(b64_str)
243
+ require 'base64'
244
+ raw = Base64.strict_decode64(b64_str)
245
+ raise ArgumentError, "expected 32-byte base64 value, got #{raw.bytesize}" unless raw.bytesize == 32
246
+
247
+ raw
248
+ end
249
+
250
+ def encode_base64(raw_bytes)
251
+ require 'base64'
252
+ Base64.strict_encode64(raw_bytes)
253
+ end
254
+
255
+ # Serialises a discovery certificate result (shared by discoverByIdentityKey/Attributes).
256
+ def write_discovery_result(w, result)
257
+ certs = result[:certificates] || []
258
+ w.write_varint(result[:total_certificates] || certs.length)
259
+ certs.each do |cert|
260
+ # Write certificate binary (length-prefixed)
261
+ cert_w = Writer.new
262
+ write_certificate_fields(cert_w, cert)
263
+ cert_bin = cert_w.to_binary
264
+ w.write_varint(cert_bin.bytesize)
265
+ w.write_bytes(cert_bin)
266
+
267
+ # certifierInfo
268
+ info = cert[:certifier_info] || {}
269
+ w.write_utf8_string(info[:name].to_s)
270
+ w.write_utf8_string(info[:icon_url].to_s)
271
+ w.write_utf8_string(info[:description].to_s)
272
+ w.write_byte(info[:trust].to_i)
273
+
274
+ # publiclyRevealedKeyring: map of field→base64
275
+ keyring = cert[:publicly_revealed_keyring] || {}
276
+ w.write_varint(keyring.length)
277
+ keyring.each do |k, v|
278
+ w.write_utf8_string(k.to_s)
279
+ val_bytes = decode_base64_32_safe(v.to_s)
280
+ w.write_varint(val_bytes.bytesize)
281
+ w.write_bytes(val_bytes)
282
+ end
283
+
284
+ # decryptedFields: map of field→utf8
285
+ decrypted = cert[:decrypted_fields] || {}
286
+ w.write_varint(decrypted.length)
287
+ decrypted.each do |k, v|
288
+ w.write_utf8_string(k.to_s)
289
+ w.write_utf8_string(v.to_s)
290
+ end
291
+ end
292
+ end
293
+
294
+ def read_discovery_result(r)
295
+ total = r.read_varint
296
+ certs = total.times.map do
297
+ cert_len = r.read_varint
298
+ cert_r = Reader.new(r.read_bytes(cert_len))
299
+ cert = read_cert_from_reader(cert_r)
300
+
301
+ info_name = r.read_utf8_string
302
+ info_icon = r.read_utf8_string
303
+ info_desc = r.read_utf8_string
304
+ info_trust = r.read_byte
305
+
306
+ pub_count = r.read_varint
307
+ public_keyring = {}
308
+ pub_count.times do
309
+ k = r.read_utf8_string
310
+ v_len = r.read_varint
311
+ public_keyring[k] = encode_base64(r.read_bytes(v_len))
312
+ end
313
+
314
+ dec_count = r.read_varint
315
+ decrypted = {}
316
+ dec_count.times do
317
+ k = r.read_utf8_string
318
+ decrypted[k] = r.read_utf8_string
319
+ end
320
+
321
+ cert.merge(
322
+ certifier_info: {
323
+ name: info_name,
324
+ icon_url: info_icon,
325
+ description: info_desc,
326
+ trust: info_trust
327
+ },
328
+ publicly_revealed_keyring: public_keyring,
329
+ decrypted_fields: decrypted
330
+ )
331
+ end
332
+ { total_certificates: total, certificates: certs }
333
+ end
334
+
335
+ def decode_base64_32_safe(str)
336
+ require 'base64'
337
+ Base64.strict_decode64(str)
338
+ rescue StandardError
339
+ str.b
340
+ end
341
+
342
+ # -----------------------------------------------------------------------
343
+ # 1. createAction
344
+ # -----------------------------------------------------------------------
345
+
346
+ def write_create_action_params(w, args)
347
+ w.write_utf8_string(args[:description].to_s)
348
+ w.write_optional_byte_array(args[:input_beef]&.pack('C*'))
349
+
350
+ inputs = args[:inputs]
351
+ if inputs.nil?
352
+ w.write_signed_varint(-1)
353
+ else
354
+ w.write_varint(inputs.length)
355
+ inputs.each do |inp|
356
+ write_outpoint_str(w, inp[:outpoint])
357
+ if inp[:unlocking_script]
358
+ script_bytes = [inp[:unlocking_script]].pack('H*')
359
+ w.write_varint(script_bytes.bytesize)
360
+ w.write_bytes(script_bytes)
361
+ else
362
+ w.write_signed_varint(-1)
363
+ w.write_varint(inp[:unlocking_script_length] || 0)
364
+ end
365
+ w.write_utf8_string(inp[:input_description].to_s)
366
+ if inp[:sequence_number]
367
+ w.write_varint(inp[:sequence_number])
368
+ else
369
+ w.write_signed_varint(-1)
370
+ end
371
+ end
372
+ end
373
+
374
+ outputs = args[:outputs]
375
+ if outputs.nil?
376
+ w.write_signed_varint(-1)
377
+ else
378
+ w.write_varint(outputs.length)
379
+ outputs.each do |out|
380
+ script_bytes = [out[:locking_script].to_s].pack('H*')
381
+ w.write_varint(script_bytes.bytesize)
382
+ w.write_bytes(script_bytes)
383
+ w.write_varint(out[:satoshis].to_i)
384
+ w.write_utf8_string(out[:output_description].to_s)
385
+ w.write_optional_utf8_string(out[:basket])
386
+ w.write_optional_utf8_string(out[:custom_instructions])
387
+ w.write_string_array(out[:tags])
388
+ end
389
+ end
390
+
391
+ if args[:lock_time]
392
+ w.write_varint(args[:lock_time])
393
+ else
394
+ w.write_signed_varint(-1)
395
+ end
396
+
397
+ if args[:version]
398
+ w.write_varint(args[:version])
399
+ else
400
+ w.write_signed_varint(-1)
401
+ end
402
+
403
+ w.write_string_array(args[:labels])
404
+
405
+ opts = args[:options]
406
+ if opts.nil?
407
+ w.write_int8(0)
408
+ else
409
+ w.write_int8(1)
410
+ w.write_optional_bool(opts[:sign_and_process])
411
+ w.write_optional_bool(opts[:accept_delayed_broadcast])
412
+ if opts[:trust_self] == 'known'
413
+ w.write_int8(1)
414
+ elsif opts[:trust_self].nil?
415
+ w.write_int8(-1)
416
+ else
417
+ w.write_int8(0)
418
+ end
419
+ known_txids = opts[:known_txids]
420
+ if known_txids.nil?
421
+ w.write_signed_varint(-1)
422
+ else
423
+ w.write_varint(known_txids.length)
424
+ known_txids.each { |txid| w.write_bytes([txid].pack('H*')) }
425
+ end
426
+ w.write_optional_bool(opts[:return_txid_only])
427
+ w.write_optional_bool(opts[:no_send])
428
+ no_send_change = opts[:no_send_change]
429
+ if no_send_change.nil?
430
+ w.write_signed_varint(-1)
431
+ else
432
+ w.write_varint(no_send_change.length)
433
+ no_send_change.each { |op| write_outpoint_str(w, op) }
434
+ end
435
+ send_with = opts[:send_with]
436
+ if send_with.nil?
437
+ w.write_signed_varint(-1)
438
+ else
439
+ w.write_varint(send_with.length)
440
+ send_with.each { |txid| w.write_bytes([txid].pack('H*')) }
441
+ end
442
+ w.write_optional_bool(opts[:randomize_outputs])
443
+ end
444
+ end
445
+
446
+ def read_create_action_params(r)
447
+ args = {}
448
+ args[:description] = r.read_utf8_string
449
+
450
+ input_beef_raw = r.read_optional_byte_array
451
+ args[:input_beef] = input_beef_raw&.bytes
452
+
453
+ inputs_count = r.read_signed_varint
454
+ args[:inputs] = if inputs_count == -1
455
+ nil
456
+ else
457
+ inputs_count.times.map do
458
+ inp = {}
459
+ inp[:outpoint] = read_outpoint_str(r)
460
+ script_len = r.read_signed_varint
461
+ if script_len >= 0
462
+ inp[:unlocking_script] = r.read_bytes(script_len).unpack1('H*')
463
+ else
464
+ inp[:unlocking_script] = nil
465
+ inp[:unlocking_script_length] = r.read_varint
466
+ end
467
+ inp[:input_description] = r.read_utf8_string
468
+ seq = r.read_signed_varint
469
+ inp[:sequence_number] = seq == -1 ? nil : seq
470
+ inp
471
+ end
472
+ end
473
+
474
+ outputs_count = r.read_signed_varint
475
+ args[:outputs] = if outputs_count == -1
476
+ nil
477
+ else
478
+ outputs_count.times.map do
479
+ out = {}
480
+ ls_len = r.read_varint
481
+ out[:locking_script] = r.read_bytes(ls_len).unpack1('H*')
482
+ out[:satoshis] = r.read_varint
483
+ out[:output_description] = r.read_utf8_string
484
+ out[:basket] = r.read_optional_utf8_string
485
+ out[:custom_instructions] = r.read_optional_utf8_string
486
+ out[:tags] = r.read_string_array
487
+ out
488
+ end
489
+ end
490
+
491
+ lock_time = r.read_signed_varint
492
+ args[:lock_time] = lock_time == -1 ? nil : lock_time
493
+ version = r.read_signed_varint
494
+ args[:version] = version == -1 ? nil : version
495
+ args[:labels] = r.read_string_array
496
+
497
+ opts_flag = r.read_int8
498
+ if opts_flag == 1
499
+ opts = {}
500
+ opts[:sign_and_process] = r.read_optional_bool
501
+ opts[:accept_delayed_broadcast] = r.read_optional_bool
502
+ trust_self_flag = r.read_int8
503
+ opts[:trust_self] = if trust_self_flag == -1
504
+ nil
505
+ elsif trust_self_flag == 1
506
+ 'known'
507
+ end
508
+ known_count = r.read_signed_varint
509
+ opts[:known_txids] = if known_count == -1
510
+ nil
511
+ else
512
+ known_count.times.map { r.read_bytes(32).unpack1('H*') }
513
+ end
514
+ opts[:return_txid_only] = r.read_optional_bool
515
+ opts[:no_send] = r.read_optional_bool
516
+ no_send_change_count = r.read_signed_varint
517
+ opts[:no_send_change] = if no_send_change_count == -1
518
+ nil
519
+ else
520
+ no_send_change_count.times.map { read_outpoint_str(r) }
521
+ end
522
+ send_with_count = r.read_signed_varint
523
+ opts[:send_with] = if send_with_count == -1
524
+ nil
525
+ else
526
+ send_with_count.times.map { r.read_bytes(32).unpack1('H*') }
527
+ end
528
+ opts[:randomize_outputs] = r.read_optional_bool
529
+ args[:options] = opts
530
+ else
531
+ args[:options] = nil
532
+ end
533
+
534
+ args
535
+ end
536
+
537
+ def write_create_action_result(w, result)
538
+ txid = result[:txid]
539
+ if txid && !txid.empty?
540
+ w.write_int8(1)
541
+ w.write_bytes([txid].pack('H*'))
542
+ else
543
+ w.write_int8(0)
544
+ end
545
+
546
+ tx = result[:tx]
547
+ if tx
548
+ w.write_int8(1)
549
+ tx_bytes = tx.is_a?(Array) ? tx.pack('C*') : tx
550
+ w.write_varint(tx_bytes.bytesize)
551
+ w.write_bytes(tx_bytes)
552
+ else
553
+ w.write_int8(0)
554
+ end
555
+
556
+ no_send_change = result[:no_send_change]
557
+ if no_send_change
558
+ w.write_varint(no_send_change.length)
559
+ no_send_change.each { |op| write_outpoint_str(w, op) }
560
+ else
561
+ w.write_signed_varint(-1)
562
+ end
563
+
564
+ send_with_results = result[:send_with_results]
565
+ if send_with_results
566
+ w.write_varint(send_with_results.length)
567
+ send_with_results.each do |swr|
568
+ w.write_bytes([swr[:txid]].pack('H*'))
569
+ status_code = { 'unproven' => 1, 'sending' => 2, 'failed' => 3 }[swr[:status]] || 1
570
+ w.write_int8(status_code)
571
+ end
572
+ else
573
+ w.write_signed_varint(-1)
574
+ end
575
+
576
+ signable = result[:signable_transaction]
577
+ if signable
578
+ w.write_int8(1)
579
+ tx_bytes = signable[:tx].is_a?(Array) ? signable[:tx].pack('C*') : signable[:tx]
580
+ w.write_varint(tx_bytes.bytesize)
581
+ w.write_bytes(tx_bytes)
582
+ require 'base64'
583
+ ref_bytes = Base64.strict_decode64(signable[:reference])
584
+ w.write_varint(ref_bytes.bytesize)
585
+ w.write_bytes(ref_bytes)
586
+ else
587
+ w.write_int8(0)
588
+ end
589
+ end
590
+
591
+ def read_create_action_result(r)
592
+ result = {}
593
+ txid_flag = r.read_int8
594
+ result[:txid] = txid_flag == 1 ? r.read_bytes(32).unpack1('H*') : nil
595
+
596
+ tx_flag = r.read_int8
597
+ if tx_flag == 1
598
+ tx_len = r.read_varint
599
+ result[:tx] = r.read_bytes(tx_len).bytes
600
+ else
601
+ result[:tx] = nil
602
+ end
603
+
604
+ no_send_change_count = r.read_signed_varint
605
+ result[:no_send_change] = if no_send_change_count == -1
606
+ nil
607
+ else
608
+ no_send_change_count.times.map { read_outpoint_str(r) }
609
+ end
610
+
611
+ swr_count = r.read_signed_varint
612
+ result[:send_with_results] = if swr_count == -1
613
+ nil
614
+ else
615
+ swr_count.times.map do
616
+ txid = r.read_bytes(32).unpack1('H*')
617
+ status = { 1 => 'unproven', 2 => 'sending', 3 => 'failed' }[r.read_int8] || 'failed'
618
+ { txid: txid, status: status }
619
+ end
620
+ end
621
+
622
+ signable_flag = r.read_int8
623
+ if signable_flag == 1
624
+ tx_len = r.read_varint
625
+ tx = r.read_bytes(tx_len).bytes
626
+ ref_len = r.read_varint
627
+ ref = encode_base64(r.read_bytes(ref_len))
628
+ result[:signable_transaction] = { tx: tx, reference: ref }
629
+ else
630
+ result[:signable_transaction] = nil
631
+ end
632
+
633
+ result
634
+ end
635
+
636
+ # -----------------------------------------------------------------------
637
+ # 2. signAction
638
+ # -----------------------------------------------------------------------
639
+
640
+ def write_sign_action_params(w, args)
641
+ spends = args[:spends] || {}
642
+ w.write_varint(spends.length)
643
+ spends.each do |idx, spend|
644
+ w.write_varint(idx.to_i)
645
+ script_bytes = [spend[:unlocking_script].to_s].pack('H*')
646
+ w.write_varint(script_bytes.bytesize)
647
+ w.write_bytes(script_bytes)
648
+ if spend[:sequence_number]
649
+ w.write_varint(spend[:sequence_number])
650
+ else
651
+ w.write_signed_varint(-1)
652
+ end
653
+ end
654
+
655
+ require 'base64'
656
+ ref_bytes = Base64.strict_decode64(args[:reference].to_s)
657
+ w.write_varint(ref_bytes.bytesize)
658
+ w.write_bytes(ref_bytes)
659
+
660
+ opts = args[:options]
661
+ if opts.nil?
662
+ w.write_int8(0)
663
+ else
664
+ w.write_int8(1)
665
+ w.write_optional_bool(opts[:accept_delayed_broadcast])
666
+ w.write_optional_bool(opts[:return_txid_only])
667
+ w.write_optional_bool(opts[:no_send])
668
+ send_with = opts[:send_with]
669
+ if send_with.nil?
670
+ w.write_signed_varint(-1)
671
+ else
672
+ w.write_varint(send_with.length)
673
+ send_with.each { |txid| w.write_bytes([txid].pack('H*')) }
674
+ end
675
+ end
676
+ end
677
+
678
+ def read_sign_action_params(r)
679
+ spends = {}
680
+ spend_count = r.read_varint
681
+ spend_count.times do
682
+ idx = r.read_varint
683
+ us_len = r.read_varint
684
+ us = r.read_bytes(us_len).unpack1('H*')
685
+ seq = r.read_signed_varint
686
+ spends[idx] = { unlocking_script: us, sequence_number: seq == -1 ? nil : seq }
687
+ end
688
+
689
+ ref_len = r.read_varint
690
+ reference = encode_base64(r.read_bytes(ref_len))
691
+
692
+ opts_flag = r.read_int8
693
+ options = if opts_flag == 1
694
+ opts = {}
695
+ opts[:accept_delayed_broadcast] = r.read_optional_bool
696
+ opts[:return_txid_only] = r.read_optional_bool
697
+ opts[:no_send] = r.read_optional_bool
698
+ sw_count = r.read_signed_varint
699
+ opts[:send_with] = if sw_count == -1
700
+ nil
701
+ else
702
+ sw_count.times.map { r.read_bytes(32).unpack1('H*') }
703
+ end
704
+ opts
705
+ end
706
+
707
+ { spends: spends, reference: reference, options: options }
708
+ end
709
+
710
+ def write_sign_action_result(w, result)
711
+ txid = result[:txid]
712
+ if txid && !txid.empty?
713
+ w.write_int8(1)
714
+ w.write_bytes([txid].pack('H*'))
715
+ else
716
+ w.write_int8(0)
717
+ end
718
+
719
+ tx = result[:tx]
720
+ if tx
721
+ w.write_int8(1)
722
+ tx_bytes = tx.is_a?(Array) ? tx.pack('C*') : tx
723
+ w.write_varint(tx_bytes.bytesize)
724
+ w.write_bytes(tx_bytes)
725
+ else
726
+ w.write_int8(0)
727
+ end
728
+
729
+ swr = result[:send_with_results]
730
+ if swr
731
+ w.write_varint(swr.length)
732
+ swr.each do |item|
733
+ w.write_bytes([item[:txid]].pack('H*'))
734
+ status_code = { 'unproven' => 1, 'sending' => 2, 'failed' => 3 }[item[:status]] || 1
735
+ w.write_int8(status_code)
736
+ end
737
+ else
738
+ w.write_signed_varint(-1)
739
+ end
740
+ end
741
+
742
+ def read_sign_action_result(r)
743
+ result = {}
744
+ txid_flag = r.read_int8
745
+ result[:txid] = txid_flag == 1 ? r.read_bytes(32).unpack1('H*') : nil
746
+
747
+ tx_flag = r.read_int8
748
+ if tx_flag == 1
749
+ tx_len = r.read_varint
750
+ result[:tx] = r.read_bytes(tx_len).bytes
751
+ else
752
+ result[:tx] = nil
753
+ end
754
+
755
+ swr_count = r.read_signed_varint
756
+ result[:send_with_results] = if swr_count == -1
757
+ nil
758
+ else
759
+ swr_count.times.map do
760
+ txid = r.read_bytes(32).unpack1('H*')
761
+ status = { 1 => 'unproven', 2 => 'sending', 3 => 'failed' }[r.read_int8] || 'failed'
762
+ { txid: txid, status: status }
763
+ end
764
+ end
765
+
766
+ result
767
+ end
768
+
769
+ # -----------------------------------------------------------------------
770
+ # 3. abortAction — reference is the entire params payload (no length prefix)
771
+ # -----------------------------------------------------------------------
772
+
773
+ def write_abort_action_params(w, args)
774
+ require 'base64'
775
+ ref_bytes = Base64.strict_decode64(args[:reference].to_s)
776
+ w.write_bytes(ref_bytes)
777
+ end
778
+
779
+ def read_abort_action_params(r)
780
+ ref_bytes = r.read_remaining
781
+ { reference: encode_base64(ref_bytes) }
782
+ end
783
+
784
+ def write_abort_action_result(w, _result)
785
+ # No payload — response is just the zero error byte written by serialize_response
786
+ end
787
+
788
+ def read_abort_action_result(_r)
789
+ { aborted: true }
790
+ end
791
+
792
+ # -----------------------------------------------------------------------
793
+ # 4. listActions
794
+ # -----------------------------------------------------------------------
795
+
796
+ LIST_ACTIONS_INCLUDE_FLAGS = %i[
797
+ include_labels include_inputs include_input_source_locking_scripts
798
+ include_input_unlocking_scripts include_outputs include_output_locking_scripts
799
+ ].freeze
800
+
801
+ def write_list_actions_params(w, args)
802
+ labels = args[:labels] || []
803
+ w.write_varint(labels.length)
804
+ labels.each { |l| w.write_utf8_string(l) }
805
+
806
+ lqm = args[:label_query_mode]
807
+ if lqm.nil?
808
+ w.write_int8(-1)
809
+ elsif lqm == 'any'
810
+ w.write_int8(1)
811
+ else
812
+ w.write_int8(2)
813
+ end
814
+
815
+ LIST_ACTIONS_INCLUDE_FLAGS.each do |flag|
816
+ w.write_optional_bool(args[flag])
817
+ end
818
+
819
+ w.write_signed_varint(args[:limit].nil? ? -1 : args[:limit])
820
+ w.write_signed_varint(args[:offset].nil? ? -1 : args[:offset])
821
+ w.write_optional_bool(args[:seek_permission])
822
+ end
823
+
824
+ def read_list_actions_params(r)
825
+ args = {}
826
+ label_count = r.read_varint
827
+ args[:labels] = label_count.times.map { r.read_utf8_string }
828
+
829
+ lqm_flag = r.read_int8
830
+ args[:label_query_mode] = case lqm_flag
831
+ when 1 then 'any'
832
+ when 2 then 'all'
833
+ end
834
+
835
+ LIST_ACTIONS_INCLUDE_FLAGS.each do |flag|
836
+ args[flag] = r.read_optional_bool
837
+ end
838
+
839
+ limit = r.read_signed_varint
840
+ args[:limit] = limit == -1 ? nil : limit
841
+ offset = r.read_signed_varint
842
+ args[:offset] = offset == -1 ? nil : offset
843
+ args[:seek_permission] = r.read_optional_bool
844
+ args
845
+ end
846
+
847
+ def write_list_actions_result(w, result)
848
+ actions = result[:actions] || []
849
+ w.write_varint(result[:total_actions] || actions.length)
850
+ actions.each do |action|
851
+ w.write_bytes([action[:txid]].pack('H*'))
852
+ w.write_varint(action[:satoshis].to_i)
853
+ status_code = {
854
+ 'completed' => 1, 'unprocessed' => 2, 'sending' => 3,
855
+ 'unproven' => 4, 'unsigned' => 5, 'nosend' => 6,
856
+ 'nonfinal' => 7, 'failed' => 8
857
+ }[action[:status]] || -1
858
+ w.write_int8(status_code)
859
+ w.write_int8(action[:is_outgoing] ? 1 : 0)
860
+ w.write_utf8_string(action[:description].to_s)
861
+ w.write_string_array(action[:labels])
862
+ w.write_varint(action[:version].to_i)
863
+ w.write_varint(action[:lock_time].to_i)
864
+
865
+ inputs = action[:inputs]
866
+ if inputs.nil?
867
+ w.write_signed_varint(-1)
868
+ else
869
+ w.write_varint(inputs.length)
870
+ inputs.each do |inp|
871
+ write_outpoint_str(w, inp[:source_outpoint])
872
+ w.write_varint(inp[:source_satoshis].to_i)
873
+ w.write_optional_byte_array(inp[:source_locking_script] ? [inp[:source_locking_script]].pack('H*') : nil)
874
+ w.write_optional_byte_array(inp[:unlocking_script] ? [inp[:unlocking_script]].pack('H*') : nil)
875
+ w.write_utf8_string(inp[:input_description].to_s)
876
+ w.write_varint(inp[:sequence_number].to_i)
877
+ end
878
+ end
879
+
880
+ outputs = action[:outputs]
881
+ if outputs.nil?
882
+ w.write_signed_varint(-1)
883
+ else
884
+ w.write_varint(outputs.length)
885
+ outputs.each do |out|
886
+ w.write_varint(out[:output_index].to_i)
887
+ w.write_varint(out[:satoshis].to_i)
888
+ w.write_optional_byte_array(out[:locking_script] ? [out[:locking_script]].pack('H*') : nil)
889
+ w.write_int8(out[:spendable] ? 1 : 0)
890
+ w.write_utf8_string(out[:output_description].to_s)
891
+ w.write_optional_utf8_string(out[:basket])
892
+ w.write_string_array(out[:tags])
893
+ w.write_optional_utf8_string(out[:custom_instructions])
894
+ end
895
+ end
896
+ end
897
+ end
898
+
899
+ def read_list_actions_result(r)
900
+ total = r.read_varint
901
+ actions = total.times.map do
902
+ action = {}
903
+ action[:txid] = r.read_bytes(32).unpack1('H*')
904
+ action[:satoshis] = r.read_varint
905
+ action[:status] = {
906
+ 1 => 'completed', 2 => 'unprocessed', 3 => 'sending',
907
+ 4 => 'unproven', 5 => 'unsigned', 6 => 'nosend',
908
+ 7 => 'nonfinal', 8 => 'failed'
909
+ }[r.read_int8]
910
+ action[:is_outgoing] = r.read_int8 == 1
911
+ action[:description] = r.read_utf8_string
912
+ action[:labels] = r.read_string_array
913
+ action[:version] = r.read_varint
914
+ action[:lock_time] = r.read_varint
915
+
916
+ inputs_count = r.read_signed_varint
917
+ action[:inputs] = if inputs_count == -1
918
+ nil
919
+ else
920
+ inputs_count.times.map do
921
+ inp = {}
922
+ inp[:source_outpoint] = read_outpoint_str(r)
923
+ inp[:source_satoshis] = r.read_varint
924
+ sls = r.read_optional_byte_array
925
+ inp[:source_locking_script] = sls&.unpack1('H*')
926
+ us = r.read_optional_byte_array
927
+ inp[:unlocking_script] = us&.unpack1('H*')
928
+ inp[:input_description] = r.read_utf8_string
929
+ inp[:sequence_number] = r.read_varint
930
+ inp
931
+ end
932
+ end
933
+
934
+ outputs_count = r.read_signed_varint
935
+ action[:outputs] = if outputs_count == -1
936
+ nil
937
+ else
938
+ outputs_count.times.map do
939
+ out = {}
940
+ out[:output_index] = r.read_varint
941
+ out[:satoshis] = r.read_varint
942
+ ls = r.read_optional_byte_array
943
+ out[:locking_script] = ls&.unpack1('H*')
944
+ out[:spendable] = r.read_int8 == 1
945
+ out[:output_description] = r.read_utf8_string
946
+ out[:basket] = r.read_optional_utf8_string
947
+ out[:tags] = r.read_string_array
948
+ out[:custom_instructions] = r.read_optional_utf8_string
949
+ out
950
+ end
951
+ end
952
+
953
+ action
954
+ end
955
+
956
+ { total_actions: total, actions: actions }
957
+ end
958
+
959
+ # -----------------------------------------------------------------------
960
+ # 5. internalizeAction
961
+ # -----------------------------------------------------------------------
962
+
963
+ def write_internalize_action_params(w, args)
964
+ tx = args[:tx]
965
+ tx_bytes = tx.is_a?(Array) ? tx.pack('C*') : tx.to_s.b
966
+ w.write_varint(tx_bytes.bytesize)
967
+ w.write_bytes(tx_bytes)
968
+
969
+ outputs = args[:outputs] || []
970
+ w.write_varint(outputs.length)
971
+ outputs.each do |out|
972
+ w.write_varint(out[:output_index].to_i)
973
+ if out[:protocol] == 'wallet payment'
974
+ w.write_byte(1)
975
+ rem = out[:payment_remittance] || {}
976
+ w.write_bytes([rem[:sender_identity_key].to_s].pack('H*'))
977
+ require 'base64'
978
+ pref = Base64.strict_decode64(rem[:derivation_prefix].to_s)
979
+ w.write_varint(pref.bytesize)
980
+ w.write_bytes(pref)
981
+ suf = Base64.strict_decode64(rem[:derivation_suffix].to_s)
982
+ w.write_varint(suf.bytesize)
983
+ w.write_bytes(suf)
984
+ else
985
+ w.write_byte(2)
986
+ rem = out[:insertion_remittance] || {}
987
+ w.write_utf8_string(rem[:basket].to_s)
988
+ w.write_optional_utf8_string(rem[:custom_instructions])
989
+ tags = rem[:tags] || []
990
+ w.write_varint(tags.length)
991
+ tags.each { |t| w.write_utf8_string(t) }
992
+ end
993
+ end
994
+
995
+ w.write_string_array(args[:labels])
996
+ w.write_utf8_string(args[:description].to_s)
997
+ w.write_optional_bool(args[:seek_permission])
998
+ end
999
+
1000
+ def read_internalize_action_params(r)
1001
+ args = {}
1002
+ tx_len = r.read_varint
1003
+ args[:tx] = r.read_bytes(tx_len).bytes
1004
+
1005
+ outputs_count = r.read_varint
1006
+ args[:outputs] = outputs_count.times.map do
1007
+ out = {}
1008
+ out[:output_index] = r.read_varint
1009
+ proto_flag = r.read_byte
1010
+ if proto_flag == 1
1011
+ out[:protocol] = 'wallet payment'
1012
+ rem = {}
1013
+ rem[:sender_identity_key] = r.read_bytes(33).unpack1('H*')
1014
+ pref_len = r.read_varint
1015
+ rem[:derivation_prefix] = encode_base64(r.read_bytes(pref_len))
1016
+ suf_len = r.read_varint
1017
+ rem[:derivation_suffix] = encode_base64(r.read_bytes(suf_len))
1018
+ out[:payment_remittance] = rem
1019
+ else
1020
+ out[:protocol] = 'basket insertion'
1021
+ rem = {}
1022
+ rem[:basket] = r.read_utf8_string
1023
+ rem[:custom_instructions] = r.read_optional_utf8_string
1024
+ tags_count = r.read_varint
1025
+ rem[:tags] = tags_count.times.map { r.read_utf8_string }
1026
+ out[:insertion_remittance] = rem
1027
+ end
1028
+ out
1029
+ end
1030
+
1031
+ args[:labels] = r.read_string_array
1032
+ args[:description] = r.read_utf8_string
1033
+ args[:seek_permission] = r.read_optional_bool
1034
+ args
1035
+ end
1036
+
1037
+ def write_internalize_action_result(w, _result)
1038
+ # No payload
1039
+ end
1040
+
1041
+ def read_internalize_action_result(_r)
1042
+ { accepted: true }
1043
+ end
1044
+
1045
+ # -----------------------------------------------------------------------
1046
+ # 6. listOutputs
1047
+ # -----------------------------------------------------------------------
1048
+
1049
+ def write_list_outputs_params(w, args)
1050
+ w.write_utf8_string(args[:basket].to_s)
1051
+
1052
+ tags = args[:tags]
1053
+ if tags.nil? || tags.empty?
1054
+ w.write_varint(0)
1055
+ else
1056
+ w.write_varint(tags.length)
1057
+ tags.each { |t| w.write_utf8_string(t) }
1058
+ end
1059
+
1060
+ tqm = args[:tag_query_mode]
1061
+ if tqm == 'all'
1062
+ w.write_int8(1)
1063
+ elsif tqm == 'any'
1064
+ w.write_int8(2)
1065
+ else
1066
+ w.write_int8(-1)
1067
+ end
1068
+
1069
+ inc = args[:include]
1070
+ if inc == 'locking scripts'
1071
+ w.write_int8(1)
1072
+ elsif inc == 'entire transactions'
1073
+ w.write_int8(2)
1074
+ else
1075
+ w.write_int8(-1)
1076
+ end
1077
+
1078
+ w.write_optional_bool(args[:include_custom_instructions])
1079
+ w.write_optional_bool(args[:include_tags])
1080
+ w.write_optional_bool(args[:include_labels])
1081
+ w.write_signed_varint(args[:limit].nil? ? -1 : args[:limit])
1082
+ w.write_signed_varint(args[:offset].nil? ? -1 : args[:offset])
1083
+ w.write_optional_bool(args[:seek_permission])
1084
+ end
1085
+
1086
+ def read_list_outputs_params(r)
1087
+ args = {}
1088
+ args[:basket] = r.read_utf8_string
1089
+
1090
+ tags_count = r.read_varint
1091
+ args[:tags] = tags_count.zero? ? nil : tags_count.times.map { r.read_utf8_string }
1092
+
1093
+ tqm_flag = r.read_int8
1094
+ args[:tag_query_mode] = case tqm_flag
1095
+ when 1 then 'all'
1096
+ when 2 then 'any'
1097
+ end
1098
+
1099
+ inc_flag = r.read_int8
1100
+ args[:include] = case inc_flag
1101
+ when 1 then 'locking scripts'
1102
+ when 2 then 'entire transactions'
1103
+ end
1104
+
1105
+ args[:include_custom_instructions] = r.read_optional_bool
1106
+ args[:include_tags] = r.read_optional_bool
1107
+ args[:include_labels] = r.read_optional_bool
1108
+
1109
+ limit = r.read_signed_varint
1110
+ args[:limit] = limit == -1 ? nil : limit
1111
+ offset = r.read_signed_varint
1112
+ args[:offset] = offset == -1 ? nil : offset
1113
+ args[:seek_permission] = r.read_optional_bool
1114
+ args
1115
+ end
1116
+
1117
+ def write_list_outputs_result(w, result)
1118
+ outputs = result[:outputs] || []
1119
+ w.write_varint(result[:total_outputs] || outputs.length)
1120
+
1121
+ beef = result[:beef]
1122
+ if beef
1123
+ beef_bytes = beef.is_a?(Array) ? beef.pack('C*') : beef
1124
+ w.write_varint(beef_bytes.bytesize)
1125
+ w.write_bytes(beef_bytes)
1126
+ else
1127
+ w.write_signed_varint(-1)
1128
+ end
1129
+
1130
+ outputs.each do |out|
1131
+ write_outpoint_str(w, out[:outpoint])
1132
+ w.write_varint(out[:satoshis].to_i)
1133
+ w.write_optional_byte_array(out[:locking_script] ? [out[:locking_script]].pack('H*') : nil)
1134
+ w.write_optional_utf8_string(out[:custom_instructions])
1135
+ w.write_string_array(out[:tags])
1136
+ w.write_string_array(out[:labels])
1137
+ end
1138
+ end
1139
+
1140
+ def read_list_outputs_result(r)
1141
+ total = r.read_varint
1142
+ beef_raw = r.read_optional_byte_array
1143
+ beef = beef_raw&.bytes
1144
+
1145
+ outputs = total.times.map do
1146
+ out = {}
1147
+ out[:outpoint] = read_outpoint_str(r)
1148
+ out[:satoshis] = r.read_varint
1149
+ ls = r.read_optional_byte_array
1150
+ out[:locking_script] = ls&.unpack1('H*')
1151
+ out[:custom_instructions] = r.read_optional_utf8_string
1152
+ out[:tags] = r.read_string_array
1153
+ out[:labels] = r.read_string_array
1154
+ out
1155
+ end
1156
+
1157
+ { total_outputs: total, beef: beef, outputs: outputs }
1158
+ end
1159
+
1160
+ # -----------------------------------------------------------------------
1161
+ # 7. relinquishOutput
1162
+ # -----------------------------------------------------------------------
1163
+
1164
+ def write_relinquish_output_params(w, args)
1165
+ w.write_utf8_string(args[:basket].to_s)
1166
+ write_outpoint_str(w, args[:output].to_s)
1167
+ end
1168
+
1169
+ def read_relinquish_output_params(r)
1170
+ { basket: r.read_utf8_string, output: read_outpoint_str(r) }
1171
+ end
1172
+
1173
+ def write_relinquish_output_result(w, _result); end
1174
+
1175
+ def read_relinquish_output_result(_r)
1176
+ { relinquished: true }
1177
+ end
1178
+
1179
+ # -----------------------------------------------------------------------
1180
+ # 8. getPublicKey
1181
+ # -----------------------------------------------------------------------
1182
+
1183
+ def write_get_public_key_params(w, args)
1184
+ identity = args[:identity_key] ? 1 : 0
1185
+ w.write_byte(identity)
1186
+
1187
+ if identity == 1
1188
+ w.write_privileged(args[:privileged], args[:privileged_reason])
1189
+ else
1190
+ w.write_protocol_id(args[:protocol_id])
1191
+ w.write_utf8_string(args[:key_id].to_s)
1192
+ w.write_counterparty(args[:counterparty])
1193
+ w.write_privileged(args[:privileged], args[:privileged_reason])
1194
+ w.write_optional_bool(args[:for_self])
1195
+ end
1196
+
1197
+ w.write_optional_bool(args[:seek_permission])
1198
+ end
1199
+
1200
+ def read_get_public_key_params(r)
1201
+ args = {}
1202
+ identity_flag = r.read_byte
1203
+ args[:identity_key] = identity_flag == 1
1204
+
1205
+ if args[:identity_key]
1206
+ args[:privileged], args[:privileged_reason] = r.read_privileged
1207
+ else
1208
+ args[:protocol_id] = r.read_protocol_id
1209
+ args[:key_id] = r.read_utf8_string
1210
+ args[:counterparty] = r.read_counterparty
1211
+ args[:privileged], args[:privileged_reason] = r.read_privileged
1212
+ args[:for_self] = r.read_optional_bool
1213
+ end
1214
+
1215
+ args[:seek_permission] = r.read_optional_bool
1216
+ args
1217
+ end
1218
+
1219
+ def write_get_public_key_result(w, result)
1220
+ w.write_bytes([result[:public_key].to_s].pack('H*'))
1221
+ end
1222
+
1223
+ def read_get_public_key_result(r)
1224
+ { public_key: r.read_remaining.unpack1('H*') }
1225
+ end
1226
+
1227
+ # -----------------------------------------------------------------------
1228
+ # 9. revealCounterpartyKeyLinkage
1229
+ # -----------------------------------------------------------------------
1230
+
1231
+ def write_reveal_counterparty_key_linkage_params(w, args)
1232
+ w.write_privileged(args[:privileged], args[:privileged_reason])
1233
+ w.write_bytes([args[:counterparty].to_s].pack('H*'))
1234
+ w.write_bytes([args[:verifier].to_s].pack('H*'))
1235
+ end
1236
+
1237
+ def read_reveal_counterparty_key_linkage_params(r)
1238
+ privileged, privileged_reason = r.read_privileged
1239
+ counterparty = r.read_bytes(33).unpack1('H*')
1240
+ verifier = r.read_bytes(33).unpack1('H*')
1241
+ { privileged: privileged, privileged_reason: privileged_reason,
1242
+ counterparty: counterparty, verifier: verifier }
1243
+ end
1244
+
1245
+ def write_reveal_counterparty_key_linkage_result(w, result)
1246
+ w.write_bytes([result[:prover].to_s].pack('H*'))
1247
+ w.write_bytes([result[:verifier].to_s].pack('H*'))
1248
+ w.write_bytes([result[:counterparty].to_s].pack('H*'))
1249
+ w.write_utf8_string(result[:revelation_time].to_s)
1250
+ enc_link = result[:encrypted_linkage]
1251
+ enc_link = enc_link.is_a?(Array) ? enc_link.pack('C*') : enc_link.to_s.b
1252
+ w.write_varint(enc_link.bytesize)
1253
+ w.write_bytes(enc_link)
1254
+ enc_proof = result[:encrypted_linkage_proof]
1255
+ enc_proof = enc_proof.is_a?(Array) ? enc_proof.pack('C*') : enc_proof.to_s.b
1256
+ w.write_varint(enc_proof.bytesize)
1257
+ w.write_bytes(enc_proof)
1258
+ end
1259
+
1260
+ def read_reveal_counterparty_key_linkage_result(r)
1261
+ prover = r.read_bytes(33).unpack1('H*')
1262
+ verifier = r.read_bytes(33).unpack1('H*')
1263
+ counterparty = r.read_bytes(33).unpack1('H*')
1264
+ revelation_time = r.read_utf8_string
1265
+ el_len = r.read_varint
1266
+ encrypted_linkage = r.read_bytes(el_len).bytes
1267
+ ep_len = r.read_varint
1268
+ encrypted_linkage_proof = r.read_bytes(ep_len).bytes
1269
+ { prover: prover, verifier: verifier, counterparty: counterparty,
1270
+ revelation_time: revelation_time, encrypted_linkage: encrypted_linkage,
1271
+ encrypted_linkage_proof: encrypted_linkage_proof }
1272
+ end
1273
+
1274
+ # -----------------------------------------------------------------------
1275
+ # 10. revealSpecificKeyLinkage
1276
+ # -----------------------------------------------------------------------
1277
+
1278
+ def write_reveal_specific_key_linkage_params(w, args)
1279
+ write_key_related_params(w, args)
1280
+ w.write_bytes([args[:verifier].to_s].pack('H*'))
1281
+ end
1282
+
1283
+ def read_reveal_specific_key_linkage_params(r)
1284
+ args = read_key_related_from_reader(r)
1285
+ args[:verifier] = r.read_bytes(33).unpack1('H*')
1286
+ args
1287
+ end
1288
+
1289
+ def write_reveal_specific_key_linkage_result(w, result)
1290
+ w.write_bytes([result[:prover].to_s].pack('H*'))
1291
+ w.write_bytes([result[:verifier].to_s].pack('H*'))
1292
+ w.write_bytes([result[:counterparty].to_s].pack('H*'))
1293
+ w.write_byte(result[:protocol_id][0].to_i)
1294
+ w.write_utf8_string(result[:protocol_id][1].to_s)
1295
+ w.write_utf8_string(result[:key_id].to_s)
1296
+ el = result[:encrypted_linkage]
1297
+ el = el.is_a?(Array) ? el.pack('C*') : el.to_s.b
1298
+ w.write_varint(el.bytesize)
1299
+ w.write_bytes(el)
1300
+ ep = result[:encrypted_linkage_proof]
1301
+ ep = ep.is_a?(Array) ? ep.pack('C*') : ep.to_s.b
1302
+ w.write_varint(ep.bytesize)
1303
+ w.write_bytes(ep)
1304
+ w.write_byte(result[:proof_type].to_i)
1305
+ end
1306
+
1307
+ def read_reveal_specific_key_linkage_result(r)
1308
+ prover = r.read_bytes(33).unpack1('H*')
1309
+ verifier = r.read_bytes(33).unpack1('H*')
1310
+ counterparty = r.read_bytes(33).unpack1('H*')
1311
+ level = r.read_byte
1312
+ protocol = r.read_utf8_string
1313
+ key_id = r.read_utf8_string
1314
+ el_len = r.read_varint
1315
+ encrypted_linkage = r.read_bytes(el_len).bytes
1316
+ ep_len = r.read_varint
1317
+ encrypted_linkage_proof = r.read_bytes(ep_len).bytes
1318
+ proof_type = r.read_byte
1319
+ { prover: prover, verifier: verifier, counterparty: counterparty,
1320
+ protocol_id: [level, protocol], key_id: key_id,
1321
+ encrypted_linkage: encrypted_linkage,
1322
+ encrypted_linkage_proof: encrypted_linkage_proof,
1323
+ proof_type: proof_type }
1324
+ end
1325
+
1326
+ # -----------------------------------------------------------------------
1327
+ # 11. encrypt
1328
+ # -----------------------------------------------------------------------
1329
+
1330
+ def write_encrypt_params(w, args)
1331
+ write_key_related_params(w, args)
1332
+ pt = args[:plaintext]
1333
+ pt_bytes = pt.is_a?(Array) ? pt.pack('C*') : pt.to_s.b
1334
+ w.write_varint(pt_bytes.bytesize)
1335
+ w.write_bytes(pt_bytes)
1336
+ w.write_optional_bool(args[:seek_permission])
1337
+ end
1338
+
1339
+ def read_encrypt_params(r)
1340
+ args = read_key_related_from_reader(r)
1341
+ pt_len = r.read_varint
1342
+ args[:plaintext] = r.read_bytes(pt_len).bytes
1343
+ args[:seek_permission] = r.read_optional_bool
1344
+ args
1345
+ end
1346
+
1347
+ def write_encrypt_result(w, result)
1348
+ ct = result[:ciphertext]
1349
+ ct_bytes = ct.is_a?(Array) ? ct.pack('C*') : ct.to_s.b
1350
+ w.write_bytes(ct_bytes)
1351
+ end
1352
+
1353
+ def read_encrypt_result(r)
1354
+ { ciphertext: r.read_remaining.bytes }
1355
+ end
1356
+
1357
+ # -----------------------------------------------------------------------
1358
+ # 12. decrypt
1359
+ # -----------------------------------------------------------------------
1360
+
1361
+ def write_decrypt_params(w, args)
1362
+ write_key_related_params(w, args)
1363
+ ct = args[:ciphertext]
1364
+ ct_bytes = ct.is_a?(Array) ? ct.pack('C*') : ct.to_s.b
1365
+ w.write_varint(ct_bytes.bytesize)
1366
+ w.write_bytes(ct_bytes)
1367
+ w.write_optional_bool(args[:seek_permission])
1368
+ end
1369
+
1370
+ def read_decrypt_params(r)
1371
+ args = read_key_related_from_reader(r)
1372
+ ct_len = r.read_varint
1373
+ args[:ciphertext] = r.read_bytes(ct_len).bytes
1374
+ args[:seek_permission] = r.read_optional_bool
1375
+ args
1376
+ end
1377
+
1378
+ def write_decrypt_result(w, result)
1379
+ pt = result[:plaintext]
1380
+ pt_bytes = pt.is_a?(Array) ? pt.pack('C*') : pt.to_s.b
1381
+ w.write_bytes(pt_bytes)
1382
+ end
1383
+
1384
+ def read_decrypt_result(r)
1385
+ { plaintext: r.read_remaining.bytes }
1386
+ end
1387
+
1388
+ # -----------------------------------------------------------------------
1389
+ # 13. createHmac
1390
+ # -----------------------------------------------------------------------
1391
+
1392
+ def write_create_hmac_params(w, args)
1393
+ write_key_related_params(w, args)
1394
+ data = args[:data]
1395
+ data_bytes = data.is_a?(Array) ? data.pack('C*') : data.to_s.b
1396
+ w.write_varint(data_bytes.bytesize)
1397
+ w.write_bytes(data_bytes)
1398
+ w.write_optional_bool(args[:seek_permission])
1399
+ end
1400
+
1401
+ def read_create_hmac_params(r)
1402
+ args = read_key_related_from_reader(r)
1403
+ data_len = r.read_varint
1404
+ args[:data] = r.read_bytes(data_len).bytes
1405
+ args[:seek_permission] = r.read_optional_bool
1406
+ args
1407
+ end
1408
+
1409
+ def write_create_hmac_result(w, result)
1410
+ hmac = result[:hmac]
1411
+ hmac_bytes = hmac.is_a?(Array) ? hmac.pack('C*') : hmac.to_s.b
1412
+ w.write_bytes(hmac_bytes)
1413
+ end
1414
+
1415
+ def read_create_hmac_result(r)
1416
+ { hmac: r.read_remaining.bytes }
1417
+ end
1418
+
1419
+ # -----------------------------------------------------------------------
1420
+ # 14. verifyHmac
1421
+ # -----------------------------------------------------------------------
1422
+
1423
+ def write_verify_hmac_params(w, args)
1424
+ write_key_related_params(w, args)
1425
+ hmac = args[:hmac]
1426
+ hmac_bytes = hmac.is_a?(Array) ? hmac.pack('C*') : hmac.to_s.b
1427
+ w.write_bytes(hmac_bytes) # fixed 32 bytes, no length prefix
1428
+ data = args[:data]
1429
+ data_bytes = data.is_a?(Array) ? data.pack('C*') : data.to_s.b
1430
+ w.write_varint(data_bytes.bytesize)
1431
+ w.write_bytes(data_bytes)
1432
+ w.write_optional_bool(args[:seek_permission])
1433
+ end
1434
+
1435
+ def read_verify_hmac_params(r)
1436
+ args = read_key_related_from_reader(r)
1437
+ args[:hmac] = r.read_bytes(32).bytes
1438
+ data_len = r.read_varint
1439
+ args[:data] = r.read_bytes(data_len).bytes
1440
+ args[:seek_permission] = r.read_optional_bool
1441
+ args
1442
+ end
1443
+
1444
+ def write_verify_hmac_result(w, _result); end
1445
+
1446
+ def read_verify_hmac_result(_r)
1447
+ { valid: true }
1448
+ end
1449
+
1450
+ # -----------------------------------------------------------------------
1451
+ # 15. createSignature
1452
+ # -----------------------------------------------------------------------
1453
+
1454
+ def write_create_signature_params(w, args)
1455
+ write_key_related_params(w, args)
1456
+ if args[:data]
1457
+ w.write_byte(1)
1458
+ data = args[:data]
1459
+ data_bytes = data.is_a?(Array) ? data.pack('C*') : data.to_s.b
1460
+ w.write_varint(data_bytes.bytesize)
1461
+ w.write_bytes(data_bytes)
1462
+ elsif args[:hash_to_directly_sign]
1463
+ w.write_byte(2)
1464
+ hash = args[:hash_to_directly_sign]
1465
+ hash_bytes = hash.is_a?(Array) ? hash.pack('C*') : hash.to_s.b
1466
+ w.write_bytes(hash_bytes)
1467
+ else
1468
+ w.write_byte(0)
1469
+ end
1470
+ w.write_optional_bool(args[:seek_permission])
1471
+ end
1472
+
1473
+ def read_create_signature_params(r)
1474
+ args = read_key_related_from_reader(r)
1475
+ data_type = r.read_byte
1476
+ if data_type == 1
1477
+ data_len = r.read_varint
1478
+ args[:data] = r.read_bytes(data_len).bytes
1479
+ elsif data_type == 2
1480
+ args[:hash_to_directly_sign] = r.read_bytes(32).bytes
1481
+ end
1482
+ args[:seek_permission] = r.read_optional_bool
1483
+ args
1484
+ end
1485
+
1486
+ def write_create_signature_result(w, result)
1487
+ sig = result[:signature]
1488
+ sig_bytes = sig.is_a?(Array) ? sig.pack('C*') : sig.to_s.b
1489
+ w.write_bytes(sig_bytes)
1490
+ end
1491
+
1492
+ def read_create_signature_result(r)
1493
+ { signature: r.read_remaining.bytes }
1494
+ end
1495
+
1496
+ # -----------------------------------------------------------------------
1497
+ # 16. verifySignature
1498
+ # -----------------------------------------------------------------------
1499
+
1500
+ def write_verify_signature_params(w, args)
1501
+ write_key_related_params(w, args)
1502
+ w.write_optional_bool(args[:for_self])
1503
+ sig = args[:signature]
1504
+ sig_bytes = sig.is_a?(Array) ? sig.pack('C*') : sig.to_s.b
1505
+ w.write_varint(sig_bytes.bytesize)
1506
+ w.write_bytes(sig_bytes)
1507
+ if args[:data]
1508
+ w.write_byte(1)
1509
+ data = args[:data]
1510
+ data_bytes = data.is_a?(Array) ? data.pack('C*') : data.to_s.b
1511
+ w.write_varint(data_bytes.bytesize)
1512
+ w.write_bytes(data_bytes)
1513
+ elsif args[:hash_to_directly_verify]
1514
+ w.write_byte(2)
1515
+ hash = args[:hash_to_directly_verify]
1516
+ hash_bytes = hash.is_a?(Array) ? hash.pack('C*') : hash.to_s.b
1517
+ w.write_bytes(hash_bytes)
1518
+ else
1519
+ w.write_byte(0)
1520
+ end
1521
+ w.write_optional_bool(args[:seek_permission])
1522
+ end
1523
+
1524
+ def read_verify_signature_params(r)
1525
+ args = read_key_related_from_reader(r)
1526
+ args[:for_self] = r.read_optional_bool
1527
+ sig_len = r.read_varint
1528
+ args[:signature] = r.read_bytes(sig_len).bytes
1529
+ data_type = r.read_byte
1530
+ if data_type == 1
1531
+ data_len = r.read_varint
1532
+ args[:data] = r.read_bytes(data_len).bytes
1533
+ elsif data_type == 2
1534
+ args[:hash_to_directly_verify] = r.read_bytes(32).bytes
1535
+ end
1536
+ args[:seek_permission] = r.read_optional_bool
1537
+ args
1538
+ end
1539
+
1540
+ def write_verify_signature_result(w, _result); end
1541
+
1542
+ def read_verify_signature_result(_r)
1543
+ { valid: true }
1544
+ end
1545
+
1546
+ # -----------------------------------------------------------------------
1547
+ # 17. acquireCertificate
1548
+ # -----------------------------------------------------------------------
1549
+
1550
+ def write_acquire_certificate_params(w, args)
1551
+ w.write_bytes(decode_base64_32(args[:type].to_s))
1552
+ w.write_bytes([args[:certifier].to_s].pack('H*'))
1553
+
1554
+ fields = args[:fields] || {}
1555
+ w.write_varint(fields.length)
1556
+ fields.each do |k, v|
1557
+ w.write_utf8_string(k.to_s)
1558
+ w.write_utf8_string(v.to_s)
1559
+ end
1560
+
1561
+ w.write_privileged(args[:privileged], args[:privileged_reason])
1562
+
1563
+ if args[:acquisition_protocol] == 'direct'
1564
+ w.write_byte(1)
1565
+ w.write_bytes(decode_base64_32(args[:serial_number].to_s))
1566
+ write_outpoint_str(w, args[:revocation_outpoint].to_s)
1567
+ sig_bytes = [args[:signature].to_s].pack('H*')
1568
+ w.write_varint(sig_bytes.bytesize)
1569
+ w.write_bytes(sig_bytes)
1570
+
1571
+ keyring_revealer = args[:keyring_revealer]
1572
+ if keyring_revealer == 'certifier'
1573
+ w.write_byte(11)
1574
+ else
1575
+ kr_bytes = [keyring_revealer.to_s].pack('H*')
1576
+ w.write_bytes(kr_bytes)
1577
+ end
1578
+
1579
+ keyring = args[:keyring_for_subject] || {}
1580
+ w.write_varint(keyring.length)
1581
+ keyring.each do |k, v|
1582
+ w.write_utf8_string(k.to_s)
1583
+ v_bytes = decode_base64_32_safe(v.to_s)
1584
+ w.write_varint(v_bytes.bytesize)
1585
+ w.write_bytes(v_bytes)
1586
+ end
1587
+ else
1588
+ w.write_byte(2)
1589
+ w.write_utf8_string(args[:certifier_url].to_s)
1590
+ end
1591
+ end
1592
+
1593
+ def read_acquire_certificate_params(r)
1594
+ args = {}
1595
+ args[:type] = encode_base64(r.read_bytes(32))
1596
+ args[:certifier] = r.read_bytes(33).unpack1('H*')
1597
+
1598
+ fields_count = r.read_varint
1599
+ args[:fields] = {}
1600
+ fields_count.times do
1601
+ k = r.read_utf8_string
1602
+ args[:fields][k] = r.read_utf8_string
1603
+ end
1604
+
1605
+ args[:privileged], args[:privileged_reason] = r.read_privileged
1606
+
1607
+ protocol_flag = r.read_byte
1608
+ if protocol_flag == 1
1609
+ args[:acquisition_protocol] = 'direct'
1610
+ args[:serial_number] = encode_base64(r.read_bytes(32))
1611
+ args[:revocation_outpoint] = read_outpoint_str(r)
1612
+ sig_len = r.read_varint
1613
+ args[:signature] = r.read_bytes(sig_len).unpack1('H*')
1614
+ kr_flag = r.read_byte
1615
+ if kr_flag == 11
1616
+ args[:keyring_revealer] = 'certifier'
1617
+ else
1618
+ remaining = r.read_bytes(32)
1619
+ args[:keyring_revealer] = ([kr_flag].pack('C') + remaining).unpack1('H*')
1620
+ end
1621
+ kr_count = r.read_varint
1622
+ args[:keyring_for_subject] = {}
1623
+ kr_count.times do
1624
+ k = r.read_utf8_string
1625
+ v_len = r.read_varint
1626
+ args[:keyring_for_subject][k] = encode_base64(r.read_bytes(v_len))
1627
+ end
1628
+ else
1629
+ args[:acquisition_protocol] = 'issuance'
1630
+ args[:certifier_url] = r.read_utf8_string
1631
+ end
1632
+
1633
+ args
1634
+ end
1635
+
1636
+ def write_acquire_certificate_result(w, result)
1637
+ write_certificate_fields(w, result)
1638
+ end
1639
+
1640
+ def read_acquire_certificate_result(r)
1641
+ read_cert_from_reader(r)
1642
+ end
1643
+
1644
+ # -----------------------------------------------------------------------
1645
+ # 18. listCertificates
1646
+ # -----------------------------------------------------------------------
1647
+
1648
+ def write_list_certificates_params(w, args)
1649
+ certifiers = args[:certifiers] || []
1650
+ w.write_varint(certifiers.length)
1651
+ certifiers.each { |c| w.write_bytes([c].pack('H*')) }
1652
+
1653
+ types = args[:types] || []
1654
+ w.write_varint(types.length)
1655
+ types.each { |t| w.write_bytes(decode_base64_32(t)) }
1656
+
1657
+ w.write_signed_varint(args[:limit].nil? ? -1 : args[:limit])
1658
+ w.write_signed_varint(args[:offset].nil? ? -1 : args[:offset])
1659
+ w.write_privileged(args[:privileged], args[:privileged_reason])
1660
+ end
1661
+
1662
+ def read_list_certificates_params(r)
1663
+ args = {}
1664
+ cert_count = r.read_varint
1665
+ args[:certifiers] = cert_count.times.map { r.read_bytes(33).unpack1('H*') }
1666
+
1667
+ types_count = r.read_varint
1668
+ args[:types] = types_count.times.map { encode_base64(r.read_bytes(32)) }
1669
+
1670
+ limit = r.read_signed_varint
1671
+ args[:limit] = limit == -1 ? nil : limit
1672
+ offset = r.read_signed_varint
1673
+ args[:offset] = offset == -1 ? nil : offset
1674
+ args[:privileged], args[:privileged_reason] = r.read_privileged
1675
+ args
1676
+ end
1677
+
1678
+ def write_list_certificates_result(w, result)
1679
+ certs = result[:certificates] || []
1680
+ w.write_varint(result[:total_certificates] || certs.length)
1681
+ certs.each do |cert|
1682
+ cert_w = Writer.new
1683
+ write_certificate_fields(cert_w, cert)
1684
+ cert_bin = cert_w.to_binary
1685
+ w.write_varint(cert_bin.bytesize)
1686
+ w.write_bytes(cert_bin)
1687
+
1688
+ keyring = cert[:keyring]
1689
+ if keyring && !keyring.empty?
1690
+ w.write_int8(1)
1691
+ w.write_varint(keyring.length)
1692
+ keyring.each do |k, v|
1693
+ w.write_utf8_string(k.to_s)
1694
+ v_bytes = decode_base64_32_safe(v.to_s)
1695
+ w.write_varint(v_bytes.bytesize)
1696
+ w.write_bytes(v_bytes)
1697
+ end
1698
+ else
1699
+ w.write_int8(0)
1700
+ end
1701
+
1702
+ verifier_hex = cert[:verifier].to_s
1703
+ verifier_bytes = verifier_hex.empty? ? ''.b : [verifier_hex].pack('H*')
1704
+ w.write_varint(verifier_bytes.bytesize)
1705
+ w.write_bytes(verifier_bytes)
1706
+ end
1707
+ end
1708
+
1709
+ def read_list_certificates_result(r)
1710
+ total = r.read_varint
1711
+ certs = total.times.map do
1712
+ cert_len = r.read_varint
1713
+ cert_r = Reader.new(r.read_bytes(cert_len))
1714
+ cert = read_cert_from_reader(cert_r)
1715
+
1716
+ keyring_flag = r.read_int8
1717
+ if keyring_flag == 1
1718
+ kr_count = r.read_varint
1719
+ keyring = {}
1720
+ kr_count.times do
1721
+ k = r.read_utf8_string
1722
+ v_len = r.read_varint
1723
+ keyring[k] = encode_base64(r.read_bytes(v_len))
1724
+ end
1725
+ cert[:keyring] = keyring
1726
+ else
1727
+ cert[:keyring] = {}
1728
+ end
1729
+
1730
+ ver_len = r.read_varint
1731
+ cert[:verifier] = ver_len.zero? ? nil : r.read_bytes(ver_len).unpack1('H*')
1732
+ cert
1733
+ end
1734
+ { total_certificates: total, certificates: certs }
1735
+ end
1736
+
1737
+ # -----------------------------------------------------------------------
1738
+ # 19. proveCertificate
1739
+ # -----------------------------------------------------------------------
1740
+
1741
+ def write_prove_certificate_params(w, args)
1742
+ cert = args[:certificate] || {}
1743
+ write_certificate_fields(w, cert)
1744
+
1745
+ fields_to_reveal = args[:fields_to_reveal] || []
1746
+ w.write_varint(fields_to_reveal.length)
1747
+ fields_to_reveal.each { |f| w.write_utf8_string(f) }
1748
+
1749
+ w.write_bytes([args[:verifier].to_s].pack('H*'))
1750
+ w.write_privileged(args[:privileged], args[:privileged_reason])
1751
+ end
1752
+
1753
+ def read_prove_certificate_params(r)
1754
+ cert = read_cert_from_reader(r)
1755
+
1756
+ ftr_count = r.read_varint
1757
+ fields_to_reveal = ftr_count.times.map { r.read_utf8_string }
1758
+
1759
+ verifier = r.read_bytes(33).unpack1('H*')
1760
+ privileged, privileged_reason = r.read_privileged
1761
+
1762
+ { certificate: cert, fields_to_reveal: fields_to_reveal,
1763
+ verifier: verifier, privileged: privileged, privileged_reason: privileged_reason }
1764
+ end
1765
+
1766
+ def write_prove_certificate_result(w, result)
1767
+ keyring = result[:keyring_for_verifier] || {}
1768
+ w.write_varint(keyring.length)
1769
+ keyring.each do |k, v|
1770
+ w.write_utf8_string(k.to_s)
1771
+ v_bytes = decode_base64_32_safe(v.to_s)
1772
+ w.write_varint(v_bytes.bytesize)
1773
+ w.write_bytes(v_bytes)
1774
+ end
1775
+ end
1776
+
1777
+ def read_prove_certificate_result(r)
1778
+ kr_count = r.read_varint
1779
+ keyring = {}
1780
+ kr_count.times do
1781
+ k = r.read_utf8_string
1782
+ v_len = r.read_varint
1783
+ keyring[k] = encode_base64(r.read_bytes(v_len))
1784
+ end
1785
+ { keyring_for_verifier: keyring }
1786
+ end
1787
+
1788
+ # -----------------------------------------------------------------------
1789
+ # 20. relinquishCertificate
1790
+ # -----------------------------------------------------------------------
1791
+
1792
+ def write_relinquish_certificate_params(w, args)
1793
+ w.write_bytes(decode_base64_32(args[:type].to_s))
1794
+ w.write_bytes(decode_base64_32(args[:serial_number].to_s))
1795
+ w.write_bytes([args[:certifier].to_s].pack('H*'))
1796
+ end
1797
+
1798
+ def read_relinquish_certificate_params(r)
1799
+ type = encode_base64(r.read_bytes(32))
1800
+ serial_number = encode_base64(r.read_bytes(32))
1801
+ certifier = r.read_bytes(33).unpack1('H*')
1802
+ { type: type, serial_number: serial_number, certifier: certifier }
1803
+ end
1804
+
1805
+ def write_relinquish_certificate_result(w, _result); end
1806
+
1807
+ def read_relinquish_certificate_result(_r)
1808
+ { relinquished: true }
1809
+ end
1810
+
1811
+ # -----------------------------------------------------------------------
1812
+ # 21. discoverByIdentityKey
1813
+ # -----------------------------------------------------------------------
1814
+
1815
+ def write_discover_by_identity_key_params(w, args)
1816
+ w.write_bytes([args[:identity_key].to_s].pack('H*'))
1817
+ w.write_signed_varint(args[:limit].nil? ? -1 : args[:limit])
1818
+ w.write_signed_varint(args[:offset].nil? ? -1 : args[:offset])
1819
+ w.write_optional_bool(args[:seek_permission])
1820
+ end
1821
+
1822
+ def read_discover_by_identity_key_params(r)
1823
+ identity_key = r.read_bytes(33).unpack1('H*')
1824
+ limit = r.read_signed_varint
1825
+ offset = r.read_signed_varint
1826
+ seek_permission = r.read_optional_bool
1827
+ { identity_key: identity_key,
1828
+ limit: limit == -1 ? nil : limit,
1829
+ offset: offset == -1 ? nil : offset,
1830
+ seek_permission: seek_permission }
1831
+ end
1832
+
1833
+ def write_discover_by_identity_key_result(w, result)
1834
+ write_discovery_result(w, result)
1835
+ end
1836
+
1837
+ def read_discover_by_identity_key_result(r)
1838
+ read_discovery_result(r)
1839
+ end
1840
+
1841
+ # -----------------------------------------------------------------------
1842
+ # 22. discoverByAttributes
1843
+ # -----------------------------------------------------------------------
1844
+
1845
+ def write_discover_by_attributes_params(w, args)
1846
+ attributes = args[:attributes] || {}
1847
+ w.write_varint(attributes.length)
1848
+ attributes.each do |k, v|
1849
+ w.write_utf8_string(k.to_s)
1850
+ w.write_utf8_string(v.to_s)
1851
+ end
1852
+ w.write_signed_varint(args[:limit].nil? ? -1 : args[:limit])
1853
+ w.write_signed_varint(args[:offset].nil? ? -1 : args[:offset])
1854
+ w.write_optional_bool(args[:seek_permission])
1855
+ end
1856
+
1857
+ def read_discover_by_attributes_params(r)
1858
+ attr_count = r.read_varint
1859
+ attributes = {}
1860
+ attr_count.times do
1861
+ k = r.read_utf8_string
1862
+ attributes[k] = r.read_utf8_string
1863
+ end
1864
+ limit = r.read_signed_varint
1865
+ offset = r.read_signed_varint
1866
+ seek_permission = r.read_optional_bool
1867
+ { attributes: attributes,
1868
+ limit: limit == -1 ? nil : limit,
1869
+ offset: offset == -1 ? nil : offset,
1870
+ seek_permission: seek_permission }
1871
+ end
1872
+
1873
+ def write_discover_by_attributes_result(w, result)
1874
+ write_discovery_result(w, result)
1875
+ end
1876
+
1877
+ def read_discover_by_attributes_result(r)
1878
+ read_discovery_result(r)
1879
+ end
1880
+
1881
+ # -----------------------------------------------------------------------
1882
+ # 23. isAuthenticated (no params)
1883
+ # -----------------------------------------------------------------------
1884
+
1885
+ def write_is_authenticated_params(w, _args); end
1886
+
1887
+ def read_is_authenticated_params(_r)
1888
+ {}
1889
+ end
1890
+
1891
+ def write_is_authenticated_result(w, result)
1892
+ w.write_byte(result[:authenticated] ? 1 : 0)
1893
+ end
1894
+
1895
+ def read_is_authenticated_result(r)
1896
+ { authenticated: r.read_byte == 1 }
1897
+ end
1898
+
1899
+ # -----------------------------------------------------------------------
1900
+ # 24. waitForAuthentication (no params)
1901
+ # -----------------------------------------------------------------------
1902
+
1903
+ def write_wait_for_authentication_params(w, _args); end
1904
+
1905
+ def read_wait_for_authentication_params(_r)
1906
+ {}
1907
+ end
1908
+
1909
+ def write_wait_for_authentication_result(w, _result); end
1910
+
1911
+ def read_wait_for_authentication_result(_r)
1912
+ { authenticated: true }
1913
+ end
1914
+
1915
+ # -----------------------------------------------------------------------
1916
+ # 25. getHeight (no params)
1917
+ # -----------------------------------------------------------------------
1918
+
1919
+ def write_get_height_params(w, _args); end
1920
+
1921
+ def read_get_height_params(_r)
1922
+ {}
1923
+ end
1924
+
1925
+ def write_get_height_result(w, result)
1926
+ w.write_varint(result[:height].to_i)
1927
+ end
1928
+
1929
+ def read_get_height_result(r)
1930
+ { height: r.read_varint }
1931
+ end
1932
+
1933
+ # -----------------------------------------------------------------------
1934
+ # 26. getHeaderForHeight
1935
+ # -----------------------------------------------------------------------
1936
+
1937
+ def write_get_header_for_height_params(w, args)
1938
+ w.write_varint(args[:height].to_i)
1939
+ end
1940
+
1941
+ def read_get_header_for_height_params(r)
1942
+ { height: r.read_varint }
1943
+ end
1944
+
1945
+ def write_get_header_for_height_result(w, result)
1946
+ header_bytes = [result[:header].to_s].pack('H*')
1947
+ w.write_bytes(header_bytes)
1948
+ end
1949
+
1950
+ def read_get_header_for_height_result(r)
1951
+ { header: r.read_remaining.unpack1('H*') }
1952
+ end
1953
+
1954
+ # -----------------------------------------------------------------------
1955
+ # 27. getNetwork (no params)
1956
+ # -----------------------------------------------------------------------
1957
+
1958
+ def write_get_network_params(w, _args); end
1959
+
1960
+ def read_get_network_params(_r)
1961
+ {}
1962
+ end
1963
+
1964
+ def write_get_network_result(w, result)
1965
+ w.write_byte(result[:network] == 'mainnet' ? 0 : 1)
1966
+ end
1967
+
1968
+ def read_get_network_result(r)
1969
+ { network: r.read_byte.zero? ? 'mainnet' : 'testnet' }
1970
+ end
1971
+
1972
+ # -----------------------------------------------------------------------
1973
+ # 28. getVersion (no params)
1974
+ # -----------------------------------------------------------------------
1975
+
1976
+ def write_get_version_params(w, _args); end
1977
+
1978
+ def read_get_version_params(_r)
1979
+ {}
1980
+ end
1981
+
1982
+ def write_get_version_result(w, result)
1983
+ version_bytes = result[:version].to_s.encode('UTF-8').b
1984
+ w.write_bytes(version_bytes)
1985
+ end
1986
+
1987
+ def read_get_version_result(r)
1988
+ { version: r.read_remaining.force_encoding('UTF-8') }
1989
+ end
1990
+ end
1991
+ end
1992
+ end
1993
+ end