bsv-sdk 0.19.1 → 0.22.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 +89 -0
- data/README.md +2 -2
- data/lib/bsv/auth/transport.rb +1 -1
- data/lib/bsv/mcp/tools/broadcast_p2pkh.rb +5 -3
- data/lib/bsv/network/protocol.rb +4 -5
- 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 +6 -3
- data/lib/bsv/network/protocols/jungle_bus.rb +6 -0
- data/lib/bsv/network/protocols.rb +1 -0
- data/lib/bsv/network/provider.rb +7 -9
- 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/overlay/lookup_resolver.rb +0 -1
- data/lib/bsv/overlay/topic_broadcaster.rb +0 -1
- data/lib/bsv/primitives/curve.rb +1 -11
- data/lib/bsv/primitives/ecies.rb +1 -8
- data/lib/bsv/primitives/hex.rb +1 -1
- data/lib/bsv/script/script.rb +1 -1
- data/lib/bsv/transaction/beef.rb +0 -2
- data/lib/bsv/transaction/chain_tracker.rb +74 -13
- data/lib/bsv/transaction/chain_trackers/whats_on_chain.rb +3 -3
- data/lib/bsv/transaction/chain_trackers.rb +0 -10
- data/lib/bsv/transaction/fee_models/live_policy.rb +10 -8
- data/lib/bsv/transaction/merkle_path.rb +0 -2
- 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 +15 -8
- 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 +47 -3
- data/lib/bsv/transaction/chain_trackers/chaintracks.rb +0 -83
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
module Serializer
|
|
6
|
+
# Shared BRC-103 result codec for discover_by_identity_key and
|
|
7
|
+
# discover_by_attributes (both return the same shape).
|
|
8
|
+
#
|
|
9
|
+
# Result wire layout:
|
|
10
|
+
# [varint: total_certificates]
|
|
11
|
+
# per certificate: [IdentityCertificate inline bytes (int-prefixed base cert + meta)]
|
|
12
|
+
module DiscoverCertificatesResult
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
# @param result [Hash] { total_certificates:, certificates: [IdentityCert Hash, ...] }
|
|
16
|
+
# @return [String] binary
|
|
17
|
+
def serialize(result)
|
|
18
|
+
certs = result[:certificates] || []
|
|
19
|
+
w = Wire::Writer.new
|
|
20
|
+
w.write_varint(certs.length)
|
|
21
|
+
certs.each do |cert|
|
|
22
|
+
cert_bytes = Certificate.serialize_identity_certificate(cert)
|
|
23
|
+
w.write_bytes(cert_bytes)
|
|
24
|
+
end
|
|
25
|
+
w.buf
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @param bytes [String] binary
|
|
29
|
+
# @return [Hash] { total_certificates:, certificates: [...] }
|
|
30
|
+
def deserialize(bytes)
|
|
31
|
+
r = Wire::Reader.new(bytes)
|
|
32
|
+
total = r.read_varint
|
|
33
|
+
certs = total.times.map { Certificate.deserialize_identity_certificate(r) }
|
|
34
|
+
{ total_certificates: total, certificates: certs }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
module Serializer
|
|
6
|
+
# BRC-103 wire codec for the +encrypt+ call (call byte 11).
|
|
7
|
+
#
|
|
8
|
+
# Port of go-sdk/wallet/serializer/encrypt.go.
|
|
9
|
+
module Encrypt
|
|
10
|
+
# Args wire layout:
|
|
11
|
+
# [key-related params: protocol + key_id + counterparty + privileged]
|
|
12
|
+
# [VarInt plaintext_len][plaintext bytes]
|
|
13
|
+
# [optional_bool seek_permission]
|
|
14
|
+
module Args
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
def serialize(args)
|
|
18
|
+
w = BSV::Wallet::Wire::Writer.new
|
|
19
|
+
Common.write_key_related_params(
|
|
20
|
+
w,
|
|
21
|
+
protocol_id: args[:protocol_id],
|
|
22
|
+
key_id: args[:key_id],
|
|
23
|
+
counterparty: args[:counterparty],
|
|
24
|
+
privileged: args[:privileged],
|
|
25
|
+
privileged_reason: args[:privileged_reason]
|
|
26
|
+
)
|
|
27
|
+
plaintext = Common.to_binary(args[:plaintext])
|
|
28
|
+
w.write_varint(plaintext.bytesize)
|
|
29
|
+
w.write_bytes(plaintext)
|
|
30
|
+
w.write_optional_bool(args[:seek_permission])
|
|
31
|
+
w.buf
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def deserialize(bytes)
|
|
35
|
+
r = BSV::Wallet::Wire::Reader.new(bytes)
|
|
36
|
+
params = Common.read_key_related_params(r)
|
|
37
|
+
len = r.read_varint
|
|
38
|
+
plaintext = r.read_bytes(len)
|
|
39
|
+
seek_permission = r.read_optional_bool
|
|
40
|
+
params.merge(plaintext: plaintext, seek_permission: seek_permission)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Result wire layout:
|
|
45
|
+
# [ciphertext bytes — remaining payload]
|
|
46
|
+
module Result
|
|
47
|
+
module_function
|
|
48
|
+
|
|
49
|
+
def serialize(result)
|
|
50
|
+
Common.to_binary(result[:ciphertext])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def deserialize(bytes)
|
|
54
|
+
{ ciphertext: bytes.b }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
module Serializer
|
|
6
|
+
# BRC-103 serialiser for the get_header_for_height call (call byte 26).
|
|
7
|
+
#
|
|
8
|
+
# Wire format:
|
|
9
|
+
# Args: [VarInt] height
|
|
10
|
+
# Result: [80 bytes] raw block header
|
|
11
|
+
#
|
|
12
|
+
# Port of go-sdk/wallet/serializer/get_header.go.
|
|
13
|
+
module GetHeaderForHeight
|
|
14
|
+
HEADER_BYTES = 80
|
|
15
|
+
|
|
16
|
+
module Args
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# @param args [Hash] { height: Integer }
|
|
20
|
+
# @return [String] binary (varint-encoded height)
|
|
21
|
+
def serialize(args)
|
|
22
|
+
BSV::Transaction::VarInt.encode(args[:height].to_i)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param bytes [String] binary
|
|
26
|
+
# @return [Hash] { height: Integer }
|
|
27
|
+
def deserialize(bytes)
|
|
28
|
+
raise BSV::Wallet::InvalidParameterError.new('get_header_for_height args', 'at least 1 byte') if bytes.b.empty?
|
|
29
|
+
|
|
30
|
+
height, = BSV::Transaction::VarInt.decode(bytes.b, 0)
|
|
31
|
+
{ height: height }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module Result
|
|
36
|
+
module_function
|
|
37
|
+
|
|
38
|
+
# @param result [Hash] { header: String } — 80-byte binary block header
|
|
39
|
+
# @return [String] 80-byte binary
|
|
40
|
+
# @raise [InvalidParameterError] if header is not exactly 80 bytes
|
|
41
|
+
def serialize(result)
|
|
42
|
+
header = result[:header].to_s.b
|
|
43
|
+
unless header.bytesize == HEADER_BYTES
|
|
44
|
+
raise BSV::Wallet::InvalidParameterError.new(
|
|
45
|
+
'get_header_for_height result header',
|
|
46
|
+
"exactly #{HEADER_BYTES} bytes, got #{header.bytesize}"
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
header
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param bytes [String] binary
|
|
54
|
+
# @return [Hash] { header: String }
|
|
55
|
+
# @raise [InvalidParameterError] if payload is not exactly 80 bytes
|
|
56
|
+
def deserialize(bytes)
|
|
57
|
+
data = bytes.b
|
|
58
|
+
unless data.bytesize == HEADER_BYTES
|
|
59
|
+
raise BSV::Wallet::InvalidParameterError.new(
|
|
60
|
+
'get_header_for_height result',
|
|
61
|
+
"exactly #{HEADER_BYTES} bytes, got #{data.bytesize}"
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
{ header: data }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
module Serializer
|
|
6
|
+
# BRC-103 serialiser for the get_height call (call byte 25).
|
|
7
|
+
#
|
|
8
|
+
# Wire format — result only (no args payload):
|
|
9
|
+
# [VarInt] block height (uint32 range)
|
|
10
|
+
#
|
|
11
|
+
# Port of go-sdk/wallet/serializer/get_height.go.
|
|
12
|
+
module GetHeight
|
|
13
|
+
module Args
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def serialize(_args = {})
|
|
17
|
+
''.b
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def deserialize(_bytes)
|
|
21
|
+
{}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module Result
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
# @param result [Hash] { height: Integer }
|
|
29
|
+
# @return [String] binary (varint-encoded height)
|
|
30
|
+
def serialize(result)
|
|
31
|
+
BSV::Transaction::VarInt.encode(result[:height].to_i)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param bytes [String] binary
|
|
35
|
+
# @return [Hash] { height: Integer }
|
|
36
|
+
def deserialize(bytes)
|
|
37
|
+
raise BSV::Wallet::InvalidParameterError.new('get_height result', 'at least 1 byte') if bytes.b.empty?
|
|
38
|
+
|
|
39
|
+
height, = BSV::Transaction::VarInt.decode(bytes.b, 0)
|
|
40
|
+
{ height: height }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
module Serializer
|
|
6
|
+
# BRC-103 serialiser for the get_network call (call byte 27).
|
|
7
|
+
#
|
|
8
|
+
# Wire format — result only (no args payload):
|
|
9
|
+
# [1 byte] 0x00 = mainnet, 0x01 = testnet
|
|
10
|
+
module GetNetwork
|
|
11
|
+
# Args is always empty — get_network takes no parameters.
|
|
12
|
+
module Args
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def serialize(_args = {})
|
|
16
|
+
''.b
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def deserialize(_bytes)
|
|
20
|
+
{}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module Result
|
|
25
|
+
MAINNET_CODE = 0
|
|
26
|
+
TESTNET_CODE = 1
|
|
27
|
+
|
|
28
|
+
module_function
|
|
29
|
+
|
|
30
|
+
# @param result [Hash] { network: :mainnet | :testnet }
|
|
31
|
+
# @return [String] 1-byte binary
|
|
32
|
+
# @raise [InvalidParameterError] if +:network+ is not :mainnet or :testnet
|
|
33
|
+
def serialize(result)
|
|
34
|
+
code = case result[:network]
|
|
35
|
+
when :mainnet then MAINNET_CODE
|
|
36
|
+
when :testnet then TESTNET_CODE
|
|
37
|
+
else
|
|
38
|
+
raise BSV::Wallet::InvalidParameterError.new(
|
|
39
|
+
'network', ":mainnet or :testnet, got #{result[:network].inspect}"
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
[code].pack('C')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @param bytes [String] 1-byte binary
|
|
46
|
+
# @return [Hash] { network: :mainnet | :testnet }
|
|
47
|
+
# @raise [InvalidParameterError] if the byte is not 0x00 or 0x01
|
|
48
|
+
def deserialize(bytes)
|
|
49
|
+
data = bytes.b
|
|
50
|
+
raise BSV::Wallet::InvalidParameterError.new('get_network result', 'exactly 1 byte') unless data.bytesize == 1
|
|
51
|
+
|
|
52
|
+
case data.getbyte(0)
|
|
53
|
+
when MAINNET_CODE then { network: :mainnet }
|
|
54
|
+
when TESTNET_CODE then { network: :testnet }
|
|
55
|
+
else
|
|
56
|
+
raise BSV::Wallet::InvalidParameterError.new(
|
|
57
|
+
'get_network result', "0x00 (mainnet) or 0x01 (testnet), got 0x#{data.unpack1('H*')}"
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -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
|