bsv-sdk 0.2.1 → 0.3.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/lib/bsv/network/broadcast_response.rb +1 -2
- data/lib/bsv/primitives/bsm.rb +2 -6
- data/lib/bsv/primitives/curve.rb +1 -2
- data/lib/bsv/primitives/encrypted_message.rb +100 -0
- data/lib/bsv/primitives/extended_key.rb +1 -2
- data/lib/bsv/primitives/key_shares.rb +83 -0
- data/lib/bsv/primitives/mnemonic.rb +1 -3
- data/lib/bsv/primitives/point_in_finite_field.rb +72 -0
- data/lib/bsv/primitives/polynomial.rb +95 -0
- data/lib/bsv/primitives/private_key.rb +100 -3
- data/lib/bsv/primitives/signed_message.rb +104 -0
- data/lib/bsv/primitives/symmetric_key.rb +128 -0
- data/lib/bsv/primitives.rb +18 -12
- data/lib/bsv/script/interpreter/interpreter.rb +1 -3
- data/lib/bsv/script/interpreter/operations/bitwise.rb +1 -3
- data/lib/bsv/script/interpreter/operations/crypto.rb +3 -9
- data/lib/bsv/script/interpreter/operations/flow_control.rb +2 -6
- data/lib/bsv/script/interpreter/operations/splice.rb +1 -3
- data/lib/bsv/script/interpreter/script_number.rb +2 -7
- data/lib/bsv/script/script.rb +252 -1
- data/lib/bsv/transaction/beef.rb +1 -4
- data/lib/bsv/transaction/transaction.rb +123 -45
- data/lib/bsv/transaction/transaction_input.rb +1 -2
- data/lib/bsv/transaction/transaction_output.rb +1 -2
- data/lib/bsv/transaction/var_int.rb +4 -16
- data/lib/bsv/transaction.rb +14 -14
- data/lib/bsv/version.rb +1 -1
- data/lib/bsv/wallet_interface/errors/invalid_hmac_error.rb +11 -0
- data/lib/bsv/wallet_interface/errors/invalid_parameter_error.rb +14 -0
- data/lib/bsv/wallet_interface/errors/invalid_signature_error.rb +11 -0
- data/lib/bsv/wallet_interface/errors/unsupported_action_error.rb +11 -0
- data/lib/bsv/wallet_interface/errors/wallet_error.rb +14 -0
- data/lib/bsv/wallet_interface/interface.rb +384 -0
- data/lib/bsv/wallet_interface/key_deriver.rb +142 -0
- data/lib/bsv/wallet_interface/memory_store.rb +115 -0
- data/lib/bsv/wallet_interface/proto_wallet.rb +361 -0
- data/lib/bsv/wallet_interface/storage_adapter.rb +51 -0
- data/lib/bsv/wallet_interface/validators.rb +126 -0
- data/lib/bsv/wallet_interface/version.rb +7 -0
- data/lib/bsv/wallet_interface/wallet_client.rb +486 -0
- data/lib/bsv/wallet_interface.rb +25 -0
- data/lib/bsv-wallet.rb +4 -0
- metadata +24 -3
- /data/{LICENCE → LICENSE} +0 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Wallet
|
|
5
|
+
# BRC-100 Wallet Interface
|
|
6
|
+
#
|
|
7
|
+
# Defines the 28 methods of the standard BSV wallet-to-application interface.
|
|
8
|
+
# Include this module and override the methods your implementation supports.
|
|
9
|
+
# Unimplemented methods raise {UnsupportedActionError}.
|
|
10
|
+
module Interface
|
|
11
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
|
12
|
+
|
|
13
|
+
# --- Transaction Operations ---
|
|
14
|
+
|
|
15
|
+
# Creates a new Bitcoin transaction with metadata and labels.
|
|
16
|
+
#
|
|
17
|
+
# @param args [Hash] transaction parameters
|
|
18
|
+
# @option args [String] :description (required) 5-50 char description
|
|
19
|
+
# @option args [Array<Integer>] :input_beef BEEF data for inputs
|
|
20
|
+
# @option args [Array<Hash>] :inputs input objects with :outpoint, :unlocking_script, :input_description
|
|
21
|
+
# @option args [Array<Hash>] :outputs output objects with :locking_script, :satoshis, :output_description
|
|
22
|
+
# @option args [Integer] :lock_time optional lock time
|
|
23
|
+
# @option args [Integer] :version optional transaction version
|
|
24
|
+
# @option args [Array<String>] :labels optional transaction labels
|
|
25
|
+
# @option args [Hash] :options optional processing options
|
|
26
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
27
|
+
# @return [Hash] with :txid, :tx, :no_send_change, :send_with_results, :signable_transaction
|
|
28
|
+
def create_action(args, originator: nil)
|
|
29
|
+
raise UnsupportedActionError, 'create_action'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Signs a previously created transaction.
|
|
33
|
+
#
|
|
34
|
+
# @param args [Hash] signing parameters
|
|
35
|
+
# @option args [Hash] :spends map of input indexes to unlocking scripts
|
|
36
|
+
# @option args [String] :reference base64 reference from create_action
|
|
37
|
+
# @option args [Hash] :options optional processing options
|
|
38
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
39
|
+
# @return [Hash] with :txid, :tx, :send_with_results
|
|
40
|
+
def sign_action(args, originator: nil)
|
|
41
|
+
raise UnsupportedActionError, 'sign_action'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Aborts an incomplete transaction.
|
|
45
|
+
#
|
|
46
|
+
# @param args [Hash]
|
|
47
|
+
# @option args [String] :reference base64 reference to abort
|
|
48
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
49
|
+
# @return [Hash] { aborted: true }
|
|
50
|
+
def abort_action(args, originator: nil)
|
|
51
|
+
raise UnsupportedActionError, 'abort_action'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Lists transactions matching the specified labels.
|
|
55
|
+
#
|
|
56
|
+
# @param args [Hash] query parameters
|
|
57
|
+
# @option args [Array<String>] :labels (required) labels to filter by
|
|
58
|
+
# @option args [String] :label_query_mode 'any' or 'all'
|
|
59
|
+
# @option args [Boolean] :include_labels include labels in results
|
|
60
|
+
# @option args [Boolean] :include_inputs include input details
|
|
61
|
+
# @option args [Boolean] :include_outputs include output details
|
|
62
|
+
# @option args [Integer] :limit max results (default 10, max 10000)
|
|
63
|
+
# @option args [Integer] :offset number to skip
|
|
64
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
65
|
+
# @return [Hash] with :total_actions, :actions
|
|
66
|
+
def list_actions(args, originator: nil)
|
|
67
|
+
raise UnsupportedActionError, 'list_actions'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Accepts an incoming transaction for internalization.
|
|
71
|
+
#
|
|
72
|
+
# @param args [Hash]
|
|
73
|
+
# @option args [Array<Integer>] :tx Atomic BEEF-formatted transaction
|
|
74
|
+
# @option args [Array<Hash>] :outputs metadata about outputs
|
|
75
|
+
# @option args [String] :description 5-50 char description
|
|
76
|
+
# @option args [Array<String>] :labels optional labels
|
|
77
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
78
|
+
# @return [Hash] { accepted: true }
|
|
79
|
+
def internalize_action(args, originator: nil)
|
|
80
|
+
raise UnsupportedActionError, 'internalize_action'
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Lists spendable outputs in a basket.
|
|
84
|
+
#
|
|
85
|
+
# @param args [Hash]
|
|
86
|
+
# @option args [String] :basket (required) basket name
|
|
87
|
+
# @option args [Array<String>] :tags optional tag filter
|
|
88
|
+
# @option args [String] :tag_query_mode 'any' or 'all'
|
|
89
|
+
# @option args [String] :include 'locking scripts' or 'entire transactions'
|
|
90
|
+
# @option args [Integer] :limit max results
|
|
91
|
+
# @option args [Integer] :offset number to skip
|
|
92
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
93
|
+
# @return [Hash] with :total_outputs, :outputs
|
|
94
|
+
def list_outputs(args, originator: nil)
|
|
95
|
+
raise UnsupportedActionError, 'list_outputs'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Releases an output from basket tracking.
|
|
99
|
+
#
|
|
100
|
+
# @param args [Hash]
|
|
101
|
+
# @option args [String] :basket basket name
|
|
102
|
+
# @option args [String] :output outpoint string
|
|
103
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
104
|
+
# @return [Hash] { relinquished: true }
|
|
105
|
+
def relinquish_output(args, originator: nil)
|
|
106
|
+
raise UnsupportedActionError, 'relinquish_output'
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# --- Public Key Management ---
|
|
110
|
+
|
|
111
|
+
# Retrieves a derived or identity public key.
|
|
112
|
+
#
|
|
113
|
+
# @param args [Hash]
|
|
114
|
+
# @option args [Boolean] :identity_key if true, return the identity key
|
|
115
|
+
# @option args [Array] :protocol_id [security_level, protocol_name]
|
|
116
|
+
# @option args [String] :key_id key identifier
|
|
117
|
+
# @option args [String] :counterparty public key hex, 'self', or 'anyone'
|
|
118
|
+
# @option args [Boolean] :for_self derive from own identity
|
|
119
|
+
# @option args [Boolean] :privileged privileged mode
|
|
120
|
+
# @option args [String] :privileged_reason reason for privileged access
|
|
121
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
122
|
+
# @return [Hash] { public_key: String }
|
|
123
|
+
def get_public_key(args, originator: nil)
|
|
124
|
+
raise UnsupportedActionError, 'get_public_key'
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Reveals key linkage between self and a counterparty to a verifier.
|
|
128
|
+
#
|
|
129
|
+
# @param args [Hash]
|
|
130
|
+
# @option args [String] :counterparty counterparty public key hex
|
|
131
|
+
# @option args [String] :verifier verifier public key hex
|
|
132
|
+
# @option args [Boolean] :privileged privileged mode
|
|
133
|
+
# @option args [String] :privileged_reason reason for privileged access
|
|
134
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
135
|
+
# @return [Hash] with :prover, :verifier, :counterparty, :revelation_time, :encrypted_linkage, :encrypted_linkage_proof
|
|
136
|
+
def reveal_counterparty_key_linkage(args, originator: nil)
|
|
137
|
+
raise UnsupportedActionError, 'reveal_counterparty_key_linkage'
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Reveals specific key linkage for a particular interaction.
|
|
141
|
+
#
|
|
142
|
+
# @param args [Hash]
|
|
143
|
+
# @option args [String] :counterparty counterparty public key hex
|
|
144
|
+
# @option args [String] :verifier verifier public key hex
|
|
145
|
+
# @option args [Array] :protocol_id [security_level, protocol_name]
|
|
146
|
+
# @option args [String] :key_id key identifier
|
|
147
|
+
# @option args [Boolean] :privileged privileged mode
|
|
148
|
+
# @option args [String] :privileged_reason reason for privileged access
|
|
149
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
150
|
+
# @return [Hash] with :prover, :verifier, :counterparty, :protocol_id, :key_id, :encrypted_linkage, :encrypted_linkage_proof, :proof_type
|
|
151
|
+
def reveal_specific_key_linkage(args, originator: nil)
|
|
152
|
+
raise UnsupportedActionError, 'reveal_specific_key_linkage'
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# --- Cryptography Operations ---
|
|
156
|
+
|
|
157
|
+
# Encrypts data using a derived symmetric key.
|
|
158
|
+
#
|
|
159
|
+
# @param args [Hash]
|
|
160
|
+
# @option args [Array<Integer>] :plaintext byte array to encrypt
|
|
161
|
+
# @option args [Array] :protocol_id [security_level, protocol_name]
|
|
162
|
+
# @option args [String] :key_id key identifier
|
|
163
|
+
# @option args [String] :counterparty public key hex, 'self', or 'anyone'
|
|
164
|
+
# @option args [Boolean] :privileged privileged mode
|
|
165
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
166
|
+
# @return [Hash] { ciphertext: Array<Integer> }
|
|
167
|
+
def encrypt(args, originator: nil)
|
|
168
|
+
raise UnsupportedActionError, 'encrypt'
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Decrypts data using a derived symmetric key.
|
|
172
|
+
#
|
|
173
|
+
# @param args [Hash]
|
|
174
|
+
# @option args [Array<Integer>] :ciphertext byte array to decrypt
|
|
175
|
+
# @option args [Array] :protocol_id [security_level, protocol_name]
|
|
176
|
+
# @option args [String] :key_id key identifier
|
|
177
|
+
# @option args [String] :counterparty public key hex, 'self', or 'anyone'
|
|
178
|
+
# @option args [Boolean] :privileged privileged mode
|
|
179
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
180
|
+
# @return [Hash] { plaintext: Array<Integer> }
|
|
181
|
+
def decrypt(args, originator: nil)
|
|
182
|
+
raise UnsupportedActionError, 'decrypt'
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Creates an HMAC using a derived symmetric key.
|
|
186
|
+
#
|
|
187
|
+
# @param args [Hash]
|
|
188
|
+
# @option args [Array<Integer>] :data byte array
|
|
189
|
+
# @option args [Array] :protocol_id [security_level, protocol_name]
|
|
190
|
+
# @option args [String] :key_id key identifier
|
|
191
|
+
# @option args [String] :counterparty public key hex, 'self', or 'anyone'
|
|
192
|
+
# @option args [Boolean] :privileged privileged mode
|
|
193
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
194
|
+
# @return [Hash] { hmac: Array<Integer> }
|
|
195
|
+
def create_hmac(args, originator: nil)
|
|
196
|
+
raise UnsupportedActionError, 'create_hmac'
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Verifies an HMAC using a derived symmetric key.
|
|
200
|
+
#
|
|
201
|
+
# @param args [Hash]
|
|
202
|
+
# @option args [Array<Integer>] :data byte array
|
|
203
|
+
# @option args [Array<Integer>] :hmac HMAC to verify
|
|
204
|
+
# @option args [Array] :protocol_id [security_level, protocol_name]
|
|
205
|
+
# @option args [String] :key_id key identifier
|
|
206
|
+
# @option args [String] :counterparty public key hex, 'self', or 'anyone'
|
|
207
|
+
# @option args [Boolean] :privileged privileged mode
|
|
208
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
209
|
+
# @return [Hash] { valid: true }
|
|
210
|
+
def verify_hmac(args, originator: nil)
|
|
211
|
+
raise UnsupportedActionError, 'verify_hmac'
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Creates a digital signature using a derived private key.
|
|
215
|
+
#
|
|
216
|
+
# @param args [Hash]
|
|
217
|
+
# @option args [Array<Integer>] :data data to sign
|
|
218
|
+
# @option args [Array<Integer>] :hash_to_directly_sign pre-hashed data to sign
|
|
219
|
+
# @option args [Array] :protocol_id [security_level, protocol_name]
|
|
220
|
+
# @option args [String] :key_id key identifier
|
|
221
|
+
# @option args [String] :counterparty public key hex, 'self', or 'anyone'
|
|
222
|
+
# @option args [Boolean] :privileged privileged mode
|
|
223
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
224
|
+
# @return [Hash] { signature: Array<Integer> }
|
|
225
|
+
def create_signature(args, originator: nil)
|
|
226
|
+
raise UnsupportedActionError, 'create_signature'
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Verifies a digital signature using a derived public key.
|
|
230
|
+
#
|
|
231
|
+
# @param args [Hash]
|
|
232
|
+
# @option args [Array<Integer>] :data original data
|
|
233
|
+
# @option args [Array<Integer>] :hash_to_directly_verify pre-hashed data
|
|
234
|
+
# @option args [Array<Integer>] :signature DER-encoded signature
|
|
235
|
+
# @option args [Array] :protocol_id [security_level, protocol_name]
|
|
236
|
+
# @option args [String] :key_id key identifier
|
|
237
|
+
# @option args [String] :counterparty public key hex, 'self', or 'anyone'
|
|
238
|
+
# @option args [Boolean] :for_self verify own signature
|
|
239
|
+
# @option args [Boolean] :privileged privileged mode
|
|
240
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
241
|
+
# @return [Hash] { valid: true }
|
|
242
|
+
def verify_signature(args, originator: nil)
|
|
243
|
+
raise UnsupportedActionError, 'verify_signature'
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# --- Identity and Certificate Management ---
|
|
247
|
+
|
|
248
|
+
# Acquires an identity certificate.
|
|
249
|
+
#
|
|
250
|
+
# @param args [Hash]
|
|
251
|
+
# @option args [String] :type certificate type (base64)
|
|
252
|
+
# @option args [String] :certifier certifier public key hex
|
|
253
|
+
# @option args [String] :acquisition_protocol 'direct' or 'issuance'
|
|
254
|
+
# @option args [Hash] :fields certificate fields
|
|
255
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
256
|
+
# @return [Hash] certificate data
|
|
257
|
+
def acquire_certificate(args, originator: nil)
|
|
258
|
+
raise UnsupportedActionError, 'acquire_certificate'
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Lists identity certificates.
|
|
262
|
+
#
|
|
263
|
+
# @param args [Hash]
|
|
264
|
+
# @option args [Array<String>] :certifiers certifier public keys
|
|
265
|
+
# @option args [Array<String>] :types certificate types
|
|
266
|
+
# @option args [Integer] :limit max results
|
|
267
|
+
# @option args [Integer] :offset number to skip
|
|
268
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
269
|
+
# @return [Hash] with :total_certificates, :certificates
|
|
270
|
+
def list_certificates(args, originator: nil)
|
|
271
|
+
raise UnsupportedActionError, 'list_certificates'
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Proves select fields of a certificate to a verifier.
|
|
275
|
+
#
|
|
276
|
+
# @param args [Hash]
|
|
277
|
+
# @option args [Hash] :certificate the certificate to prove
|
|
278
|
+
# @option args [Array<String>] :fields_to_reveal field names to reveal
|
|
279
|
+
# @option args [String] :verifier verifier public key hex
|
|
280
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
281
|
+
# @return [Hash] { keyring_for_verifier: Hash }
|
|
282
|
+
def prove_certificate(args, originator: nil)
|
|
283
|
+
raise UnsupportedActionError, 'prove_certificate'
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Removes a certificate from the wallet.
|
|
287
|
+
#
|
|
288
|
+
# @param args [Hash]
|
|
289
|
+
# @option args [String] :type certificate type
|
|
290
|
+
# @option args [String] :serial_number certificate serial number
|
|
291
|
+
# @option args [String] :certifier certifier public key hex
|
|
292
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
293
|
+
# @return [Hash] { relinquished: true }
|
|
294
|
+
def relinquish_certificate(args, originator: nil)
|
|
295
|
+
raise UnsupportedActionError, 'relinquish_certificate'
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Discovers certificates by identity key.
|
|
299
|
+
#
|
|
300
|
+
# @param args [Hash]
|
|
301
|
+
# @option args [String] :identity_key public key hex to search
|
|
302
|
+
# @option args [Integer] :limit max results
|
|
303
|
+
# @option args [Integer] :offset number to skip
|
|
304
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
305
|
+
# @return [Hash] with :total_certificates, :certificates
|
|
306
|
+
def discover_by_identity_key(args, originator: nil)
|
|
307
|
+
raise UnsupportedActionError, 'discover_by_identity_key'
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Discovers certificates by attributes.
|
|
311
|
+
#
|
|
312
|
+
# @param args [Hash]
|
|
313
|
+
# @option args [Hash] :attributes attribute name/value pairs to match
|
|
314
|
+
# @option args [Integer] :limit max results
|
|
315
|
+
# @option args [Integer] :offset number to skip
|
|
316
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
317
|
+
# @return [Hash] with :total_certificates, :certificates
|
|
318
|
+
def discover_by_attributes(args, originator: nil)
|
|
319
|
+
raise UnsupportedActionError, 'discover_by_attributes'
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# --- Blockchain and Network Data ---
|
|
323
|
+
|
|
324
|
+
# Returns the current blockchain height.
|
|
325
|
+
#
|
|
326
|
+
# @param args [Hash] empty hash
|
|
327
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
328
|
+
# @return [Hash] { height: Integer }
|
|
329
|
+
def get_height(args = {}, originator: nil)
|
|
330
|
+
raise UnsupportedActionError, 'get_height'
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Returns the block header at a given height.
|
|
334
|
+
#
|
|
335
|
+
# @param args [Hash]
|
|
336
|
+
# @option args [Integer] :height block height
|
|
337
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
338
|
+
# @return [Hash] { header: String }
|
|
339
|
+
def get_header_for_height(args, originator: nil)
|
|
340
|
+
raise UnsupportedActionError, 'get_header_for_height'
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Returns the network (mainnet or testnet).
|
|
344
|
+
#
|
|
345
|
+
# @param args [Hash] empty hash
|
|
346
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
347
|
+
# @return [Hash] { network: String }
|
|
348
|
+
def get_network(args = {}, originator: nil)
|
|
349
|
+
raise UnsupportedActionError, 'get_network'
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Returns the wallet version string.
|
|
353
|
+
#
|
|
354
|
+
# @param args [Hash] empty hash
|
|
355
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
356
|
+
# @return [Hash] { version: String }
|
|
357
|
+
def get_version(args = {}, originator: nil)
|
|
358
|
+
raise UnsupportedActionError, 'get_version'
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# --- Authentication ---
|
|
362
|
+
|
|
363
|
+
# Checks if the user is authenticated.
|
|
364
|
+
#
|
|
365
|
+
# @param args [Hash] empty hash
|
|
366
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
367
|
+
# @return [Hash] { authenticated: Boolean }
|
|
368
|
+
def is_authenticated(args = {}, originator: nil)
|
|
369
|
+
raise UnsupportedActionError, 'is_authenticated'
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Waits until the user is authenticated.
|
|
373
|
+
#
|
|
374
|
+
# @param args [Hash] empty hash
|
|
375
|
+
# @param originator [String, nil] FQDN of the originating application
|
|
376
|
+
# @return [Hash] { authenticated: true }
|
|
377
|
+
def wait_for_authentication(args = {}, originator: nil)
|
|
378
|
+
raise UnsupportedActionError, 'wait_for_authentication'
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
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
|
+
else
|
|
20
|
+
root_key
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns the identity public key as a hex string.
|
|
25
|
+
# @return [String] 66-character compressed public key hex
|
|
26
|
+
def identity_key
|
|
27
|
+
@root_key.public_key.to_hex
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Derives a public key using BRC-42 key derivation.
|
|
31
|
+
#
|
|
32
|
+
# @param protocol_id [Array] [security_level, protocol_name]
|
|
33
|
+
# @param key_id [String] key identifier
|
|
34
|
+
# @param counterparty [String] public key hex, 'self', or 'anyone'
|
|
35
|
+
# @param for_self [Boolean] derive from own identity rather than counterparty's
|
|
36
|
+
# @return [BSV::Primitives::PublicKey]
|
|
37
|
+
def derive_public_key(protocol_id, key_id, counterparty, for_self: false)
|
|
38
|
+
Validators.validate_protocol_id!(protocol_id)
|
|
39
|
+
Validators.validate_key_id!(key_id)
|
|
40
|
+
invoice = compute_invoice_number(protocol_id, key_id)
|
|
41
|
+
counterparty_pub = resolve_counterparty(counterparty)
|
|
42
|
+
|
|
43
|
+
if for_self
|
|
44
|
+
@root_key.derive_child(counterparty_pub, invoice).public_key
|
|
45
|
+
else
|
|
46
|
+
counterparty_pub.derive_child(@root_key, invoice)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Derives a private key using BRC-42 key derivation.
|
|
51
|
+
#
|
|
52
|
+
# @param protocol_id [Array] [security_level, protocol_name]
|
|
53
|
+
# @param key_id [String] key identifier
|
|
54
|
+
# @param counterparty [String] public key hex, 'self', or 'anyone'
|
|
55
|
+
# @return [BSV::Primitives::PrivateKey]
|
|
56
|
+
def derive_private_key(protocol_id, key_id, counterparty)
|
|
57
|
+
Validators.validate_protocol_id!(protocol_id)
|
|
58
|
+
Validators.validate_key_id!(key_id)
|
|
59
|
+
invoice = compute_invoice_number(protocol_id, key_id)
|
|
60
|
+
counterparty_pub = resolve_counterparty(counterparty)
|
|
61
|
+
@root_key.derive_child(counterparty_pub, invoice)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Derives a symmetric key for encryption/HMAC operations.
|
|
65
|
+
#
|
|
66
|
+
# Uses ECDH between the derived private and public child keys to
|
|
67
|
+
# produce a shared secret, then uses the X-coordinate as the key.
|
|
68
|
+
#
|
|
69
|
+
# @param protocol_id [Array] [security_level, protocol_name]
|
|
70
|
+
# @param key_id [String] key identifier
|
|
71
|
+
# @param counterparty [String] public key hex, 'self', or 'anyone'
|
|
72
|
+
# @return [BSV::Primitives::SymmetricKey]
|
|
73
|
+
def derive_symmetric_key(protocol_id, key_id, counterparty)
|
|
74
|
+
Validators.validate_protocol_id!(protocol_id)
|
|
75
|
+
Validators.validate_key_id!(key_id)
|
|
76
|
+
invoice = compute_invoice_number(protocol_id, key_id)
|
|
77
|
+
counterparty_pub = resolve_counterparty(counterparty)
|
|
78
|
+
|
|
79
|
+
derived_private = @root_key.derive_child(counterparty_pub, invoice)
|
|
80
|
+
derived_public = counterparty_pub.derive_child(@root_key, invoice)
|
|
81
|
+
|
|
82
|
+
BSV::Primitives::SymmetricKey.from_ecdh(derived_private, derived_public)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Reveals the ECDH shared secret between this wallet and a counterparty.
|
|
86
|
+
# Used for BRC-69 Method 1 (counterparty key linkage).
|
|
87
|
+
#
|
|
88
|
+
# @param counterparty [String] public key hex (not 'self')
|
|
89
|
+
# @return [String] compressed shared secret bytes
|
|
90
|
+
def reveal_counterparty_secret(counterparty)
|
|
91
|
+
raise InvalidParameterError.new('counterparty', 'not "self" for key linkage revelation') if counterparty == 'self'
|
|
92
|
+
|
|
93
|
+
counterparty_pub = resolve_counterparty(counterparty)
|
|
94
|
+
@root_key.derive_shared_secret(counterparty_pub).compressed
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Reveals the specific key offset for a particular derived key.
|
|
98
|
+
# Used for BRC-69 Method 2 (specific key linkage).
|
|
99
|
+
#
|
|
100
|
+
# @param counterparty [String] public key hex
|
|
101
|
+
# @param protocol_id [Array] [security_level, protocol_name]
|
|
102
|
+
# @param key_id [String] key identifier
|
|
103
|
+
# @return [String] HMAC-SHA256 bytes (the key offset)
|
|
104
|
+
def reveal_specific_secret(counterparty, protocol_id, key_id)
|
|
105
|
+
Validators.validate_protocol_id!(protocol_id)
|
|
106
|
+
Validators.validate_key_id!(key_id)
|
|
107
|
+
counterparty_pub = resolve_counterparty(counterparty)
|
|
108
|
+
shared = @root_key.derive_shared_secret(counterparty_pub)
|
|
109
|
+
invoice = compute_invoice_number(protocol_id, key_id)
|
|
110
|
+
BSV::Primitives::Digest.hmac_sha256(shared.compressed, invoice.encode('UTF-8'))
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
# Resolves a counterparty identifier to a PublicKey.
|
|
116
|
+
#
|
|
117
|
+
# @param counterparty [String] 'self', 'anyone', or a hex public key
|
|
118
|
+
# @return [BSV::Primitives::PublicKey]
|
|
119
|
+
def resolve_counterparty(counterparty)
|
|
120
|
+
case counterparty
|
|
121
|
+
when 'self'
|
|
122
|
+
@root_key.public_key
|
|
123
|
+
when 'anyone'
|
|
124
|
+
BSV::Primitives::PrivateKey.new(ANYONE_BN).public_key
|
|
125
|
+
else
|
|
126
|
+
Validators.validate_counterparty!(counterparty)
|
|
127
|
+
BSV::Primitives::PublicKey.from_hex(counterparty)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Computes the invoice number from a protocol ID and key ID.
|
|
132
|
+
# Format: "#{security_level}-#{protocol_name}-#{key_id}"
|
|
133
|
+
#
|
|
134
|
+
# @param protocol_id [Array] [security_level, protocol_name]
|
|
135
|
+
# @param key_id [String]
|
|
136
|
+
# @return [String]
|
|
137
|
+
def compute_invoice_number(protocol_id, key_id)
|
|
138
|
+
"#{protocol_id[0]}-#{protocol_id[1]}-#{key_id}"
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
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
|
+
end
|
|
17
|
+
|
|
18
|
+
def store_action(action_data)
|
|
19
|
+
@actions << action_data
|
|
20
|
+
action_data
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def find_actions(query)
|
|
24
|
+
apply_pagination(filter_actions(query), query)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def count_actions(query)
|
|
28
|
+
filter_actions(query).length
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def store_output(output_data)
|
|
32
|
+
@outputs << output_data
|
|
33
|
+
output_data
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def find_outputs(query)
|
|
37
|
+
apply_pagination(filter_outputs(query), query)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def count_outputs(query)
|
|
41
|
+
filter_outputs(query).length
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def delete_output(outpoint)
|
|
45
|
+
idx = @outputs.index { |o| o[:outpoint] == outpoint }
|
|
46
|
+
return false unless idx
|
|
47
|
+
|
|
48
|
+
@outputs.delete_at(idx)
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def store_certificate(cert_data)
|
|
53
|
+
@certificates << cert_data
|
|
54
|
+
cert_data
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def find_certificates(query)
|
|
58
|
+
results = @certificates
|
|
59
|
+
results = results.select { |c| query[:certifiers].include?(c[:certifier]) } if query[:certifiers]
|
|
60
|
+
results = results.select { |c| query[:types].include?(c[:type]) } if query[:types]
|
|
61
|
+
apply_pagination(results, query)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def delete_certificate(type:, serial_number:, certifier:)
|
|
65
|
+
idx = @certificates.index do |c|
|
|
66
|
+
c[:type] == type && c[:serial_number] == serial_number && c[:certifier] == certifier
|
|
67
|
+
end
|
|
68
|
+
return false unless idx
|
|
69
|
+
|
|
70
|
+
@certificates.delete_at(idx)
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def filter_actions(query)
|
|
77
|
+
results = @actions
|
|
78
|
+
return results unless query[:labels]
|
|
79
|
+
|
|
80
|
+
mode = query[:label_query_mode] || 'any'
|
|
81
|
+
results.select do |a|
|
|
82
|
+
action_labels = a[:labels] || []
|
|
83
|
+
if mode == 'all'
|
|
84
|
+
(query[:labels] - action_labels).empty?
|
|
85
|
+
else
|
|
86
|
+
(query[:labels] & action_labels).any?
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def filter_outputs(query)
|
|
92
|
+
results = @outputs
|
|
93
|
+
results = results.select { |o| o[:basket] == query[:basket] } if query[:basket]
|
|
94
|
+
if query[:tags]
|
|
95
|
+
mode = query[:tag_query_mode] || 'any'
|
|
96
|
+
results = results.select do |o|
|
|
97
|
+
output_tags = o[:tags] || []
|
|
98
|
+
if mode == 'all'
|
|
99
|
+
(query[:tags] - output_tags).empty?
|
|
100
|
+
else
|
|
101
|
+
(query[:tags] & output_tags).any?
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
query[:include_spent] ? results : results.reject { |o| o[:spendable] == false }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def apply_pagination(results, query)
|
|
109
|
+
offset = query[:offset] || 0
|
|
110
|
+
limit = query[:limit] || 10
|
|
111
|
+
results[offset, limit] || []
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|