fix(wallet)!: make TxBuilder::current_height type-safe via absolute::Height - #449
fix(wallet)!: make TxBuilder::current_height type-safe via absolute::Height#449none34829 wants to merge 1 commit into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #449 +/- ##
=======================================
Coverage 80.21% 80.21%
=======================================
Files 24 24
Lines 5348 5348
Branches 242 242
=======================================
Hits 4290 4290
Misses 980 980
Partials 78 78
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
4065bc8 to
b8a0b5e
Compare
|
Thanks for the review- the type-safe approach is definitely cleaner.
|
|
CI failures here are unrelated to this PR- I've opened #455 with a focused mechanical rename to |
…_unchecked be710a8 chore(tests): replace deprecated FeeRate::from_sat_per_vb_unchecked (none34829) Pull request description: ### Description `bitcoin-units 0.1.3` deprecated `FeeRate::from_sat_per_vb_unchecked` in favor of `FeeRate::from_sat_per_vb_u32`. Cargo resolves any 0.1.x automatically, so fresh CI runs started failing on master (and all open PRs) once 0.1.3 was published. This patch swaps every call site over to the new name. The two functions compute the same value for any input that fits in `u32`; every existing call site passes a small literal (1, 3, 5, 10, 25, 50, 255, 454, 1000, 10_000), so no behavioural change. All usages are in test/fixture code (`src/wallet/coin_selection.rs` test module, `tests/wallet.rs`, `tests/build_fee_bump.rs`) — no production code path was using the deprecated function. ### Notes to the reviewers - Pure mechanical rename; no logic or type changes. - Unblocks CI on master and every open PR (e.g. #449, #442, #445, #448). ### Changelog notice N/A — internal test-only change. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `just p` before pushing ACKs for top commit: ValuedMammal: ACK be710a8 Tree-SHA512: b2354fcf6e39228bc5796f92af4d3692fbdb750267c2c5a7d814fc5c84f4af64b746562fda7e01dac282ba04acb17ff7bf5c7ef0155a3d6f302974003baf0629
b8a0b5e to
6dac1a0
Compare
yan-pi
left a comment
There was a problem hiding this comment.
NACK (6dac1a0) on the current patch as a resolution for #49.
Accepting absolute::Height in the explicit setter is a good API improvement, but the fallback still panics for a height accepted through Wallet::apply_update and Wallet::load. I reproduced both paths at height 500_000_000 on the current head.
I think you can keep the typed setter, make the fallback conversion return CreateTxError::InvalidCurrentHeight, and add the regression test.
The PR description and checklist also need to be updated: they describe the old force-pushed implementation, the current signature is API-breaking, and the branch needs a rebase.
| // If the local chain tip height is not a valid block height for a locktime, | ||
| // that's an invariant violation in `LocalChain` (block heights are | ||
| // always below the locktime threshold) and should be addressed upstream. | ||
| absolute::LockTime::from_height(tip_height) | ||
| .expect("LocalChain tip height should be a valid block height") | ||
| } |
There was a problem hiding this comment.
The invariant described here is not enforced by LocalChain.
CheckPoint::new accepts a BlockId with any u32 height, CheckPoint::push only checks ordering, and LocalChain::from_changeset/apply_update do not check the locktime threshold.
The update below succeeds, after which transaction creation reaches this expect:
let tip = wallet.latest_checkpoint().push(BlockId {
height: 500_000_000,
hash: BlockHash::all_zeros(),
}).unwrap();
wallet.apply_update(Update {
chain: Some(tip),
..Default::default()
}).unwrap();
let mut builder = wallet.build_tx();
builder.add_recipient(script, amount);
let _ = builder.finish();I reproduced the same panic after taking the staged ChangeSet and loading it with Wallet::load().load_wallet_no_persist(...).
I'd keep absolute::Height for the explicit setter and make the fallback conversion return CreateTxError::InvalidCurrentHeight instead of relying on this invariant.
Please also add regression tests for the update and reload paths. The current tests only wrap existing valid constants in Height::from_consensus(...).unwrap() and do not exercise the audit finding.
Description
Closes #49.
TxBuilder::current_heightand the fallback tochain.tip().height()increate_txboth calledabsolute::LockTime::from_height(h).expect(...), which panics wheneverh >= 500_000_000. The Wizardsardine audit flagged this because it lets a remote Electrum/Esplora server crash the wallet by reporting a tip height at or above that threshold — panics on externally-provided input are unsafe for a library.This PR replaces the panics with a typed error:
TxParams.current_heightnow stores a rawu32instead of an already-validatedabsolute::LockTime, so the builder setter no longer has to validate in a position where it can't return aResult.Wallet::create_txperforms theu32 -> LockTimeconversion in one place, mapping theConversionErrorto a newCreateTxError::InvalidCurrentHeight(u32)variant via?.TxBuilder::current_height) and the implicit-fallback path (chain tip) now flow through the same validation and surface the same error.Notes to the reviewers
TxParams.current_heightis crate-private (pub(crate)) so there's no external API break there. TheTxBuilder::current_heightpublic signature is unchanged.CreateTxErroris technically an additive change, but it is an enum so downstream exhaustive matches would need to be updated.CreateTxErroris not marked#[non_exhaustive], which is tracked separately in Consider making all error enum variants#[non_exhaustive]#239.Changelog notice
Wallet::create_txnow returnsCreateTxError::InvalidCurrentHeightinstead of panicking whenTxBuilder::current_heightor the fallback chain tip height is>= 500_000_000.Checklists
All Submissions:
just pbefore pushing (140 lib tests + 118 wallet + 22 fee_bump + 10 persisted + 13 descriptor_macro + 54 doctests all pass; clippy clean; fmt clean; cargo doc clean)Bugfixes: