bsv-sdk 0.6.2 → 0.7.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 +19 -0
- data/lib/bsv/transaction/beef.rb +44 -17
- data/lib/bsv/version.rb +1 -1
- data/lib/bsv/wallet_interface/file_store.rb +53 -1
- data/lib/bsv/wallet_interface/local_proof_store.rb +42 -0
- data/lib/bsv/wallet_interface/memory_store.rb +18 -0
- data/lib/bsv/wallet_interface/proof_store.rb +32 -0
- data/lib/bsv/wallet_interface/storage_adapter.rb +16 -0
- data/lib/bsv/wallet_interface/version.rb +1 -1
- data/lib/bsv/wallet_interface/wallet_client.rb +59 -8
- data/lib/bsv/wallet_interface.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 27f8c2431bb1d2a33a2f068fc4f46a616eb1ce000b1e61309819a745b596d57f
|
|
4
|
+
data.tar.gz: 172cb6b1dd0eeff2cbaa2887ccef5454df6dd8f18ebf7be7b16fb342d09fa749
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec1fc0e96520b641f9d42d37fe88507f835c434304ea550b26b38544805ae318b5ccccd8fb3d4246ba798855af5a688e54ea865f20bcf2415dabed0bda46378e
|
|
7
|
+
data.tar.gz: f41066e6683556f6d09e17a4211b02d304880dad99104ea67b555a823af8d6adb9ff7f7ce526c9a18cc67602abab3e47408fc078fc624d15b9c105f1f9a73ca0
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.0] - 2026-04-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Wallet** — pluggable proof store for merkle proof persistence. The wallet is now a lightweight SPV node: `internalize_action` extracts and stores merkle proofs from incoming BEEF; `create_action` reattaches them to produce valid BEEF with BUMPs for ARC broadcast.
|
|
13
|
+
- `ProofStore` interface with `store_proof` / `resolve_proof`
|
|
14
|
+
- `LocalProofStore` default implementation using `StorageAdapter`
|
|
15
|
+
- `WalletClient` accepts injectable `proof_store:` parameter
|
|
16
|
+
- Transaction caching (`store_transaction` / `find_transaction`) for ancestry reconstruction
|
|
17
|
+
- **Wallet** — `StorageAdapter` gains `store_proof`, `find_proof`, `store_transaction`, `find_transaction` methods, implemented in both `MemoryStore` and `FileStore`.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **Transaction** — `Beef#to_binary` now defaults to BEEF V1 (BRC-62) format, matching the TS reference SDK's `Transaction#toBEEF()`. ARC's parser does not support V2. Pass `version: BEEF_V2` for BRC-96 format. Atomic BEEF (BRC-95) inner envelope remains V2 per spec.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- **Wallet** — `wire_source_from_storage` resolves merkle proofs via proof store so `to_beef` produces valid BEEF that ARC accepts. Previously, BEEF contained source transactions without proofs, causing ARC 463/468 rejections.
|
|
26
|
+
|
|
8
27
|
## [0.6.2] - 2026-04-06
|
|
9
28
|
|
|
10
29
|
### Added
|
data/lib/bsv/transaction/beef.rb
CHANGED
|
@@ -152,35 +152,30 @@ module BSV
|
|
|
152
152
|
from_binary([hex].pack('H*'))
|
|
153
153
|
end
|
|
154
154
|
|
|
155
|
-
# --- Serialisation
|
|
155
|
+
# --- Serialisation ---
|
|
156
156
|
|
|
157
|
-
# Serialise the BEEF bundle to
|
|
157
|
+
# Serialise the BEEF bundle to binary format.
|
|
158
158
|
#
|
|
159
|
+
# Defaults to V1 (BRC-62) for compatibility with ARC and the
|
|
160
|
+
# reference TS SDK. Pass +version: BEEF_V2+ for BRC-96 format.
|
|
161
|
+
#
|
|
162
|
+
# @param version [Integer] BEEF_V1 (default) or BEEF_V2
|
|
159
163
|
# @return [String] raw BEEF binary
|
|
160
|
-
def to_binary
|
|
164
|
+
def to_binary(version: BEEF_V1)
|
|
161
165
|
bump_map = build_bump_map
|
|
162
166
|
bumps_to_write = bump_map.values.sort_by { |entry| entry[:index] }.map { |entry| entry[:bump] }
|
|
163
167
|
|
|
164
|
-
buf = [
|
|
168
|
+
buf = [version].pack('V')
|
|
165
169
|
|
|
166
170
|
buf << VarInt.encode(bumps_to_write.length)
|
|
167
171
|
bumps_to_write.each { |bump| buf << bump.to_binary }
|
|
168
172
|
|
|
169
173
|
buf << VarInt.encode(@transactions.length)
|
|
170
174
|
@transactions.each do |beef_tx|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
buf << [FORMAT_TXID_ONLY].pack('C')
|
|
174
|
-
buf << beef_tx.known_txid
|
|
175
|
-
when FORMAT_RAW_TX_AND_BUMP
|
|
176
|
-
buf << [FORMAT_RAW_TX_AND_BUMP].pack('C')
|
|
177
|
-
mp = beef_tx.transaction.merkle_path
|
|
178
|
-
idx = bump_map[mp][:index]
|
|
179
|
-
buf << VarInt.encode(idx)
|
|
180
|
-
buf << beef_tx.transaction.to_binary
|
|
175
|
+
if version == BEEF_V2
|
|
176
|
+
write_v2_tx(buf, beef_tx, bump_map)
|
|
181
177
|
else
|
|
182
|
-
buf
|
|
183
|
-
buf << beef_tx.transaction.to_binary
|
|
178
|
+
write_v1_tx(buf, beef_tx, bump_map)
|
|
184
179
|
end
|
|
185
180
|
end
|
|
186
181
|
|
|
@@ -203,7 +198,8 @@ module BSV
|
|
|
203
198
|
# Write subject txid in internal byte order (reverse of display order),
|
|
204
199
|
# matching JS and Go SDK conventions for Bitcoin binary formats.
|
|
205
200
|
buf << subject_txid.b.reverse
|
|
206
|
-
|
|
201
|
+
# BRC-95: inner envelope is always V2
|
|
202
|
+
buf << to_binary(version: BEEF_V2)
|
|
207
203
|
buf
|
|
208
204
|
end
|
|
209
205
|
|
|
@@ -606,6 +602,37 @@ module BSV
|
|
|
606
602
|
end
|
|
607
603
|
end
|
|
608
604
|
|
|
605
|
+
# V1 (BRC-62): raw_tx + has_bump(byte) [+ bump_index(varint)]
|
|
606
|
+
def write_v1_tx(buf, beef_tx, bump_map)
|
|
607
|
+
buf << beef_tx.transaction.to_binary
|
|
608
|
+
if beef_tx.format == FORMAT_RAW_TX_AND_BUMP
|
|
609
|
+
mp = beef_tx.transaction.merkle_path
|
|
610
|
+
idx = bump_map[mp][:index]
|
|
611
|
+
buf << [1].pack('C')
|
|
612
|
+
buf << VarInt.encode(idx)
|
|
613
|
+
else
|
|
614
|
+
buf << [0].pack('C')
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
# V2 (BRC-96): format_byte [+ bump_index(varint)] + raw_tx
|
|
619
|
+
def write_v2_tx(buf, beef_tx, bump_map)
|
|
620
|
+
case beef_tx.format
|
|
621
|
+
when FORMAT_TXID_ONLY
|
|
622
|
+
buf << [FORMAT_TXID_ONLY].pack('C')
|
|
623
|
+
buf << beef_tx.known_txid
|
|
624
|
+
when FORMAT_RAW_TX_AND_BUMP
|
|
625
|
+
buf << [FORMAT_RAW_TX_AND_BUMP].pack('C')
|
|
626
|
+
mp = beef_tx.transaction.merkle_path
|
|
627
|
+
idx = bump_map[mp][:index]
|
|
628
|
+
buf << VarInt.encode(idx)
|
|
629
|
+
buf << beef_tx.transaction.to_binary
|
|
630
|
+
else
|
|
631
|
+
buf << [FORMAT_RAW_TX].pack('C')
|
|
632
|
+
buf << beef_tx.transaction.to_binary
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
|
|
609
636
|
def build_bump_map
|
|
610
637
|
map = {}.compare_by_identity
|
|
611
638
|
idx = 0
|
data/lib/bsv/version.rb
CHANGED
|
@@ -72,8 +72,26 @@ module BSV
|
|
|
72
72
|
result
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
def store_proof(txid, bump_hex)
|
|
76
|
+
super
|
|
77
|
+
save_proofs
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def store_transaction(txid, tx_hex)
|
|
81
|
+
super
|
|
82
|
+
save_transactions
|
|
83
|
+
end
|
|
84
|
+
|
|
75
85
|
private
|
|
76
86
|
|
|
87
|
+
def proofs_path
|
|
88
|
+
File.join(@dir, 'proofs.json')
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def transactions_path
|
|
92
|
+
File.join(@dir, 'transactions.json')
|
|
93
|
+
end
|
|
94
|
+
|
|
77
95
|
def check_permissions
|
|
78
96
|
dir_mode = File.stat(@dir).mode & 0o777
|
|
79
97
|
if dir_mode != 0o700
|
|
@@ -81,7 +99,7 @@ module BSV
|
|
|
81
99
|
'Other users may be able to access wallet data.')
|
|
82
100
|
end
|
|
83
101
|
|
|
84
|
-
[actions_path, outputs_path, certificates_path].each do |path|
|
|
102
|
+
[actions_path, outputs_path, certificates_path, proofs_path, transactions_path].each do |path|
|
|
85
103
|
next unless File.exist?(path)
|
|
86
104
|
|
|
87
105
|
file_mode = File.stat(path).mode & 0o777
|
|
@@ -108,6 +126,8 @@ module BSV
|
|
|
108
126
|
@actions = load_file(actions_path)
|
|
109
127
|
@outputs = load_file(outputs_path)
|
|
110
128
|
@certificates = load_file(certificates_path)
|
|
129
|
+
@proofs = load_proofs_file
|
|
130
|
+
@transactions = load_transactions_file
|
|
111
131
|
end
|
|
112
132
|
|
|
113
133
|
def load_file(path)
|
|
@@ -134,6 +154,38 @@ module BSV
|
|
|
134
154
|
write_file(certificates_path, @certificates)
|
|
135
155
|
end
|
|
136
156
|
|
|
157
|
+
def save_proofs
|
|
158
|
+
json = JSON.pretty_generate(@proofs)
|
|
159
|
+
tmp = "#{proofs_path}.tmp"
|
|
160
|
+
File.open(tmp, File::WRONLY | File::CREAT | File::TRUNC, 0o600) { |f| f.write(json) }
|
|
161
|
+
File.rename(tmp, proofs_path)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def load_proofs_file
|
|
165
|
+
return {} unless File.exist?(proofs_path)
|
|
166
|
+
|
|
167
|
+
data = JSON.parse(File.read(proofs_path))
|
|
168
|
+
data.is_a?(Hash) ? data : {}
|
|
169
|
+
rescue JSON::ParserError
|
|
170
|
+
{}
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def save_transactions
|
|
174
|
+
json = JSON.pretty_generate(@transactions)
|
|
175
|
+
tmp = "#{transactions_path}.tmp"
|
|
176
|
+
File.open(tmp, File::WRONLY | File::CREAT | File::TRUNC, 0o600) { |f| f.write(json) }
|
|
177
|
+
File.rename(tmp, transactions_path)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def load_transactions_file
|
|
181
|
+
return {} unless File.exist?(transactions_path)
|
|
182
|
+
|
|
183
|
+
data = JSON.parse(File.read(transactions_path))
|
|
184
|
+
data.is_a?(Hash) ? data : {}
|
|
185
|
+
rescue JSON::ParserError
|
|
186
|
+
{}
|
|
187
|
+
end
|
|
188
|
+
|
|
137
189
|
def write_file(path, data)
|
|
138
190
|
json = JSON.pretty_generate(stringify_keys_deep(data))
|
|
139
191
|
tmp = "#{path}.tmp"
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
|
@@ -13,6 +13,8 @@ module BSV
|
|
|
13
13
|
@actions = []
|
|
14
14
|
@outputs = []
|
|
15
15
|
@certificates = []
|
|
16
|
+
@proofs = {}
|
|
17
|
+
@transactions = {}
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def store_action(action_data)
|
|
@@ -62,6 +64,22 @@ module BSV
|
|
|
62
64
|
filter_certificates(query).length
|
|
63
65
|
end
|
|
64
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
|
+
|
|
65
83
|
def delete_certificate(type:, serial_number:, certifier:)
|
|
66
84
|
idx = @certificates.index do |c|
|
|
67
85
|
c[:type] == type && c[:serial_number] == serial_number && c[:certifier] == certifier
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
|
@@ -50,6 +50,22 @@ module BSV
|
|
|
50
50
|
def count_certificates(_query)
|
|
51
51
|
raise NotImplementedError, "#{self.class}#count_certificates not implemented"
|
|
52
52
|
end
|
|
53
|
+
|
|
54
|
+
def store_proof(_txid, _bump_hex)
|
|
55
|
+
raise NotImplementedError, "#{self.class}#store_proof not implemented"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def find_proof(_txid)
|
|
59
|
+
raise NotImplementedError, "#{self.class}#find_proof not implemented"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def store_transaction(_txid, _tx_hex)
|
|
63
|
+
raise NotImplementedError, "#{self.class}#store_transaction not implemented"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def find_transaction(_txid)
|
|
67
|
+
raise NotImplementedError, "#{self.class}#find_transaction not implemented"
|
|
68
|
+
end
|
|
53
69
|
end
|
|
54
70
|
end
|
|
55
71
|
end
|
|
@@ -35,17 +35,22 @@ module BSV
|
|
|
35
35
|
# @return [String] the network ('mainnet' or 'testnet')
|
|
36
36
|
attr_reader :network
|
|
37
37
|
|
|
38
|
+
# @return [ProofStore] the merkle proof persistence store
|
|
39
|
+
attr_reader :proof_store
|
|
40
|
+
|
|
38
41
|
# @param key [BSV::Primitives::PrivateKey, String, KeyDeriver] signing key
|
|
39
42
|
# @param storage [StorageAdapter] persistence adapter (default: FileStore).
|
|
40
43
|
# Use +storage: MemoryStore.new+ for tests.
|
|
41
44
|
# @param network [String] 'mainnet' (default) or 'testnet'
|
|
42
45
|
# @param chain_provider [ChainProvider] blockchain data provider (default: NullChainProvider)
|
|
46
|
+
# @param proof_store [ProofStore, nil] merkle proof store (default: LocalProofStore backed by storage)
|
|
43
47
|
# @param http_client [#request, nil] injectable HTTP client for certificate issuance
|
|
44
|
-
def initialize(key, storage: FileStore.new, network: 'mainnet', chain_provider: NullChainProvider.new, http_client: nil)
|
|
48
|
+
def initialize(key, storage: FileStore.new, network: 'mainnet', chain_provider: NullChainProvider.new, proof_store: nil, http_client: nil)
|
|
45
49
|
super(key)
|
|
46
50
|
@storage = storage
|
|
47
51
|
@network = network
|
|
48
52
|
@chain_provider = chain_provider
|
|
53
|
+
@proof_store = proof_store || LocalProofStore.new(storage)
|
|
49
54
|
@http_client = http_client
|
|
50
55
|
@pending = {}
|
|
51
56
|
end
|
|
@@ -177,6 +182,7 @@ module BSV
|
|
|
177
182
|
beef = BSV::Transaction::Beef.from_binary(beef_binary)
|
|
178
183
|
tx = extract_subject_transaction(beef)
|
|
179
184
|
|
|
185
|
+
store_proofs_from_beef(beef)
|
|
180
186
|
process_internalize_outputs(tx, args[:outputs])
|
|
181
187
|
store_action(tx, args, status: 'completed')
|
|
182
188
|
{ accepted: true }
|
|
@@ -520,6 +526,35 @@ module BSV
|
|
|
520
526
|
|
|
521
527
|
input.source_satoshis = stored[:satoshis]
|
|
522
528
|
input.source_locking_script = BSV::Script::Script.from_hex(stored[:locking_script])
|
|
529
|
+
|
|
530
|
+
return unless stored[:source_tx_hex]
|
|
531
|
+
|
|
532
|
+
source_tx = BSV::Transaction::Transaction.from_hex(stored[:source_tx_hex])
|
|
533
|
+
txid_hex = outpoint.split('.').first
|
|
534
|
+
proof = @proof_store.resolve_proof(txid_hex)
|
|
535
|
+
source_tx.merkle_path = proof if proof
|
|
536
|
+
|
|
537
|
+
# Recursively wire ancestors of the source tx so to_beef can build a
|
|
538
|
+
# complete, valid BEEF with the full proof chain.
|
|
539
|
+
wire_source_tx_ancestors(source_tx) unless source_tx.merkle_path
|
|
540
|
+
|
|
541
|
+
input.source_transaction = source_tx
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def wire_source_tx_ancestors(tx)
|
|
545
|
+
tx.inputs.each do |inp|
|
|
546
|
+
next if inp.source_transaction
|
|
547
|
+
|
|
548
|
+
ancestor_txid_hex = inp.prev_tx_id.reverse.unpack1('H*')
|
|
549
|
+
tx_hex = @storage.find_transaction(ancestor_txid_hex)
|
|
550
|
+
next unless tx_hex
|
|
551
|
+
|
|
552
|
+
ancestor_tx = BSV::Transaction::Transaction.from_hex(tx_hex)
|
|
553
|
+
proof = @proof_store.resolve_proof(ancestor_txid_hex)
|
|
554
|
+
ancestor_tx.merkle_path = proof if proof
|
|
555
|
+
wire_source_tx_ancestors(ancestor_tx) unless ancestor_tx.merkle_path
|
|
556
|
+
inp.source_transaction = ancestor_tx
|
|
557
|
+
end
|
|
523
558
|
end
|
|
524
559
|
|
|
525
560
|
def build_outputs(tx, outputs)
|
|
@@ -601,6 +636,8 @@ module BSV
|
|
|
601
636
|
def store_tracked_outputs(txid, tx, output_specs)
|
|
602
637
|
return unless output_specs
|
|
603
638
|
|
|
639
|
+
tx_hex = tx.to_hex
|
|
640
|
+
|
|
604
641
|
output_specs.each do |spec|
|
|
605
642
|
next unless spec[:basket]
|
|
606
643
|
|
|
@@ -617,7 +654,8 @@ module BSV
|
|
|
617
654
|
basket: spec[:basket],
|
|
618
655
|
tags: spec[:tags] || [],
|
|
619
656
|
custom_instructions: spec[:custom_instructions],
|
|
620
|
-
spendable: true
|
|
657
|
+
spendable: true,
|
|
658
|
+
source_tx_hex: tx_hex
|
|
621
659
|
})
|
|
622
660
|
end
|
|
623
661
|
end
|
|
@@ -644,6 +682,16 @@ module BSV
|
|
|
644
682
|
|
|
645
683
|
# --- Internalize helpers ---
|
|
646
684
|
|
|
685
|
+
def store_proofs_from_beef(beef)
|
|
686
|
+
beef.transactions.each do |beef_tx|
|
|
687
|
+
next unless beef_tx.transaction&.merkle_path
|
|
688
|
+
|
|
689
|
+
txid_hex = beef_tx.transaction.txid_hex
|
|
690
|
+
@proof_store.store_proof(txid_hex, beef_tx.transaction.merkle_path)
|
|
691
|
+
@storage.store_transaction(txid_hex, beef_tx.transaction.to_hex)
|
|
692
|
+
end
|
|
693
|
+
end
|
|
694
|
+
|
|
647
695
|
def extract_subject_transaction(beef)
|
|
648
696
|
return find_by_subject_txid(beef) if beef.subject_txid
|
|
649
697
|
|
|
@@ -660,6 +708,7 @@ module BSV
|
|
|
660
708
|
|
|
661
709
|
def process_internalize_outputs(tx, output_specs)
|
|
662
710
|
txid = tx.txid_hex
|
|
711
|
+
tx_hex = tx.to_hex
|
|
663
712
|
|
|
664
713
|
output_specs.each do |spec|
|
|
665
714
|
output_index = spec[:output_index]
|
|
@@ -668,16 +717,16 @@ module BSV
|
|
|
668
717
|
|
|
669
718
|
case spec[:protocol]
|
|
670
719
|
when 'wallet payment'
|
|
671
|
-
internalize_payment(txid, output_index, tx_output, spec[:payment_remittance])
|
|
720
|
+
internalize_payment(txid, output_index, tx_output, spec[:payment_remittance], tx_hex)
|
|
672
721
|
when 'basket insertion'
|
|
673
|
-
internalize_basket(txid, output_index, tx_output, spec[:insertion_remittance])
|
|
722
|
+
internalize_basket(txid, output_index, tx_output, spec[:insertion_remittance], tx_hex)
|
|
674
723
|
else
|
|
675
724
|
raise InvalidParameterError.new('protocol', '"wallet payment" or "basket insertion"')
|
|
676
725
|
end
|
|
677
726
|
end
|
|
678
727
|
end
|
|
679
728
|
|
|
680
|
-
def internalize_payment(txid, output_index, tx_output, remittance)
|
|
729
|
+
def internalize_payment(txid, output_index, tx_output, remittance, tx_hex = nil)
|
|
681
730
|
unless remittance
|
|
682
731
|
raise InvalidParameterError.new('payment_remittance',
|
|
683
732
|
'present for wallet payment protocol')
|
|
@@ -705,11 +754,12 @@ module BSV
|
|
|
705
754
|
spendable: true,
|
|
706
755
|
sender_identity_key: sender_key,
|
|
707
756
|
derivation_prefix: prefix,
|
|
708
|
-
derivation_suffix: suffix
|
|
757
|
+
derivation_suffix: suffix,
|
|
758
|
+
source_tx_hex: tx_hex
|
|
709
759
|
})
|
|
710
760
|
end
|
|
711
761
|
|
|
712
|
-
def internalize_basket(txid, output_index, tx_output, remittance)
|
|
762
|
+
def internalize_basket(txid, output_index, tx_output, remittance, tx_hex = nil)
|
|
713
763
|
unless remittance
|
|
714
764
|
raise InvalidParameterError.new('insertion_remittance',
|
|
715
765
|
'present for basket insertion protocol')
|
|
@@ -724,7 +774,8 @@ module BSV
|
|
|
724
774
|
basket: remittance[:basket],
|
|
725
775
|
tags: remittance[:tags] || [],
|
|
726
776
|
custom_instructions: remittance[:custom_instructions],
|
|
727
|
-
spendable: true
|
|
777
|
+
spendable: true,
|
|
778
|
+
source_tx_hex: tx_hex
|
|
728
779
|
})
|
|
729
780
|
end
|
|
730
781
|
|
data/lib/bsv/wallet_interface.rb
CHANGED
|
@@ -14,6 +14,8 @@ module BSV
|
|
|
14
14
|
autoload :StorageAdapter, 'bsv/wallet_interface/storage_adapter'
|
|
15
15
|
autoload :MemoryStore, 'bsv/wallet_interface/memory_store'
|
|
16
16
|
autoload :FileStore, 'bsv/wallet_interface/file_store'
|
|
17
|
+
autoload :ProofStore, 'bsv/wallet_interface/proof_store'
|
|
18
|
+
autoload :LocalProofStore, 'bsv/wallet_interface/local_proof_store'
|
|
17
19
|
autoload :ChainProvider, 'bsv/wallet_interface/chain_provider'
|
|
18
20
|
autoload :NullChainProvider, 'bsv/wallet_interface/null_chain_provider'
|
|
19
21
|
autoload :WalletClient, 'bsv/wallet_interface/wallet_client'
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bsv-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Bettison
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-04-
|
|
10
|
+
date: 2026-04-06 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: A Ruby library for interacting with the BSV Blockchain — keys, scripts,
|
|
13
13
|
transactions, and more.
|
|
@@ -130,8 +130,10 @@ files:
|
|
|
130
130
|
- lib/bsv/wallet_interface/file_store.rb
|
|
131
131
|
- lib/bsv/wallet_interface/interface.rb
|
|
132
132
|
- lib/bsv/wallet_interface/key_deriver.rb
|
|
133
|
+
- lib/bsv/wallet_interface/local_proof_store.rb
|
|
133
134
|
- lib/bsv/wallet_interface/memory_store.rb
|
|
134
135
|
- lib/bsv/wallet_interface/null_chain_provider.rb
|
|
136
|
+
- lib/bsv/wallet_interface/proof_store.rb
|
|
135
137
|
- lib/bsv/wallet_interface/proto_wallet.rb
|
|
136
138
|
- lib/bsv/wallet_interface/storage_adapter.rb
|
|
137
139
|
- lib/bsv/wallet_interface/validators.rb
|