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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +406 -146
  3. data/lib/bsv/identity/client.rb +11 -5
  4. data/lib/bsv/overlay/topic_broadcaster.rb +1 -3
  5. data/lib/bsv/primitives/openssl_ec_shim.rb +8 -4
  6. data/lib/bsv/primitives/secp256k1.rb +11 -1
  7. data/lib/bsv/transaction/beef.rb +45 -38
  8. data/lib/bsv/transaction/merkle_path.rb +64 -0
  9. data/lib/bsv/transaction/transaction.rb +3 -2
  10. data/lib/bsv/version.rb +1 -1
  11. metadata +2 -32
  12. data/lib/bsv/attest/configuration.rb +0 -9
  13. data/lib/bsv/attest/response.rb +0 -19
  14. data/lib/bsv/attest/verification_error.rb +0 -7
  15. data/lib/bsv/attest/version.rb +0 -7
  16. data/lib/bsv/attest.rb +0 -71
  17. data/lib/bsv/wallet_interface/chain_provider.rb +0 -37
  18. data/lib/bsv/wallet_interface/errors/invalid_hmac_error.rb +0 -11
  19. data/lib/bsv/wallet_interface/errors/invalid_parameter_error.rb +0 -14
  20. data/lib/bsv/wallet_interface/errors/invalid_signature_error.rb +0 -11
  21. data/lib/bsv/wallet_interface/errors/unsupported_action_error.rb +0 -11
  22. data/lib/bsv/wallet_interface/errors/wallet_error.rb +0 -14
  23. data/lib/bsv/wallet_interface/file_store.rb +0 -222
  24. data/lib/bsv/wallet_interface/interface.rb +0 -384
  25. data/lib/bsv/wallet_interface/key_deriver.rb +0 -144
  26. data/lib/bsv/wallet_interface/local_proof_store.rb +0 -42
  27. data/lib/bsv/wallet_interface/memory_store.rb +0 -149
  28. data/lib/bsv/wallet_interface/null_chain_provider.rb +0 -22
  29. data/lib/bsv/wallet_interface/proof_store.rb +0 -32
  30. data/lib/bsv/wallet_interface/proto_wallet.rb +0 -361
  31. data/lib/bsv/wallet_interface/storage_adapter.rb +0 -71
  32. data/lib/bsv/wallet_interface/validators.rb +0 -126
  33. data/lib/bsv/wallet_interface/version.rb +0 -7
  34. data/lib/bsv/wallet_interface/wallet_client.rb +0 -874
  35. data/lib/bsv/wallet_interface/wire/reader.rb +0 -238
  36. data/lib/bsv/wallet_interface/wire/serializer.rb +0 -1993
  37. data/lib/bsv/wallet_interface/wire/writer.rb +0 -214
  38. data/lib/bsv/wallet_interface/wire.rb +0 -19
  39. data/lib/bsv/wallet_interface.rb +0 -31
  40. data/lib/bsv-attest.rb +0 -4
  41. 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