bsv-sdk 0.20.0 → 0.23.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +106 -0
- data/lib/bsv/mcp/tools/broadcast_p2pkh.rb +5 -3
- data/lib/bsv/network/protocols/arc.rb +4 -30
- data/lib/bsv/network/protocols/arcade.rb +163 -0
- data/lib/bsv/network/protocols/chaintracks.rb +27 -4
- data/lib/bsv/network/protocols/jungle_bus.rb +34 -0
- data/lib/bsv/network/protocols/woc_rest.rb +28 -1
- data/lib/bsv/network/protocols.rb +1 -0
- data/lib/bsv/network/providers/gorilla_pool.rb +18 -18
- data/lib/bsv/network/util.rb +44 -0
- data/lib/bsv/network.rb +1 -0
- data/lib/bsv/transaction/chain_tracker.rb +66 -13
- data/lib/bsv/transaction/chain_trackers.rb +0 -10
- data/lib/bsv/transaction/fee_models/live_policy.rb +10 -8
- data/lib/bsv/version.rb +1 -1
- data/lib/bsv/wallet/errors.rb +65 -21
- data/lib/bsv/wallet/proto_wallet/validators.rb +7 -49
- data/lib/bsv/wallet/proto_wallet.rb +14 -1
- data/lib/bsv/wallet/serializer/abort_action.rb +38 -0
- data/lib/bsv/wallet/serializer/acquire_certificate.rb +171 -0
- data/lib/bsv/wallet/serializer/certificate.rb +184 -0
- data/lib/bsv/wallet/serializer/common.rb +207 -0
- data/lib/bsv/wallet/serializer/create_action_args.rb +259 -0
- data/lib/bsv/wallet/serializer/create_action_result.rb +85 -0
- data/lib/bsv/wallet/serializer/create_hmac.rb +67 -0
- data/lib/bsv/wallet/serializer/create_signature.rb +90 -0
- data/lib/bsv/wallet/serializer/decrypt.rb +60 -0
- data/lib/bsv/wallet/serializer/discover_by_attributes.rb +61 -0
- data/lib/bsv/wallet/serializer/discover_by_identity_key.rb +49 -0
- data/lib/bsv/wallet/serializer/discover_certificates_result.rb +39 -0
- data/lib/bsv/wallet/serializer/encrypt.rb +60 -0
- data/lib/bsv/wallet/serializer/get_header_for_height.rb +71 -0
- data/lib/bsv/wallet/serializer/get_height.rb +46 -0
- data/lib/bsv/wallet/serializer/get_network.rb +65 -0
- data/lib/bsv/wallet/serializer/get_public_key.rb +86 -0
- data/lib/bsv/wallet/serializer/get_version.rb +44 -0
- data/lib/bsv/wallet/serializer/internalize_action.rb +151 -0
- data/lib/bsv/wallet/serializer/list_actions.rb +348 -0
- data/lib/bsv/wallet/serializer/list_certificates.rb +124 -0
- data/lib/bsv/wallet/serializer/list_outputs.rb +167 -0
- data/lib/bsv/wallet/serializer/prove_certificate.rb +146 -0
- data/lib/bsv/wallet/serializer/relinquish_certificate.rb +56 -0
- data/lib/bsv/wallet/serializer/relinquish_output.rb +44 -0
- data/lib/bsv/wallet/serializer/reveal_counterparty_key_linkage.rb +108 -0
- data/lib/bsv/wallet/serializer/reveal_specific_key_linkage.rb +116 -0
- data/lib/bsv/wallet/serializer/sign_action_args.rb +94 -0
- data/lib/bsv/wallet/serializer/sign_action_result.rb +49 -0
- data/lib/bsv/wallet/serializer/status.rb +85 -0
- data/lib/bsv/wallet/serializer/verify_hmac.rb +67 -0
- data/lib/bsv/wallet/serializer/verify_signature.rb +101 -0
- data/lib/bsv/wallet/serializer.rb +180 -0
- data/lib/bsv/wallet/substrates/http_wallet_json.rb +129 -0
- data/lib/bsv/wallet/substrates/http_wallet_wire.rb +99 -0
- data/lib/bsv/wallet/wallet_wire.rb +20 -0
- data/lib/bsv/wallet/wallet_wire_processor.rb +61 -0
- data/lib/bsv/wallet/wallet_wire_transceiver.rb +61 -0
- data/lib/bsv/wallet/wire/calls.rb +79 -0
- data/lib/bsv/wallet/wire/frame.rb +181 -0
- data/lib/bsv/wallet/wire/reader_writer.rb +402 -0
- data/lib/bsv/wallet/wire/validation.rb +213 -0
- data/lib/bsv/wallet/wire.rb +13 -0
- data/lib/bsv/wallet.rb +17 -0
- metadata +46 -2
- data/lib/bsv/transaction/chain_trackers/chaintracks.rb +0 -83
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
module Serializer
|
|
6
|
+
# BRC-103 wire codec for the +get_public_key+ call (call byte 8).
|
|
7
|
+
#
|
|
8
|
+
# Port of go-sdk/wallet/serializer/get_public_key.go.
|
|
9
|
+
module GetPublicKey
|
|
10
|
+
IDENTITY_KEY_FLAG = 1
|
|
11
|
+
PUBKEY_SIZE = 33
|
|
12
|
+
|
|
13
|
+
# Args wire layout:
|
|
14
|
+
# [1 byte: identity_key flag — 0=no, 1=yes]
|
|
15
|
+
# If 0: [key-related params][optional_bool for_self]
|
|
16
|
+
# If 1: [privileged params only]
|
|
17
|
+
# [optional_bool seek_permission]
|
|
18
|
+
module Args
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
def serialize(args)
|
|
22
|
+
identity_key = args[:identity_key]
|
|
23
|
+
w = BSV::Wallet::Wire::Writer.new
|
|
24
|
+
|
|
25
|
+
if identity_key
|
|
26
|
+
w.write_byte(IDENTITY_KEY_FLAG)
|
|
27
|
+
Common.write_privileged_params(w, args[:privileged], args[:privileged_reason])
|
|
28
|
+
else
|
|
29
|
+
w.write_byte(0)
|
|
30
|
+
Common.write_key_related_params(
|
|
31
|
+
w,
|
|
32
|
+
protocol_id: args[:protocol_id],
|
|
33
|
+
key_id: args[:key_id],
|
|
34
|
+
counterparty: args[:counterparty],
|
|
35
|
+
privileged: args[:privileged],
|
|
36
|
+
privileged_reason: args[:privileged_reason]
|
|
37
|
+
)
|
|
38
|
+
w.write_optional_bool(args[:for_self])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
w.write_optional_bool(args[:seek_permission])
|
|
42
|
+
w.buf
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def deserialize(bytes)
|
|
46
|
+
r = BSV::Wallet::Wire::Reader.new(bytes)
|
|
47
|
+
flag = r.read_byte
|
|
48
|
+
|
|
49
|
+
if flag == IDENTITY_KEY_FLAG
|
|
50
|
+
privileged, reason = Common.read_privileged_params(r)
|
|
51
|
+
seek_permission = r.read_optional_bool
|
|
52
|
+
{
|
|
53
|
+
identity_key: true,
|
|
54
|
+
privileged: privileged,
|
|
55
|
+
privileged_reason: reason,
|
|
56
|
+
seek_permission: seek_permission
|
|
57
|
+
}
|
|
58
|
+
else
|
|
59
|
+
params = Common.read_key_related_params(r)
|
|
60
|
+
for_self = r.read_optional_bool
|
|
61
|
+
seek_permission = r.read_optional_bool
|
|
62
|
+
params.merge(identity_key: false, for_self: for_self, seek_permission: seek_permission)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Result wire layout:
|
|
68
|
+
# [33 bytes: compressed public key]
|
|
69
|
+
module Result
|
|
70
|
+
module_function
|
|
71
|
+
|
|
72
|
+
def serialize(result)
|
|
73
|
+
pubkey = result[:public_key] || ''.b
|
|
74
|
+
pubkey.bytesize == PUBKEY_SIZE ? pubkey.b : [pubkey.to_s].pack('H*')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def deserialize(bytes)
|
|
78
|
+
raise ArgumentError, "public key too short: #{bytes.bytesize}" if bytes.bytesize < PUBKEY_SIZE
|
|
79
|
+
|
|
80
|
+
{ public_key: bytes.byteslice(0, PUBKEY_SIZE) }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
module Serializer
|
|
6
|
+
# BRC-103 serialiser for the get_version call (call byte 28).
|
|
7
|
+
#
|
|
8
|
+
# Wire format — result only (no args payload):
|
|
9
|
+
# [N bytes] raw UTF-8 version string (no length prefix)
|
|
10
|
+
#
|
|
11
|
+
# Port of go-sdk/wallet/serializer/get_version.go — the Go SDK emits
|
|
12
|
+
# the string bytes directly, with no varint length prefix.
|
|
13
|
+
module GetVersion
|
|
14
|
+
module Args
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
def serialize(_args = {})
|
|
18
|
+
''.b
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def deserialize(_bytes)
|
|
22
|
+
{}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module Result
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
# @param result [Hash] { version: String }
|
|
30
|
+
# @return [String] binary (raw UTF-8 bytes)
|
|
31
|
+
def serialize(result)
|
|
32
|
+
result[:version].to_s.b
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @param bytes [String] binary
|
|
36
|
+
# @return [Hash] { version: String }
|
|
37
|
+
def deserialize(bytes)
|
|
38
|
+
{ version: bytes.b.force_encoding('UTF-8') }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
module Serializer
|
|
6
|
+
# BRC-103 serialiser for internalize_action (call byte 5).
|
|
7
|
+
#
|
|
8
|
+
# Wire layout (port of go-sdk/wallet/serializer/internalize_action.go):
|
|
9
|
+
# [varint + bytes] tx (BEEF) — length-prefixed raw bytes
|
|
10
|
+
# [varint] outputs count
|
|
11
|
+
# For each output:
|
|
12
|
+
# [varint] output_index
|
|
13
|
+
# [1 byte] protocol: 0x01=wallet_payment, 0x02=basket_insertion
|
|
14
|
+
# If wallet_payment:
|
|
15
|
+
# [33 bytes] sender_identity_key (compressed pubkey)
|
|
16
|
+
# [int_bytes] derivation_prefix
|
|
17
|
+
# [int_bytes] derivation_suffix
|
|
18
|
+
# If basket_insertion:
|
|
19
|
+
# [string] basket
|
|
20
|
+
# [optional_string] custom_instructions
|
|
21
|
+
# [string_slice] tags
|
|
22
|
+
# [string_slice] labels
|
|
23
|
+
# [string] description
|
|
24
|
+
# [optional_bool] seek_permission
|
|
25
|
+
#
|
|
26
|
+
# Result wire layout: empty (success is implicit from the frame error byte).
|
|
27
|
+
module InternalizeActionArgs
|
|
28
|
+
PROTOCOL_WALLET_PAYMENT = 1
|
|
29
|
+
PROTOCOL_BASKET_INSERTION = 2
|
|
30
|
+
PUBKEY_SIZE = 33
|
|
31
|
+
|
|
32
|
+
module_function
|
|
33
|
+
|
|
34
|
+
# @param args [Hash]
|
|
35
|
+
# @return [String] binary
|
|
36
|
+
def serialize(args)
|
|
37
|
+
w = Wire::Writer.new
|
|
38
|
+
|
|
39
|
+
tx = (args[:tx] || ''.b).b
|
|
40
|
+
w.write_varint(tx.bytesize)
|
|
41
|
+
w.write_bytes(tx)
|
|
42
|
+
|
|
43
|
+
outputs = args[:outputs] || []
|
|
44
|
+
w.write_varint(outputs.length)
|
|
45
|
+
outputs.each { |out| serialize_output(w, out) }
|
|
46
|
+
|
|
47
|
+
w.write_string_slice(args[:labels])
|
|
48
|
+
w.write_string(args[:description].to_s)
|
|
49
|
+
w.write_optional_bool(args[:seek_permission])
|
|
50
|
+
|
|
51
|
+
w.buf
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param bytes [String] binary
|
|
55
|
+
# @return [Hash]
|
|
56
|
+
def deserialize(bytes)
|
|
57
|
+
r = Wire::Reader.new(bytes)
|
|
58
|
+
|
|
59
|
+
tx_len = r.read_varint
|
|
60
|
+
tx = r.read_bytes(tx_len)
|
|
61
|
+
|
|
62
|
+
output_count = r.read_varint
|
|
63
|
+
outputs = output_count.times.map { deserialize_output(r) }
|
|
64
|
+
|
|
65
|
+
labels = r.read_string_slice
|
|
66
|
+
description = r.read_string
|
|
67
|
+
seek_permission = r.read_optional_bool
|
|
68
|
+
|
|
69
|
+
result = { tx: tx, outputs: outputs, description: description }
|
|
70
|
+
result[:labels] = labels unless labels.nil?
|
|
71
|
+
result[:seek_permission] = seek_permission unless seek_permission.nil?
|
|
72
|
+
result
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def serialize_output(writer, out)
|
|
76
|
+
writer.write_varint(out[:output_index].to_i)
|
|
77
|
+
|
|
78
|
+
case out[:protocol]
|
|
79
|
+
when :wallet_payment
|
|
80
|
+
writer.write_byte(PROTOCOL_WALLET_PAYMENT)
|
|
81
|
+
payment = out[:payment_remittance] || {}
|
|
82
|
+
writer.write_bytes([payment[:sender_identity_key]].pack('H*'))
|
|
83
|
+
writer.write_int_bytes(payment[:derivation_prefix])
|
|
84
|
+
writer.write_int_bytes(payment[:derivation_suffix])
|
|
85
|
+
when :basket_insertion
|
|
86
|
+
writer.write_byte(PROTOCOL_BASKET_INSERTION)
|
|
87
|
+
insertion = out[:insertion_remittance] || {}
|
|
88
|
+
writer.write_string(insertion[:basket].to_s)
|
|
89
|
+
writer.write_optional_string(insertion[:custom_instructions])
|
|
90
|
+
writer.write_string_slice(insertion[:tags])
|
|
91
|
+
else
|
|
92
|
+
raise ArgumentError, "unknown internalize protocol: #{out[:protocol]}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def deserialize_output(reader)
|
|
97
|
+
output_index = reader.read_varint
|
|
98
|
+
protocol_byte = reader.read_byte
|
|
99
|
+
|
|
100
|
+
case protocol_byte
|
|
101
|
+
when PROTOCOL_WALLET_PAYMENT
|
|
102
|
+
key_bytes = reader.read_bytes(PUBKEY_SIZE)
|
|
103
|
+
prefix = reader.read_int_bytes
|
|
104
|
+
suffix = reader.read_int_bytes
|
|
105
|
+
{
|
|
106
|
+
output_index: output_index,
|
|
107
|
+
protocol: :wallet_payment,
|
|
108
|
+
payment_remittance: {
|
|
109
|
+
sender_identity_key: key_bytes.unpack1('H*'),
|
|
110
|
+
derivation_prefix: prefix,
|
|
111
|
+
derivation_suffix: suffix
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
when PROTOCOL_BASKET_INSERTION
|
|
115
|
+
basket = reader.read_string
|
|
116
|
+
custom = reader.read_optional_string
|
|
117
|
+
tags = reader.read_string_slice
|
|
118
|
+
result = {
|
|
119
|
+
output_index: output_index,
|
|
120
|
+
protocol: :basket_insertion,
|
|
121
|
+
insertion_remittance: { basket: basket }
|
|
122
|
+
}
|
|
123
|
+
result[:insertion_remittance][:custom_instructions] = custom unless custom.nil?
|
|
124
|
+
result[:insertion_remittance][:tags] = tags unless tags.nil?
|
|
125
|
+
result
|
|
126
|
+
else
|
|
127
|
+
raise ArgumentError, "invalid internalize protocol byte: #{protocol_byte}"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private_class_method :serialize_output, :deserialize_output
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
module InternalizeActionResult
|
|
135
|
+
module_function
|
|
136
|
+
|
|
137
|
+
# @param _result [Hash] ignored
|
|
138
|
+
# @return [String] empty binary
|
|
139
|
+
def serialize(_result = {})
|
|
140
|
+
''.b
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @param _bytes [String] ignored
|
|
144
|
+
# @return [Hash] { accepted: true }
|
|
145
|
+
def deserialize(_bytes = nil)
|
|
146
|
+
{ accepted: true }
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
module Serializer
|
|
6
|
+
# BRC-103 serialiser for list_actions (call byte 4).
|
|
7
|
+
#
|
|
8
|
+
# Wire layout (port of go-sdk/wallet/serializer/list_actions.go):
|
|
9
|
+
#
|
|
10
|
+
# Args:
|
|
11
|
+
# [string_slice] labels
|
|
12
|
+
# [1 byte] label_query_mode: 0x01=any, 0x02=all, 0xFF=absent
|
|
13
|
+
# [optional_bool] include_labels
|
|
14
|
+
# [optional_bool] include_inputs
|
|
15
|
+
# [optional_bool] include_input_source_locking_scripts
|
|
16
|
+
# [optional_bool] include_input_unlocking_scripts
|
|
17
|
+
# [optional_bool] include_outputs
|
|
18
|
+
# [optional_bool] include_output_locking_scripts
|
|
19
|
+
# [optional_uint32] limit
|
|
20
|
+
# [optional_uint32] offset
|
|
21
|
+
# [optional_bool] seek_permission
|
|
22
|
+
#
|
|
23
|
+
# Result:
|
|
24
|
+
# [varint] total_actions
|
|
25
|
+
# For each action:
|
|
26
|
+
# [32 bytes] txid (display order — reversed from wire storage)
|
|
27
|
+
# [varint] satoshis (int64 as varint)
|
|
28
|
+
# [1 byte] status code
|
|
29
|
+
# [optional_bool ptr] is_outgoing (written as optional_bool pointer in Go)
|
|
30
|
+
# [string] description
|
|
31
|
+
# [string_slice] labels
|
|
32
|
+
# [varint] version
|
|
33
|
+
# [varint] lock_time
|
|
34
|
+
# [inputs] NegativeOne | varint count + input_record…
|
|
35
|
+
# [outputs] NegativeOne | varint count + output_record…
|
|
36
|
+
#
|
|
37
|
+
# Input record:
|
|
38
|
+
# [36 bytes] source_outpoint (32-byte wire txid + varint vout)
|
|
39
|
+
# [varint] source_satoshis
|
|
40
|
+
# [int_bytes_optional] source_locking_script
|
|
41
|
+
# [int_bytes_optional] unlocking_script
|
|
42
|
+
# [string] input_description
|
|
43
|
+
# [varint] sequence_number
|
|
44
|
+
#
|
|
45
|
+
# Output record:
|
|
46
|
+
# [varint] output_index
|
|
47
|
+
# [varint] satoshis
|
|
48
|
+
# [int_bytes_optional] locking_script
|
|
49
|
+
# [optional_bool ptr] spendable
|
|
50
|
+
# [string] output_description
|
|
51
|
+
# [string] basket
|
|
52
|
+
# [string_slice] tags
|
|
53
|
+
# [optional_string] custom_instructions
|
|
54
|
+
module ListActionsArgs
|
|
55
|
+
LABEL_QUERY_MODE_ANY = 1
|
|
56
|
+
LABEL_QUERY_MODE_ALL = 2
|
|
57
|
+
|
|
58
|
+
module_function
|
|
59
|
+
|
|
60
|
+
# @param args [Hash]
|
|
61
|
+
# @return [String] binary
|
|
62
|
+
def serialize(args)
|
|
63
|
+
w = Wire::Writer.new
|
|
64
|
+
|
|
65
|
+
w.write_string_slice(args[:labels])
|
|
66
|
+
|
|
67
|
+
case args[:label_query_mode]
|
|
68
|
+
when :any
|
|
69
|
+
w.write_byte(LABEL_QUERY_MODE_ANY)
|
|
70
|
+
when :all
|
|
71
|
+
w.write_byte(LABEL_QUERY_MODE_ALL)
|
|
72
|
+
else
|
|
73
|
+
w.write_byte(0xFF)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
w.write_optional_bool(args[:include_labels])
|
|
77
|
+
w.write_optional_bool(args[:include_inputs])
|
|
78
|
+
w.write_optional_bool(args[:include_input_source_locking_scripts])
|
|
79
|
+
w.write_optional_bool(args[:include_input_unlocking_scripts])
|
|
80
|
+
w.write_optional_bool(args[:include_outputs])
|
|
81
|
+
w.write_optional_bool(args[:include_output_locking_scripts])
|
|
82
|
+
|
|
83
|
+
w.write_optional_uint32(args[:limit])
|
|
84
|
+
w.write_optional_uint32(args[:offset])
|
|
85
|
+
w.write_optional_bool(args[:seek_permission])
|
|
86
|
+
|
|
87
|
+
w.buf
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @param bytes [String] binary
|
|
91
|
+
# @return [Hash]
|
|
92
|
+
def deserialize(bytes)
|
|
93
|
+
r = Wire::Reader.new(bytes)
|
|
94
|
+
|
|
95
|
+
labels = r.read_string_slice
|
|
96
|
+
|
|
97
|
+
mode_byte = r.read_byte
|
|
98
|
+
label_query_mode = case mode_byte
|
|
99
|
+
when LABEL_QUERY_MODE_ANY then :any
|
|
100
|
+
when LABEL_QUERY_MODE_ALL then :all
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
result = {}
|
|
104
|
+
result[:labels] = labels unless labels.nil?
|
|
105
|
+
result[:label_query_mode] = label_query_mode unless label_query_mode.nil?
|
|
106
|
+
|
|
107
|
+
v = r.read_optional_bool
|
|
108
|
+
result[:include_labels] = v unless v.nil?
|
|
109
|
+
v = r.read_optional_bool
|
|
110
|
+
result[:include_inputs] = v unless v.nil?
|
|
111
|
+
v = r.read_optional_bool
|
|
112
|
+
result[:include_input_source_locking_scripts] = v unless v.nil?
|
|
113
|
+
v = r.read_optional_bool
|
|
114
|
+
result[:include_input_unlocking_scripts] = v unless v.nil?
|
|
115
|
+
v = r.read_optional_bool
|
|
116
|
+
result[:include_outputs] = v unless v.nil?
|
|
117
|
+
v = r.read_optional_bool
|
|
118
|
+
result[:include_output_locking_scripts] = v unless v.nil?
|
|
119
|
+
|
|
120
|
+
lim = r.read_optional_uint32
|
|
121
|
+
result[:limit] = lim unless lim.nil?
|
|
122
|
+
off = r.read_optional_uint32
|
|
123
|
+
result[:offset] = off unless off.nil?
|
|
124
|
+
|
|
125
|
+
v = r.read_optional_bool
|
|
126
|
+
result[:seek_permission] = v unless v.nil?
|
|
127
|
+
|
|
128
|
+
result
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
module ListActionsResult
|
|
133
|
+
ACTION_STATUS_CODES = {
|
|
134
|
+
completed: 1,
|
|
135
|
+
unprocessed: 2,
|
|
136
|
+
sending: 3,
|
|
137
|
+
unproven: 4,
|
|
138
|
+
unsigned: 5,
|
|
139
|
+
no_send: 6,
|
|
140
|
+
non_final: 7
|
|
141
|
+
}.freeze
|
|
142
|
+
|
|
143
|
+
ACTION_CODE_STATUSES = ACTION_STATUS_CODES.invert.freeze
|
|
144
|
+
|
|
145
|
+
module_function
|
|
146
|
+
|
|
147
|
+
# @param result [Hash] { total_actions: Integer, actions: Array<Hash> }
|
|
148
|
+
# @return [String] binary
|
|
149
|
+
def serialize(result)
|
|
150
|
+
w = Wire::Writer.new
|
|
151
|
+
|
|
152
|
+
actions = result[:actions] || []
|
|
153
|
+
w.write_varint(actions.length)
|
|
154
|
+
|
|
155
|
+
actions.each do |action|
|
|
156
|
+
txid_hex = action[:txid].to_s
|
|
157
|
+
w.write_bytes([txid_hex].pack('H*'))
|
|
158
|
+
w.write_varint(action[:satoshis].to_i)
|
|
159
|
+
|
|
160
|
+
code = ACTION_STATUS_CODES.fetch(action[:status]) do
|
|
161
|
+
raise ArgumentError, "invalid action status: #{action[:status]}"
|
|
162
|
+
end
|
|
163
|
+
w.write_byte(code)
|
|
164
|
+
|
|
165
|
+
# is_outgoing: non-optional bool written as optional_bool pointer in Go
|
|
166
|
+
w.write_byte(action[:is_outgoing] ? 1 : 0)
|
|
167
|
+
|
|
168
|
+
w.write_string(action[:description].to_s)
|
|
169
|
+
w.write_string_slice(action[:labels])
|
|
170
|
+
w.write_varint(action[:version].to_i)
|
|
171
|
+
w.write_varint(action[:lock_time].to_i)
|
|
172
|
+
|
|
173
|
+
serialize_action_inputs(w, action[:inputs])
|
|
174
|
+
serialize_action_outputs(w, action[:outputs])
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
w.buf
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# @param bytes [String] binary
|
|
181
|
+
# @return [Hash]
|
|
182
|
+
def deserialize(bytes)
|
|
183
|
+
r = Wire::Reader.new(bytes)
|
|
184
|
+
|
|
185
|
+
total = r.read_varint
|
|
186
|
+
actions = total.times.map do
|
|
187
|
+
txid_hex = r.read_bytes(32).unpack1('H*')
|
|
188
|
+
satoshis = r.read_varint
|
|
189
|
+
code = r.read_byte
|
|
190
|
+
status = ACTION_CODE_STATUSES.fetch(code) do
|
|
191
|
+
raise ArgumentError, "invalid action status code: #{code}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
is_outgoing = r.read_byte == 1
|
|
195
|
+
description = r.read_string
|
|
196
|
+
labels = r.read_string_slice
|
|
197
|
+
version = r.read_varint
|
|
198
|
+
lock_time = r.read_varint
|
|
199
|
+
|
|
200
|
+
inputs = deserialize_action_inputs(r)
|
|
201
|
+
outputs = deserialize_action_outputs(r)
|
|
202
|
+
|
|
203
|
+
action = {
|
|
204
|
+
txid: txid_hex,
|
|
205
|
+
satoshis: satoshis,
|
|
206
|
+
status: status,
|
|
207
|
+
is_outgoing: is_outgoing,
|
|
208
|
+
description: description,
|
|
209
|
+
version: version,
|
|
210
|
+
lock_time: lock_time
|
|
211
|
+
}
|
|
212
|
+
action[:labels] = labels unless labels.nil?
|
|
213
|
+
action[:inputs] = inputs unless inputs.nil?
|
|
214
|
+
action[:outputs] = outputs unless outputs.nil?
|
|
215
|
+
action
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
{ total_actions: total, actions: actions }
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def serialize_action_inputs(writer, inputs)
|
|
222
|
+
if inputs.nil? || inputs.empty?
|
|
223
|
+
writer.write_negative_one
|
|
224
|
+
return
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
writer.write_varint(inputs.length)
|
|
228
|
+
inputs.each do |inp|
|
|
229
|
+
txid_hex, vout = inp[:source_outpoint].split('.')
|
|
230
|
+
writer.write_bytes([txid_hex].pack('H*'))
|
|
231
|
+
writer.write_varint(vout.to_i)
|
|
232
|
+
|
|
233
|
+
writer.write_varint(inp[:source_satoshis].to_i)
|
|
234
|
+
|
|
235
|
+
script = inp[:source_locking_script]
|
|
236
|
+
if script && !script.b.empty?
|
|
237
|
+
writer.write_int_bytes(script)
|
|
238
|
+
else
|
|
239
|
+
writer.write_negative_one
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
unlock_script = inp[:unlocking_script]
|
|
243
|
+
if unlock_script && !unlock_script.b.empty?
|
|
244
|
+
writer.write_int_bytes(unlock_script)
|
|
245
|
+
else
|
|
246
|
+
writer.write_negative_one
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
writer.write_string(inp[:input_description].to_s)
|
|
250
|
+
writer.write_varint(inp[:sequence_number].to_i)
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def serialize_action_outputs(writer, outputs)
|
|
255
|
+
if outputs.nil? || outputs.empty?
|
|
256
|
+
writer.write_negative_one
|
|
257
|
+
return
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
writer.write_varint(outputs.length)
|
|
261
|
+
outputs.each do |out|
|
|
262
|
+
writer.write_varint(out[:output_index].to_i)
|
|
263
|
+
writer.write_varint(out[:satoshis].to_i)
|
|
264
|
+
|
|
265
|
+
script = out[:locking_script]
|
|
266
|
+
if script && !script.b.empty?
|
|
267
|
+
writer.write_int_bytes(script)
|
|
268
|
+
else
|
|
269
|
+
writer.write_negative_one
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
writer.write_byte(out[:spendable] ? 1 : 0)
|
|
273
|
+
writer.write_string(out[:output_description].to_s)
|
|
274
|
+
writer.write_string(out[:basket].to_s)
|
|
275
|
+
writer.write_string_slice(out[:tags])
|
|
276
|
+
writer.write_optional_string(out[:custom_instructions])
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def deserialize_action_inputs(reader)
|
|
281
|
+
count = reader.read_varint
|
|
282
|
+
return nil if count == 0xFFFF_FFFF_FFFF_FFFF
|
|
283
|
+
|
|
284
|
+
count.times.map do
|
|
285
|
+
txid_hex = reader.read_bytes(32).unpack1('H*')
|
|
286
|
+
vout = reader.read_varint
|
|
287
|
+
source_outpoint = "#{txid_hex}.#{vout}"
|
|
288
|
+
|
|
289
|
+
source_satoshis = reader.read_varint
|
|
290
|
+
|
|
291
|
+
source_locking_script = reader.read_int_bytes
|
|
292
|
+
source_locking_script = nil if source_locking_script.empty?
|
|
293
|
+
|
|
294
|
+
unlocking_script = reader.read_int_bytes
|
|
295
|
+
unlocking_script = nil if unlocking_script.empty?
|
|
296
|
+
|
|
297
|
+
input_description = reader.read_string
|
|
298
|
+
sequence_number = reader.read_varint
|
|
299
|
+
|
|
300
|
+
inp = {
|
|
301
|
+
source_outpoint: source_outpoint,
|
|
302
|
+
source_satoshis: source_satoshis,
|
|
303
|
+
input_description: input_description,
|
|
304
|
+
sequence_number: sequence_number
|
|
305
|
+
}
|
|
306
|
+
inp[:source_locking_script] = source_locking_script unless source_locking_script.nil?
|
|
307
|
+
inp[:unlocking_script] = unlocking_script unless unlocking_script.nil?
|
|
308
|
+
inp
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def deserialize_action_outputs(reader)
|
|
313
|
+
count = reader.read_varint
|
|
314
|
+
return nil if count == 0xFFFF_FFFF_FFFF_FFFF
|
|
315
|
+
|
|
316
|
+
count.times.map do
|
|
317
|
+
output_index = reader.read_varint
|
|
318
|
+
satoshis = reader.read_varint
|
|
319
|
+
|
|
320
|
+
locking_script = reader.read_int_bytes
|
|
321
|
+
locking_script = nil if locking_script.empty?
|
|
322
|
+
|
|
323
|
+
spendable = reader.read_byte == 1
|
|
324
|
+
output_description = reader.read_string
|
|
325
|
+
basket = reader.read_string
|
|
326
|
+
tags = reader.read_string_slice
|
|
327
|
+
custom = reader.read_optional_string
|
|
328
|
+
|
|
329
|
+
out = {
|
|
330
|
+
output_index: output_index,
|
|
331
|
+
satoshis: satoshis,
|
|
332
|
+
spendable: spendable,
|
|
333
|
+
output_description: output_description,
|
|
334
|
+
basket: basket
|
|
335
|
+
}
|
|
336
|
+
out[:locking_script] = locking_script unless locking_script.nil?
|
|
337
|
+
out[:tags] = tags unless tags.nil?
|
|
338
|
+
out[:custom_instructions] = custom unless custom.nil?
|
|
339
|
+
out
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
private_class_method :serialize_action_inputs, :serialize_action_outputs,
|
|
344
|
+
:deserialize_action_inputs, :deserialize_action_outputs
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|