Skip to content

Commit 369862f

Browse files
randomloginfebyeji
authored andcommitted
Add test to capture wallet checkpoint push problems
1 parent 817e764 commit 369862f

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

src/chain/cbf.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,4 +1396,78 @@ mod tests {
13961396
}
13971397
}
13981398
}
1399+
1400+
/// Test that checkpoint building from `recent_history` handles reorgs.
1401+
///
1402+
/// Scenario: wallet synced to height 103. A 3-block reorg replaces blocks
1403+
/// 101-103 with new ones, and `recent_history` returns {97..=106} with
1404+
/// new hashes at heights 101-103.
1405+
///
1406+
/// The checkpoint must reflect the reorged chain: new hashes at 101-103,
1407+
/// pre-reorg blocks at ≤100 preserved, new blocks 104-106 present.
1408+
#[test]
1409+
fn checkpoint_building_handles_reorg() {
1410+
use bdk_chain::local_chain::LocalChain;
1411+
use bdk_chain::{BlockId, CheckPoint};
1412+
use bitcoin::BlockHash;
1413+
use std::collections::BTreeMap;
1414+
1415+
fn hash(seed: u32) -> BlockHash {
1416+
use bitcoin::hashes::{sha256d, Hash, HashEngine};
1417+
let mut engine = sha256d::Hash::engine();
1418+
engine.input(&seed.to_le_bytes());
1419+
BlockHash::from_raw_hash(sha256d::Hash::from_engine(engine))
1420+
}
1421+
1422+
let genesis = BlockId { height: 0, hash: hash(0) };
1423+
1424+
// Wallet checkpoint: 0 → 100 → 101 → 102 → 103
1425+
let wallet_cp = CheckPoint::from_block_ids([
1426+
genesis,
1427+
BlockId { height: 100, hash: hash(100) },
1428+
BlockId { height: 101, hash: hash(101) },
1429+
BlockId { height: 102, hash: hash(102) },
1430+
BlockId { height: 103, hash: hash(103) },
1431+
])
1432+
.unwrap();
1433+
1434+
// recent_history after reorg: 97-106, heights 101-103 have NEW hashes.
1435+
let recent_history: BTreeMap<u32, BlockHash> = (97..=106)
1436+
.map(|h| {
1437+
let seed = if (101..=103).contains(&h) { h + 1000 } else { h };
1438+
(h, hash(seed))
1439+
})
1440+
.collect();
1441+
1442+
// Build checkpoint using the same logic as sync_onchain_wallet.
1443+
let mut cp = wallet_cp;
1444+
for (height, block_hash) in &recent_history {
1445+
if *height > cp.height() {
1446+
let block_id = BlockId { height: *height, hash: *block_hash };
1447+
cp = cp.push(block_id).unwrap_or_else(|old| old);
1448+
}
1449+
}
1450+
1451+
// Reorged blocks must have the NEW hashes.
1452+
assert_eq!(cp.height(), 106);
1453+
assert_eq!(
1454+
cp.get(101).expect("height 101 must exist").hash(),
1455+
hash(1101),
1456+
"block 101 must have the reorged hash"
1457+
);
1458+
assert_eq!(cp.get(102).expect("height 102 must exist").hash(), hash(1102));
1459+
assert_eq!(cp.get(103).expect("height 103 must exist").hash(), hash(1103));
1460+
1461+
// Pre-reorg blocks are preserved.
1462+
assert_eq!(cp.get(100).expect("height 100 must exist").hash(), hash(100));
1463+
1464+
// New blocks above the reorg are present.
1465+
assert!(cp.get(104).is_some());
1466+
assert!(cp.get(105).is_some());
1467+
assert!(cp.get(106).is_some());
1468+
1469+
// The checkpoint must connect cleanly to a LocalChain.
1470+
let (mut chain, _) = LocalChain::from_genesis_hash(genesis.hash);
1471+
chain.apply_update(cp).expect("checkpoint must connect to chain");
1472+
}
13991473
}

0 commit comments

Comments
 (0)