bsv-sdk 0.7.0 → 0.8.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 +406 -146
- data/lib/bsv/identity/client.rb +11 -5
- data/lib/bsv/overlay/topic_broadcaster.rb +1 -3
- data/lib/bsv/primitives/openssl_ec_shim.rb +8 -4
- data/lib/bsv/primitives/secp256k1.rb +11 -1
- data/lib/bsv/transaction/beef.rb +45 -38
- data/lib/bsv/transaction/merkle_path.rb +64 -0
- data/lib/bsv/transaction/transaction.rb +3 -2
- data/lib/bsv/version.rb +1 -1
- metadata +2 -32
- data/lib/bsv/attest/configuration.rb +0 -9
- data/lib/bsv/attest/response.rb +0 -19
- data/lib/bsv/attest/verification_error.rb +0 -7
- data/lib/bsv/attest/version.rb +0 -7
- data/lib/bsv/attest.rb +0 -71
- data/lib/bsv/wallet_interface/chain_provider.rb +0 -37
- data/lib/bsv/wallet_interface/errors/invalid_hmac_error.rb +0 -11
- data/lib/bsv/wallet_interface/errors/invalid_parameter_error.rb +0 -14
- data/lib/bsv/wallet_interface/errors/invalid_signature_error.rb +0 -11
- data/lib/bsv/wallet_interface/errors/unsupported_action_error.rb +0 -11
- data/lib/bsv/wallet_interface/errors/wallet_error.rb +0 -14
- data/lib/bsv/wallet_interface/file_store.rb +0 -222
- data/lib/bsv/wallet_interface/interface.rb +0 -384
- data/lib/bsv/wallet_interface/key_deriver.rb +0 -144
- data/lib/bsv/wallet_interface/local_proof_store.rb +0 -42
- data/lib/bsv/wallet_interface/memory_store.rb +0 -149
- data/lib/bsv/wallet_interface/null_chain_provider.rb +0 -22
- data/lib/bsv/wallet_interface/proof_store.rb +0 -32
- data/lib/bsv/wallet_interface/proto_wallet.rb +0 -361
- data/lib/bsv/wallet_interface/storage_adapter.rb +0 -71
- data/lib/bsv/wallet_interface/validators.rb +0 -126
- data/lib/bsv/wallet_interface/version.rb +0 -7
- data/lib/bsv/wallet_interface/wallet_client.rb +0 -874
- data/lib/bsv/wallet_interface/wire/reader.rb +0 -238
- data/lib/bsv/wallet_interface/wire/serializer.rb +0 -1993
- data/lib/bsv/wallet_interface/wire/writer.rb +0 -214
- data/lib/bsv/wallet_interface/wire.rb +0 -19
- data/lib/bsv/wallet_interface.rb +0 -31
- data/lib/bsv-attest.rb +0 -4
- data/lib/bsv-wallet.rb +0 -4
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module BSV
|
|
4
|
-
module Wallet
|
|
5
|
-
# BRC-42/43 key derivation for the wallet interface.
|
|
6
|
-
#
|
|
7
|
-
# Derives child keys from a root private key using BKDS (BSV Key Derivation
|
|
8
|
-
# Scheme). Supports protocol IDs, key IDs, counterparties, and security
|
|
9
|
-
# levels as defined in BRC-43.
|
|
10
|
-
class KeyDeriver
|
|
11
|
-
ANYONE_BN = OpenSSL::BN.new(1)
|
|
12
|
-
|
|
13
|
-
attr_reader :root_key
|
|
14
|
-
|
|
15
|
-
# @param root_key [BSV::Primitives::PrivateKey, String] a private key or 'anyone'
|
|
16
|
-
def initialize(root_key)
|
|
17
|
-
@root_key = if root_key == 'anyone'
|
|
18
|
-
BSV::Primitives::PrivateKey.new(ANYONE_BN)
|
|
19
|
-
elsif root_key.is_a?(BSV::Primitives::PrivateKey)
|
|
20
|
-
root_key
|
|
21
|
-
else
|
|
22
|
-
raise ArgumentError, "expected a BSV::Primitives::PrivateKey or 'anyone', got #{root_key.class}"
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Returns the identity public key as a hex string.
|
|
27
|
-
# @return [String] 66-character compressed public key hex
|
|
28
|
-
def identity_key
|
|
29
|
-
@root_key.public_key.to_hex
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Derives a public key using BRC-42 key derivation.
|
|
33
|
-
#
|
|
34
|
-
# @param protocol_id [Array] [security_level, protocol_name]
|
|
35
|
-
# @param key_id [String] key identifier
|
|
36
|
-
# @param counterparty [String] public key hex, 'self', or 'anyone'
|
|
37
|
-
# @param for_self [Boolean] derive from own identity rather than counterparty's
|
|
38
|
-
# @return [BSV::Primitives::PublicKey]
|
|
39
|
-
def derive_public_key(protocol_id, key_id, counterparty, for_self: false)
|
|
40
|
-
Validators.validate_protocol_id!(protocol_id)
|
|
41
|
-
Validators.validate_key_id!(key_id)
|
|
42
|
-
invoice = compute_invoice_number(protocol_id, key_id)
|
|
43
|
-
counterparty_pub = resolve_counterparty(counterparty)
|
|
44
|
-
|
|
45
|
-
if for_self
|
|
46
|
-
@root_key.derive_child(counterparty_pub, invoice).public_key
|
|
47
|
-
else
|
|
48
|
-
counterparty_pub.derive_child(@root_key, invoice)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Derives a private key using BRC-42 key derivation.
|
|
53
|
-
#
|
|
54
|
-
# @param protocol_id [Array] [security_level, protocol_name]
|
|
55
|
-
# @param key_id [String] key identifier
|
|
56
|
-
# @param counterparty [String] public key hex, 'self', or 'anyone'
|
|
57
|
-
# @return [BSV::Primitives::PrivateKey]
|
|
58
|
-
def derive_private_key(protocol_id, key_id, counterparty)
|
|
59
|
-
Validators.validate_protocol_id!(protocol_id)
|
|
60
|
-
Validators.validate_key_id!(key_id)
|
|
61
|
-
invoice = compute_invoice_number(protocol_id, key_id)
|
|
62
|
-
counterparty_pub = resolve_counterparty(counterparty)
|
|
63
|
-
@root_key.derive_child(counterparty_pub, invoice)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Derives a symmetric key for encryption/HMAC operations.
|
|
67
|
-
#
|
|
68
|
-
# Uses ECDH between the derived private and public child keys to
|
|
69
|
-
# produce a shared secret, then uses the X-coordinate as the key.
|
|
70
|
-
#
|
|
71
|
-
# @param protocol_id [Array] [security_level, protocol_name]
|
|
72
|
-
# @param key_id [String] key identifier
|
|
73
|
-
# @param counterparty [String] public key hex, 'self', or 'anyone'
|
|
74
|
-
# @return [BSV::Primitives::SymmetricKey]
|
|
75
|
-
def derive_symmetric_key(protocol_id, key_id, counterparty)
|
|
76
|
-
Validators.validate_protocol_id!(protocol_id)
|
|
77
|
-
Validators.validate_key_id!(key_id)
|
|
78
|
-
invoice = compute_invoice_number(protocol_id, key_id)
|
|
79
|
-
counterparty_pub = resolve_counterparty(counterparty)
|
|
80
|
-
|
|
81
|
-
derived_private = @root_key.derive_child(counterparty_pub, invoice)
|
|
82
|
-
derived_public = counterparty_pub.derive_child(@root_key, invoice)
|
|
83
|
-
|
|
84
|
-
BSV::Primitives::SymmetricKey.from_ecdh(derived_private, derived_public)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Reveals the ECDH shared secret between this wallet and a counterparty.
|
|
88
|
-
# Used for BRC-69 Method 1 (counterparty key linkage).
|
|
89
|
-
#
|
|
90
|
-
# @param counterparty [String] public key hex (not 'self')
|
|
91
|
-
# @return [String] compressed shared secret bytes
|
|
92
|
-
def reveal_counterparty_secret(counterparty)
|
|
93
|
-
raise InvalidParameterError.new('counterparty', 'not "self" for key linkage revelation') if counterparty == 'self'
|
|
94
|
-
|
|
95
|
-
counterparty_pub = resolve_counterparty(counterparty)
|
|
96
|
-
@root_key.derive_shared_secret(counterparty_pub).compressed
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Reveals the specific key offset for a particular derived key.
|
|
100
|
-
# Used for BRC-69 Method 2 (specific key linkage).
|
|
101
|
-
#
|
|
102
|
-
# @param counterparty [String] public key hex
|
|
103
|
-
# @param protocol_id [Array] [security_level, protocol_name]
|
|
104
|
-
# @param key_id [String] key identifier
|
|
105
|
-
# @return [String] HMAC-SHA256 bytes (the key offset)
|
|
106
|
-
def reveal_specific_secret(counterparty, protocol_id, key_id)
|
|
107
|
-
Validators.validate_protocol_id!(protocol_id)
|
|
108
|
-
Validators.validate_key_id!(key_id)
|
|
109
|
-
counterparty_pub = resolve_counterparty(counterparty)
|
|
110
|
-
shared = @root_key.derive_shared_secret(counterparty_pub)
|
|
111
|
-
invoice = compute_invoice_number(protocol_id, key_id)
|
|
112
|
-
BSV::Primitives::Digest.hmac_sha256(shared.compressed, invoice.encode('UTF-8'))
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
private
|
|
116
|
-
|
|
117
|
-
# Resolves a counterparty identifier to a PublicKey.
|
|
118
|
-
#
|
|
119
|
-
# @param counterparty [String] 'self', 'anyone', or a hex public key
|
|
120
|
-
# @return [BSV::Primitives::PublicKey]
|
|
121
|
-
def resolve_counterparty(counterparty)
|
|
122
|
-
case counterparty
|
|
123
|
-
when 'self'
|
|
124
|
-
@root_key.public_key
|
|
125
|
-
when 'anyone'
|
|
126
|
-
BSV::Primitives::PrivateKey.new(ANYONE_BN).public_key
|
|
127
|
-
else
|
|
128
|
-
Validators.validate_counterparty!(counterparty)
|
|
129
|
-
BSV::Primitives::PublicKey.from_hex(counterparty)
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Computes the invoice number from a protocol ID and key ID.
|
|
134
|
-
# Format: "#{security_level}-#{protocol_name}-#{key_id}"
|
|
135
|
-
#
|
|
136
|
-
# @param protocol_id [Array] [security_level, protocol_name]
|
|
137
|
-
# @param key_id [String]
|
|
138
|
-
# @return [String]
|
|
139
|
-
def compute_invoice_number(protocol_id, key_id)
|
|
140
|
-
"#{protocol_id[0]}-#{protocol_id[1]}-#{key_id}"
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module BSV
|
|
4
|
-
module Wallet
|
|
5
|
-
# Default proof store — persists serialised merkle proofs via a
|
|
6
|
-
# {StorageAdapter}. Requires no external services.
|
|
7
|
-
#
|
|
8
|
-
# @example Using the default local proof store
|
|
9
|
-
# store = BSV::Wallet::MemoryStore.new
|
|
10
|
-
# proofs = BSV::Wallet::LocalProofStore.new(store)
|
|
11
|
-
# proofs.store_proof(txid_hex, merkle_path)
|
|
12
|
-
# proof = proofs.resolve_proof(txid_hex)
|
|
13
|
-
class LocalProofStore
|
|
14
|
-
include ProofStore
|
|
15
|
-
|
|
16
|
-
# @param storage [StorageAdapter] the underlying persistence adapter
|
|
17
|
-
def initialize(storage)
|
|
18
|
-
@storage = storage
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Serialise and store a merkle proof.
|
|
22
|
-
#
|
|
23
|
-
# @param txid [String] hex transaction ID
|
|
24
|
-
# @param merkle_path [BSV::Transaction::MerklePath] the proof to store
|
|
25
|
-
# @return [void]
|
|
26
|
-
def store_proof(txid, merkle_path)
|
|
27
|
-
@storage.store_proof(txid, merkle_path.to_hex)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Resolve and deserialise a merkle proof.
|
|
31
|
-
#
|
|
32
|
-
# @param txid [String] hex transaction ID
|
|
33
|
-
# @return [BSV::Transaction::MerklePath, nil] the proof, or nil if unknown
|
|
34
|
-
def resolve_proof(txid)
|
|
35
|
-
bump_hex = @storage.find_proof(txid)
|
|
36
|
-
return unless bump_hex
|
|
37
|
-
|
|
38
|
-
BSV::Transaction::MerklePath.from_hex(bump_hex)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module BSV
|
|
4
|
-
module Wallet
|
|
5
|
-
# In-memory storage adapter for testing.
|
|
6
|
-
#
|
|
7
|
-
# Stores actions, outputs, and certificates in plain Ruby arrays.
|
|
8
|
-
# Not thread-safe; intended for test use only.
|
|
9
|
-
class MemoryStore
|
|
10
|
-
include StorageAdapter
|
|
11
|
-
|
|
12
|
-
def initialize
|
|
13
|
-
@actions = []
|
|
14
|
-
@outputs = []
|
|
15
|
-
@certificates = []
|
|
16
|
-
@proofs = {}
|
|
17
|
-
@transactions = {}
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def store_action(action_data)
|
|
21
|
-
@actions << action_data
|
|
22
|
-
action_data
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def find_actions(query)
|
|
26
|
-
apply_pagination(filter_actions(query), query)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def count_actions(query)
|
|
30
|
-
filter_actions(query).length
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def store_output(output_data)
|
|
34
|
-
@outputs << output_data
|
|
35
|
-
output_data
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def find_outputs(query)
|
|
39
|
-
apply_pagination(filter_outputs(query), query)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def count_outputs(query)
|
|
43
|
-
filter_outputs(query).length
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def delete_output(outpoint)
|
|
47
|
-
idx = @outputs.index { |o| o[:outpoint] == outpoint }
|
|
48
|
-
return false unless idx
|
|
49
|
-
|
|
50
|
-
@outputs.delete_at(idx)
|
|
51
|
-
true
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def store_certificate(cert_data)
|
|
55
|
-
@certificates << cert_data
|
|
56
|
-
cert_data
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def find_certificates(query)
|
|
60
|
-
apply_pagination(filter_certificates(query), query)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def count_certificates(query)
|
|
64
|
-
filter_certificates(query).length
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def store_proof(txid, bump_hex)
|
|
68
|
-
@proofs[txid] = bump_hex
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def find_proof(txid)
|
|
72
|
-
@proofs[txid]
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def store_transaction(txid, tx_hex)
|
|
76
|
-
@transactions[txid] = tx_hex
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def find_transaction(txid)
|
|
80
|
-
@transactions[txid]
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def delete_certificate(type:, serial_number:, certifier:)
|
|
84
|
-
idx = @certificates.index do |c|
|
|
85
|
-
c[:type] == type && c[:serial_number] == serial_number && c[:certifier] == certifier
|
|
86
|
-
end
|
|
87
|
-
return false unless idx
|
|
88
|
-
|
|
89
|
-
@certificates.delete_at(idx)
|
|
90
|
-
true
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
private
|
|
94
|
-
|
|
95
|
-
def filter_actions(query)
|
|
96
|
-
results = @actions
|
|
97
|
-
return results unless query[:labels]
|
|
98
|
-
|
|
99
|
-
mode = query[:label_query_mode] || 'any'
|
|
100
|
-
results.select do |a|
|
|
101
|
-
action_labels = a[:labels] || []
|
|
102
|
-
if mode == 'all'
|
|
103
|
-
(query[:labels] - action_labels).empty?
|
|
104
|
-
else
|
|
105
|
-
(query[:labels] & action_labels).any?
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def filter_outputs(query)
|
|
111
|
-
results = @outputs
|
|
112
|
-
results = results.select { |o| o[:outpoint] == query[:outpoint] } if query[:outpoint]
|
|
113
|
-
results = results.select { |o| o[:basket] == query[:basket] } if query[:basket]
|
|
114
|
-
if query[:tags]
|
|
115
|
-
mode = query[:tag_query_mode] || 'any'
|
|
116
|
-
results = results.select do |o|
|
|
117
|
-
output_tags = o[:tags] || []
|
|
118
|
-
if mode == 'all'
|
|
119
|
-
(query[:tags] - output_tags).empty?
|
|
120
|
-
else
|
|
121
|
-
(query[:tags] & output_tags).any?
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
query[:include_spent] ? results : results.reject { |o| o[:spendable] == false }
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def filter_certificates(query)
|
|
129
|
-
results = @certificates
|
|
130
|
-
results = results.select { |c| query[:certifiers].include?(c[:certifier]) } if query[:certifiers]
|
|
131
|
-
results = results.select { |c| query[:types].include?(c[:type]) } if query[:types]
|
|
132
|
-
results = results.select { |c| c[:subject] == query[:subject] } if query[:subject]
|
|
133
|
-
if query[:attributes]
|
|
134
|
-
results = results.select do |c|
|
|
135
|
-
fields = c[:fields] || {}
|
|
136
|
-
query[:attributes].all? { |k, v| fields[k] == v || fields[k.to_sym] == v }
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
results
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def apply_pagination(results, query)
|
|
143
|
-
offset = query[:offset] || 0
|
|
144
|
-
limit = query[:limit] || 10
|
|
145
|
-
results[offset, limit] || []
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module BSV
|
|
4
|
-
module Wallet
|
|
5
|
-
# Default chain provider that raises for all blockchain queries.
|
|
6
|
-
#
|
|
7
|
-
# Used when a WalletClient is constructed without a chain provider,
|
|
8
|
-
# allowing the wallet to function for transaction and crypto operations
|
|
9
|
-
# without requiring a blockchain connection.
|
|
10
|
-
class NullChainProvider
|
|
11
|
-
include ChainProvider
|
|
12
|
-
|
|
13
|
-
def get_height
|
|
14
|
-
raise UnsupportedActionError, 'get_height (no chain provider configured)'
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def get_header(_height)
|
|
18
|
-
raise UnsupportedActionError, 'get_header_for_height (no chain provider configured)'
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module BSV
|
|
4
|
-
module Wallet
|
|
5
|
-
# Duck-typed interface for merkle proof persistence.
|
|
6
|
-
#
|
|
7
|
-
# Include this module in proof store implementations and override all
|
|
8
|
-
# methods. The default implementations raise NotImplementedError.
|
|
9
|
-
#
|
|
10
|
-
# Two implementations ship with the SDK:
|
|
11
|
-
# - {LocalProofStore} — persists proofs via a {StorageAdapter} (default)
|
|
12
|
-
# - Future: ChaintracksProofStore — resolves proofs from an external service
|
|
13
|
-
module ProofStore
|
|
14
|
-
# Store a merkle proof for a transaction.
|
|
15
|
-
#
|
|
16
|
-
# @param _txid [String] hex transaction ID
|
|
17
|
-
# @param _merkle_path [BSV::Transaction::MerklePath] the proof to store
|
|
18
|
-
# @return [void]
|
|
19
|
-
def store_proof(_txid, _merkle_path)
|
|
20
|
-
raise NotImplementedError, "#{self.class}#store_proof not implemented"
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Resolve a merkle proof for a transaction.
|
|
24
|
-
#
|
|
25
|
-
# @param _txid [String] hex transaction ID
|
|
26
|
-
# @return [BSV::Transaction::MerklePath, nil] the proof, or nil if unknown
|
|
27
|
-
def resolve_proof(_txid)
|
|
28
|
-
raise NotImplementedError, "#{self.class}#resolve_proof not implemented"
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|