Skip to content

Commit 82d7852

Browse files
randomloginfebyeji
authored andcommitted
Add test to capture wallet checkpoint push problems
1 parent 5ad44bd commit 82d7852

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

0 commit comments

Comments
 (0)