bsv-wallet 0.3.4 → 0.4.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7f0eba4cf58a9a63db79d2b731700f5d2d3ae04b82416ccc0288a1c5659a365
|
|
4
|
+
data.tar.gz: 2453264034fbbe664ced66bd853e8e5b4a52eb668e87a6eb01fd71e5b589d61b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '092fa8e32a2ac7b7b4429c0fe715858ba16bba9869530cb97c4dfab0ff5730b2b2ff018da87a60cea5c5beaf7e527945c1158127e27806825db8ba29e895e2ed'
|
|
7
|
+
data.tar.gz: 68c27f04fa860ce116c7ddaa1a76a25b4ccfc56ab4b8b3af454dbbf5b2f452c719b884df92497b01ff6b74dbcb4eced994fbe519a51443bf7200a87cdaed65df
|
data/CHANGELOG-wallet.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Changelog — bsv-wallet
|
|
2
|
+
|
|
3
|
+
All notable changes to the `bsv-wallet` gem are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
6
|
+
and this gem adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## 0.4.0 — 2026-04-10
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Protocol-ID normalisation** (F8.7): `Validators.validate_protocol_id!` now
|
|
13
|
+
strips and downcases the name before applying rules, so `' MyProtocol '` and
|
|
14
|
+
`'myprotocol'` are treated identically and cannot silently fork to different
|
|
15
|
+
key-derivation paths.
|
|
16
|
+
- **Permission-rule constants** (F8.8): Reserved prefix/suffix strings are now
|
|
17
|
+
named constants on `BSV::Wallet::Validators` (`RESERVED_PROTOCOL_PREFIXES`,
|
|
18
|
+
`RESERVED_PROTOCOL_SUFFIX`, `RESERVED_BASKET_PREFIXES`, `RESERVED_BASKET_SUFFIX`,
|
|
19
|
+
`RESERVED_BASKET_NAME`), making them discoverable and documentable.
|
|
20
|
+
- **BEEF verification in `internalize_action`** (F8.14): The BEEF bundle is now
|
|
21
|
+
verified via `Beef#verify` before any outputs are stored. If the bundle is
|
|
22
|
+
structurally invalid, a `WalletError` is raised rather than storing unverified
|
|
23
|
+
data. When the chain provider supports `valid_root_for_height?`, full SPV
|
|
24
|
+
verification is performed.
|
|
25
|
+
- **Depth cap and cycle detection in `wire_source_tx_ancestors`** (F8.18):
|
|
26
|
+
Recursion is now bounded by `WalletClient::ANCESTOR_DEPTH_CAP` (64 levels)
|
|
27
|
+
and a visited-txid `Set`, preventing stack overflow on deep or cyclic
|
|
28
|
+
transaction ancestry chains.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **`ProtoWallet#create_signature` default counterparty** (P305.1): The default
|
|
33
|
+
value for `counterparty` has changed from `'self'` to `'anyone'`, matching the
|
|
34
|
+
behaviour of `ts-sdk`'s `ProtoWallet.createSignature`. Callers that rely on
|
|
35
|
+
the `'self'` derivation path when omitting `counterparty:` must now pass
|
|
36
|
+
`counterparty: 'self'` explicitly.
|
|
37
|
+
|
|
38
|
+
### Migration notes
|
|
39
|
+
|
|
40
|
+
**P305.1 — `create_signature` counterparty default change (breaking)**
|
|
41
|
+
|
|
42
|
+
Previously, calling `wallet.create_signature({ protocol_id: ..., key_id: ...,
|
|
43
|
+
data: ... })` without a `counterparty:` key would derive using `'self'`. It now
|
|
44
|
+
derives using `'anyone'`. This changes the resulting private key and therefore
|
|
45
|
+
the signature. If your application omits `counterparty:`, add
|
|
46
|
+
`counterparty: 'self'` to preserve the old behaviour.
|
|
47
|
+
|
|
48
|
+
**F8.14 — BEEF verification now mandatory in `internalize_action`**
|
|
49
|
+
|
|
50
|
+
Calls to `internalize_action` that previously succeeded with a malformed or
|
|
51
|
+
unverifiable BEEF will now raise `BSV::Wallet::WalletError`. In practice this
|
|
52
|
+
only affects callers passing synthetic or hand-crafted BEEF bytes; legitimate
|
|
53
|
+
BEEF produced by `create_action` or broadcast round-trips will continue to work.
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- **Shared conformance suite** for `StorageAdapter`
|
|
58
|
+
implementations at `spec/support/shared_examples_for_storage_adapter.rb`.
|
|
59
|
+
`MemoryStore` and `FileStore` now both drive their behavioural
|
|
60
|
+
tests through `it_behaves_like 'a storage adapter'`, and the
|
|
61
|
+
extraction backfilled previously-missing coverage (certificate
|
|
62
|
+
`:attributes` filter, `count_certificates`, proof and transaction
|
|
63
|
+
round-trip, pagination ordering).
|
|
64
|
+
|
|
65
|
+
## 0.3.4 — 2026-04-08
|
|
66
|
+
|
|
67
|
+
Paired security patch release. Three P0 findings from the
|
|
68
|
+
[2026-04-08 cross-SDK compliance review](.architecture/reviews/20260408-cross-sdk-compliance-review.md)
|
|
69
|
+
plus follow-up hardening from the PR review pass. Must be installed
|
|
70
|
+
together — the `bsv-wallet` gemspec now pins its `bsv-sdk` dependency
|
|
71
|
+
to `>= 0.8.2, < 1.0` to enforce the paired upgrade and prevent a stale
|
|
72
|
+
pair where one gem has its fixes and the other doesn't.
|
|
73
|
+
|
|
74
|
+
Two GitHub Security Advisories accompany this release (draft until
|
|
75
|
+
CVE IDs return from MITRE):
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
### Security
|
|
79
|
+
|
|
80
|
+
- **`acquire_certificate` now verifies certifier signatures** before
|
|
81
|
+
persisting (BRC-52). Both the `'direct'` and `'issuance'` acquisition
|
|
82
|
+
paths previously wrote user-supplied `signature:` values to storage
|
|
83
|
+
without any verification — a caller could forge a certificate that
|
|
84
|
+
`list_certificates` / `prove_certificate` would later treat as
|
|
85
|
+
authentic. This was a credential forgery primitive masquerading as
|
|
86
|
+
an API finding. The new `BSV::Wallet::CertificateSignature` module
|
|
87
|
+
builds the canonical BRC-52 preimage (matching the TS reference
|
|
88
|
+
`Certificate#toBinary(false)` byte-for-byte) and delegates to
|
|
89
|
+
`ProtoWallet#verify_signature`. Invalid certificates raise
|
|
90
|
+
`BSV::Wallet::CertificateSignature::InvalidError` and are not
|
|
91
|
+
persisted. Closes F8.15 (and the verification aspect of F8.16).
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### Changed
|
|
96
|
+
|
|
97
|
+
- **`bsv-wallet.gemspec` bsv-sdk dependency pinned** to
|
|
98
|
+
`>= 0.8.2, < 1.0`. The previous `~> 0.4` constraint was stale (wallet
|
|
99
|
+
hasn't been tested against bsv-sdk 0.4.x in months) and would have
|
|
100
|
+
let a user install `bsv-wallet 0.3.4` against an old `bsv-sdk` that
|
|
101
|
+
was missing F1.3 and F5.13. Technically breaking — any consumer
|
|
102
|
+
pinned to `bsv-sdk < 0.8.2` must upgrade — but un-breaking in
|
|
103
|
+
practice: it forces users to the known-good pair rather than a
|
|
104
|
+
silently-broken combination.
|
|
105
|
+
|
|
106
|
+
### Internal
|
|
107
|
+
|
|
108
|
+
- `lib/bsv/wallet_interface/**/*` added to the
|
|
109
|
+
`Metrics/ModuleLength` exclusion list (was previously only excluded
|
|
110
|
+
from `Metrics/ClassLength`). The new `CertificateSignature` module
|
|
111
|
+
triggered the discrepancy.
|
|
112
|
+
- Review-feedback hardening bundled into the same PR to
|
|
113
|
+
keep the security-patch window small: case-insensitive ARC failure
|
|
114
|
+
matching, `Base64.strict_decode64` on BRC-52 preimage fields,
|
|
115
|
+
`EncodingError` rescue in `CertificateSignature.verify!`, rejection
|
|
116
|
+
of mixed string / symbol duplicate field names, malformed 2xx
|
|
117
|
+
rejection in ARC, and even-length guard on hex signatures.
|
|
118
|
+
|
|
119
|
+
### Migration notes
|
|
120
|
+
|
|
121
|
+
- **Existing `bsv-wallet` users** pinned to `bsv-sdk ~> 0.4` will need
|
|
122
|
+
to relax their constraint or upgrade. Anything installed before
|
|
123
|
+
`bsv-wallet 0.3.4` is vulnerable to the F8.15 certificate forgery
|
|
124
|
+
primitive.
|
|
125
|
+
- **Callers passing negative integers to `VarInt.encode`** (unlikely —
|
|
126
|
+
the docstring already disallowed it) will now get an `ArgumentError`
|
|
127
|
+
instead of silent corruption. Fix: pass non-negative values.
|
|
128
|
+
- **Callers relying on ARC broadcaster silently succeeding for INVALID
|
|
129
|
+
/ MALFORMED / MINED_IN_STALE_BLOCK / ORPHAN responses** will now see
|
|
130
|
+
`BroadcastError` raised. Fix: handle the error — the previous
|
|
131
|
+
behaviour was objectively wrong and any downstream logic that
|
|
132
|
+
tolerated it was silently corrupt.
|
|
133
|
+
- **Callers of `acquire_certificate` with a fake or untrusted
|
|
134
|
+
`signature:` field** will now see
|
|
135
|
+
`BSV::Wallet::CertificateSignature::InvalidError`. Fix: ensure the
|
|
136
|
+
certificate has been properly signed by the declared certifier.
|
|
137
|
+
|
|
138
|
+
### Test suite
|
|
139
|
+
|
|
140
|
+
- 3112 examples, 0 failures (up from 3080 on 0.8.1)
|
|
141
|
+
- 16 new regression tests for F1.3, F5.13, and F8.15
|
|
142
|
+
- 16 further regression tests for the review-feedback hardening
|
|
143
|
+
- Ruby 2.7 — 3.4 matrix green
|
|
144
|
+
- CodeQL clean; RuboCop clean across 266 files
|
|
145
|
+
|
|
146
|
+
## 0.3.3 — 2026-04-06
|
|
147
|
+
|
|
148
|
+
### Fixed
|
|
149
|
+
|
|
150
|
+
- `finalize_action` now stores the spending transaction so subsequent
|
|
151
|
+
`internalize_action` / proof resolution flows can find it. Previously the
|
|
152
|
+
wallet remembered the inputs and outputs but not the finalised tx itself.
|
|
153
|
+
|
|
154
|
+
## 0.3.2 — 2026-04-06
|
|
155
|
+
|
|
156
|
+
### Fixed
|
|
157
|
+
|
|
158
|
+
- `internalize_action` now stores **all** transactions from the
|
|
159
|
+
incoming BEEF, not just the proven ones. Unproven ancestors are needed for
|
|
160
|
+
later BEEF reconstruction in `create_action` → `to_beef`.
|
|
161
|
+
|
|
162
|
+
## 0.3.1 — 2026-04-06
|
|
163
|
+
|
|
164
|
+
### Fixed
|
|
165
|
+
|
|
166
|
+
- `internalize_action` now stores the subject transaction hex (not
|
|
167
|
+
just its proof and outputs), so the wallet can rebuild BEEF for spends of
|
|
168
|
+
the inbound outputs without re-fetching the tx.
|
|
169
|
+
|
|
170
|
+
## 0.3.0 — 2026-04-06
|
|
171
|
+
|
|
172
|
+
### Added
|
|
173
|
+
|
|
174
|
+
- **Pluggable proof store** for merkle proof persistence. The wallet
|
|
175
|
+
is now a lightweight SPV node: `internalize_action` extracts and stores
|
|
176
|
+
merkle proofs from incoming BEEF; `create_action` reattaches them to
|
|
177
|
+
produce valid BEEF with BUMPs for ARC broadcast.
|
|
178
|
+
- `ProofStore` interface with `store_proof` / `resolve_proof`.
|
|
179
|
+
- `LocalProofStore` default implementation using `StorageAdapter`.
|
|
180
|
+
- `WalletClient` accepts injectable `proof_store:` parameter.
|
|
181
|
+
- Transaction caching (`store_transaction` / `find_transaction`) for
|
|
182
|
+
ancestry reconstruction.
|
|
183
|
+
- `StorageAdapter` gains `store_proof`, `find_proof`,
|
|
184
|
+
`store_transaction`, `find_transaction` methods, implemented in both
|
|
185
|
+
`MemoryStore` and `FileStore`.
|
|
186
|
+
|
|
187
|
+
### Fixed
|
|
188
|
+
|
|
189
|
+
- `wire_source_from_storage` resolves merkle proofs via proof store
|
|
190
|
+
so `to_beef` produces valid BEEF that ARC accepts. Previously, BEEF
|
|
191
|
+
contained source transactions without proofs, causing ARC 463/468
|
|
192
|
+
rejections.
|
|
193
|
+
|
|
194
|
+
## 0.2.2 — 2026-04-06
|
|
195
|
+
|
|
196
|
+
### Fixed
|
|
197
|
+
|
|
198
|
+
- `to_beef` now includes source transactions in the BEEF output, not
|
|
199
|
+
just the subject transaction. Without ancestors, ARC could not validate the
|
|
200
|
+
spend graph.
|
|
201
|
+
|
|
202
|
+
## 0.2.1 — 2026-04-06
|
|
203
|
+
|
|
204
|
+
### Added
|
|
205
|
+
|
|
206
|
+
- `WalletClient#create_action` now accepts `UnlockingScriptTemplate`
|
|
207
|
+
objects (e.g. `P2PKH`) as input unlocking scripts, enabling template-based
|
|
208
|
+
signing without BEEF.
|
|
209
|
+
- `wire_source_from_storage` fallback populates `source_satoshis`
|
|
210
|
+
and `source_locking_script` from wallet storage when BEEF is absent or
|
|
211
|
+
incomplete, enabling BIP-143 sighash computation for wallet-tracked
|
|
212
|
+
outputs.
|
|
213
|
+
- `finalize_action` resolves template inputs via `sign_all` before
|
|
214
|
+
serialisation.
|
|
215
|
+
- `MemoryStore#filter_outputs` supports outpoint filtering for
|
|
216
|
+
efficient single-output lookups.
|
|
217
|
+
|
|
218
|
+
The sdk gem was re-released alongside this wallet change with no
|
|
219
|
+
behavioural changes of its own.
|
|
220
|
+
|
|
221
|
+
## 0.2.0 — 2026-04-01
|
|
222
|
+
|
|
223
|
+
### Added
|
|
224
|
+
|
|
225
|
+
#### Primitives
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
#### Transaction
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
#### Wallet
|
|
232
|
+
|
|
233
|
+
- **FileStore** — JSON file-backed persistent storage, now the
|
|
234
|
+
default for `WalletClient`. Data survives process restarts. `MemoryStore`
|
|
235
|
+
becomes explicit opt-in for tests.
|
|
236
|
+
- **File permissions** — directory created with 0700, files with
|
|
237
|
+
0600. Warns via Logger on startup if permissions are too open.
|
|
238
|
+
|
|
239
|
+
## 0.1.2 — 2026-03-30
|
|
240
|
+
|
|
241
|
+
### Added
|
|
242
|
+
|
|
243
|
+
#### Script
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
#### Transaction
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
#### Wallet
|
|
250
|
+
|
|
251
|
+
- **BRC-31 Auth/Peer** — mutual authentication with nonce-based
|
|
252
|
+
challenges, ECDSA signatures, and session management.
|
|
253
|
+
- **BRC-100 wire protocol** — binary ABI serialisation for all 28
|
|
254
|
+
BRC-100 methods (call codes 1-28, VarInt encoding).
|
|
255
|
+
- **Certificate issuance** — `acquire_certificate` with
|
|
256
|
+
`'issuance'` protocol (POST to certifier URL).
|
|
257
|
+
|
|
258
|
+
### Fixed
|
|
259
|
+
|
|
260
|
+
- Subject and certifier pinned in certificate issuance response
|
|
261
|
+
(not overridable by remote certifier).
|
|
262
|
+
- Wire reader negative `privileged_reason` length crash.
|
|
263
|
+
|
|
264
|
+
This was the first formal `bsv-wallet` gem release tag. Wallet code that
|
|
265
|
+
landed in master before this date (notably the BRC-100 identity certificate
|
|
266
|
+
methods and the BRC-100 blockchain-data / authentication methods committed
|
|
267
|
+
during the sdk-0.3.1 window) is part of this gem's initial released state.
|
|
@@ -141,7 +141,7 @@ module BSV
|
|
|
141
141
|
# @param originator [String, nil] FQDN of the originating application
|
|
142
142
|
# @return [Hash] { signature: Array<Integer> } DER-encoded signature as byte array
|
|
143
143
|
def create_signature(args, originator: nil)
|
|
144
|
-
counterparty = args[:counterparty] || '
|
|
144
|
+
counterparty = args[:counterparty] || 'anyone'
|
|
145
145
|
priv_key = @key_deriver.derive_private_key(args[:protocol_id], args[:key_id], counterparty)
|
|
146
146
|
|
|
147
147
|
hash = if args[:hash_to_directly_sign]
|
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
module BSV
|
|
4
4
|
module Wallet
|
|
5
5
|
module Validators
|
|
6
|
+
# Reserved protocol name prefixes (BRC-44 and BRC-98).
|
|
7
|
+
# Names beginning with any of these strings are disallowed.
|
|
8
|
+
RESERVED_PROTOCOL_PREFIXES = ['admin', 'p '].freeze
|
|
9
|
+
|
|
10
|
+
# Suffix that is disallowed on protocol names.
|
|
11
|
+
RESERVED_PROTOCOL_SUFFIX = ' protocol'
|
|
12
|
+
|
|
13
|
+
# Reserved basket name prefixes.
|
|
14
|
+
# Basket names beginning with any of these strings are disallowed.
|
|
15
|
+
RESERVED_BASKET_PREFIXES = ['admin', 'p '].freeze
|
|
16
|
+
|
|
17
|
+
# Suffix that is disallowed on basket names.
|
|
18
|
+
RESERVED_BASKET_SUFFIX = ' basket'
|
|
19
|
+
|
|
20
|
+
# Basket name that is globally reserved and cannot be used.
|
|
21
|
+
RESERVED_BASKET_NAME = 'default'
|
|
22
|
+
|
|
6
23
|
module_function
|
|
7
24
|
|
|
8
25
|
# BRC-100 protocol ID rules:
|
|
@@ -14,6 +31,10 @@ module BSV
|
|
|
14
31
|
# - must not end with ' protocol'
|
|
15
32
|
# - must not start with 'admin' (BRC-44)
|
|
16
33
|
# - must not start with 'p ' (BRC-98 reserved)
|
|
34
|
+
#
|
|
35
|
+
# The name is normalised (stripped and downcased) before validation so
|
|
36
|
+
# that ' MyProtocol ' and 'myprotocol' are treated identically and do not
|
|
37
|
+
# silently fork to different key-derivation paths (F8.7).
|
|
17
38
|
def validate_protocol_id!(protocol_id)
|
|
18
39
|
unless protocol_id.is_a?(Array) && protocol_id.length == 2
|
|
19
40
|
raise InvalidParameterError.new('protocol_id',
|
|
@@ -24,13 +45,19 @@ module BSV
|
|
|
24
45
|
raise InvalidParameterError.new('protocol_id security level', '0, 1, or 2') unless [0, 1, 2].include?(level)
|
|
25
46
|
raise InvalidParameterError.new('protocol_id name', 'a String') unless name.is_a?(String)
|
|
26
47
|
|
|
48
|
+
name = name.strip.downcase
|
|
49
|
+
|
|
27
50
|
max_length = name.start_with?('specific linkage revelation') ? 430 : 400
|
|
28
51
|
raise InvalidParameterError.new('protocol_id name', "between 5 and #{max_length} characters") if name.length < 5 || name.length > max_length
|
|
29
52
|
raise InvalidParameterError.new('protocol_id name', 'lowercase letters, numbers, and spaces only') unless name.match?(/\A[a-z0-9 ]+\z/)
|
|
30
53
|
raise InvalidParameterError.new('protocol_id name', 'free of consecutive spaces') if name.include?(' ')
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
if name.end_with?(RESERVED_PROTOCOL_SUFFIX)
|
|
55
|
+
raise InvalidParameterError.new('protocol_id name', "not ending with \"#{RESERVED_PROTOCOL_SUFFIX}\"")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
RESERVED_PROTOCOL_PREFIXES.each do |prefix|
|
|
59
|
+
raise InvalidParameterError.new('protocol_id name', "not starting with \"#{prefix}\"") if name.start_with?(prefix)
|
|
60
|
+
end
|
|
34
61
|
end
|
|
35
62
|
|
|
36
63
|
# Key ID: 1-800 bytes
|
|
@@ -67,10 +94,12 @@ module BSV
|
|
|
67
94
|
raise InvalidParameterError.new('basket', 'between 5 and 300 characters') if basket.length < 5 || basket.length > 300
|
|
68
95
|
raise InvalidParameterError.new('basket', 'lowercase letters, numbers, and spaces only') unless basket.match?(/\A[a-z0-9 ]+\z/)
|
|
69
96
|
raise InvalidParameterError.new('basket', 'free of consecutive spaces') if basket.include?(' ')
|
|
70
|
-
raise InvalidParameterError.new('basket',
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
97
|
+
raise InvalidParameterError.new('basket', "not ending with \"#{RESERVED_BASKET_SUFFIX}\"") if basket.end_with?(RESERVED_BASKET_SUFFIX)
|
|
98
|
+
|
|
99
|
+
RESERVED_BASKET_PREFIXES.each do |prefix|
|
|
100
|
+
raise InvalidParameterError.new('basket', "not starting with \"#{prefix}\"") if basket.start_with?(prefix)
|
|
101
|
+
end
|
|
102
|
+
raise InvalidParameterError.new('basket', "not equal to \"#{RESERVED_BASKET_NAME}\"") if basket == RESERVED_BASKET_NAME
|
|
74
103
|
end
|
|
75
104
|
|
|
76
105
|
# Label: 1-300 characters
|
|
@@ -4,6 +4,7 @@ require 'securerandom'
|
|
|
4
4
|
require 'base64'
|
|
5
5
|
require 'net/http'
|
|
6
6
|
require 'json'
|
|
7
|
+
require 'set'
|
|
7
8
|
require 'uri'
|
|
8
9
|
|
|
9
10
|
module BSV
|
|
@@ -180,6 +181,13 @@ module BSV
|
|
|
180
181
|
validate_internalize_action!(args)
|
|
181
182
|
beef_binary = args[:tx].pack('C*')
|
|
182
183
|
beef = BSV::Transaction::Beef.from_binary(beef_binary)
|
|
184
|
+
|
|
185
|
+
# F8.14: verify the BEEF bundle before trusting its contents.
|
|
186
|
+
# Pass the chain provider if it supports SPV root verification;
|
|
187
|
+
# otherwise fall back to structural validation via valid?.
|
|
188
|
+
chain_tracker = @chain_provider.respond_to?(:valid_root_for_height?) ? @chain_provider : nil
|
|
189
|
+
raise WalletError, 'BEEF verification failed: the bundle is structurally invalid' unless beef.verify(chain_tracker)
|
|
190
|
+
|
|
183
191
|
tx = extract_subject_transaction(beef)
|
|
184
192
|
|
|
185
193
|
store_proofs_from_beef(beef)
|
|
@@ -400,6 +408,10 @@ module BSV
|
|
|
400
408
|
{ total_certificates: total, certificates: certs.map { |c| cert_without_keyring(c) } }
|
|
401
409
|
end
|
|
402
410
|
|
|
411
|
+
# Maximum ancestor depth to traverse when wiring source transactions.
|
|
412
|
+
# Guards against stack overflow on pathologically deep or cyclic chains.
|
|
413
|
+
ANCESTOR_DEPTH_CAP = 64
|
|
414
|
+
|
|
403
415
|
private
|
|
404
416
|
|
|
405
417
|
# --- Validation ---
|
|
@@ -542,18 +554,28 @@ module BSV
|
|
|
542
554
|
input.source_transaction = source_tx
|
|
543
555
|
end
|
|
544
556
|
|
|
545
|
-
def wire_source_tx_ancestors(tx)
|
|
557
|
+
def wire_source_tx_ancestors(tx, visited: nil, depth: 0)
|
|
558
|
+
return if depth >= ANCESTOR_DEPTH_CAP
|
|
559
|
+
|
|
560
|
+
visited ||= Set.new
|
|
561
|
+
tx_txid = tx.txid_hex
|
|
562
|
+
return if visited.include?(tx_txid)
|
|
563
|
+
|
|
564
|
+
visited.add(tx_txid)
|
|
565
|
+
|
|
546
566
|
tx.inputs.each do |inp|
|
|
547
567
|
next if inp.source_transaction
|
|
548
568
|
|
|
549
569
|
ancestor_txid_hex = inp.prev_tx_id.reverse.unpack1('H*')
|
|
570
|
+
next if visited.include?(ancestor_txid_hex)
|
|
571
|
+
|
|
550
572
|
tx_hex = @storage.find_transaction(ancestor_txid_hex)
|
|
551
573
|
next unless tx_hex
|
|
552
574
|
|
|
553
575
|
ancestor_tx = BSV::Transaction::Transaction.from_hex(tx_hex)
|
|
554
576
|
proof = @proof_store.resolve_proof(ancestor_txid_hex)
|
|
555
577
|
ancestor_tx.merkle_path = proof if proof
|
|
556
|
-
wire_source_tx_ancestors(ancestor_tx) unless ancestor_tx.merkle_path
|
|
578
|
+
wire_source_tx_ancestors(ancestor_tx, visited: visited, depth: depth + 1) unless ancestor_tx.merkle_path
|
|
557
579
|
inp.source_transaction = ancestor_tx
|
|
558
580
|
end
|
|
559
581
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bsv-wallet
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.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-10 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -29,7 +29,7 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.
|
|
32
|
+
version: 0.9.0
|
|
33
33
|
- - "<"
|
|
34
34
|
- !ruby/object:Gem::Version
|
|
35
35
|
version: '1.0'
|
|
@@ -39,7 +39,7 @@ dependencies:
|
|
|
39
39
|
requirements:
|
|
40
40
|
- - ">="
|
|
41
41
|
- !ruby/object:Gem::Version
|
|
42
|
-
version: 0.
|
|
42
|
+
version: 0.9.0
|
|
43
43
|
- - "<"
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
45
|
version: '1.0'
|
|
@@ -49,6 +49,7 @@ executables: []
|
|
|
49
49
|
extensions: []
|
|
50
50
|
extra_rdoc_files: []
|
|
51
51
|
files:
|
|
52
|
+
- CHANGELOG-wallet.md
|
|
52
53
|
- LICENSE
|
|
53
54
|
- lib/bsv-wallet.rb
|
|
54
55
|
- lib/bsv/wallet_interface.rb
|
|
@@ -81,7 +82,7 @@ licenses:
|
|
|
81
82
|
metadata:
|
|
82
83
|
homepage_uri: https://github.com/sgbett/bsv-ruby-sdk
|
|
83
84
|
source_code_uri: https://github.com/sgbett/bsv-ruby-sdk
|
|
84
|
-
changelog_uri: https://github.com/sgbett/bsv-ruby-sdk/blob/master/CHANGELOG.md
|
|
85
|
+
changelog_uri: https://github.com/sgbett/bsv-ruby-sdk/blob/master/CHANGELOG-wallet.md
|
|
85
86
|
rubygems_mfa_required: 'true'
|
|
86
87
|
rdoc_options: []
|
|
87
88
|
require_paths:
|