bitcoindevkit / bitcoindevkit/bdk
file_store: `Store::load` never terminates when a changeset entry decodes to zero bytes
- Dominant language
- Rust
- Stars
- 1.1k
- Forks
- 483
- Avg merge
- 20d 3h
- Merged PRs (30d)
- 3
Description
**Describe the bug**
`EntryIter::next` (`crates/file_store/src/entry_iter.rs`) treats every successful `deserialize_from` as progress but never checks that the read advanced the file offset. For a changeset type whose bincode encoding is zero bytes (e.g. `()`, which implements `Merge` in `bdk_core`), any trailing byte after the magic bytes makes `Store::dump` decode the same position over and over, so `Store::load` never returns.
This affects loading a corrupted or hand-edited store file. #2258 appears to address this by framing each entry with a length prefix.
This issue was found by AI.
**To Reproduce**
Add `crates/file_store/tests/test_zero_width.rs` and run `cargo test -p bdk_file_store --test test_zero_width`:
```rust
use bdk_file_store::Store;
use std::time::Duration;
const MAGIC: &[u8] = b"bdk_test_magic";
#[test]
fn load_terminates_on_zero_width_changeset() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("db");
let mut bytes = MAGIC.to_vec();
bytes.push(0xff); // any trailing byte
std::fs::write(&path, &bytes).unwrap();
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let result = Store::<()>::load(MAGIC, &path);
let _ = tx.send(result.is_ok());
});
// `()` decodes as zero bytes, so the file offset never advances and `load` never reaches EOF.
assert!(
rx.recv_timeout(Duration::from_secs(5)).is_ok(),
"load did not terminate"
);
}
```
Current output:
```
thread 'load_terminates_on_zero_width_changeset' panicked at crates/file_store/tests/test_zero_width.rs:20:5:
load did not terminate
```
**Expected behavior**
Loading a store file should always terminate, returning either the aggregated changeset or an error.
Contributor guide
Research direction
Start in crates/file_store/src/entry_iter.rs, especially EntryIter::next and its deserialization loop. Add or use crates/file_store/tests/test_zero_width.rs, then run cargo test -p bdk_file_store --test test_zero_width. Done means Store::load terminates for a zero-width changeset followed by a trailing byte and returns either the changeset or an error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 84/100