#![forbid(unsafe_code)]
#[path = "common/mod.rs"]
#[allow(dead_code)]
mod common;
use dig_blockstore::BlockStore;
use common::{build_chain, temp_blockstore_dir, test_config};
#[test]
fn test_rollback_removes_canonical_entries() {
let (_guard, path) = temp_blockstore_dir();
let store = BlockStore::open(test_config(path)).expect("open");
let chain = build_chain(10);
for block in &chain {
store.extend_chain(block).expect("extend");
}
let reverted = store.rollback_to_height(5).expect("rollback");
for h in 6..10 {
assert!(
store.get_hash_by_height(h).expect("h").is_none(),
"height {h} should not be canonical after rollback to 5"
);
}
for h in 0..=5 {
assert!(
store.get_hash_by_height(h).expect("h").is_some(),
"height {h} should remain canonical"
);
}
assert_eq!(reverted.len(), 4, "heights 9,8,7,6 reverted");
assert_eq!(reverted[0], chain[9].hash(), "tip first");
assert_eq!(reverted[3], chain[6].hash(), "lowest reverted last");
}
#[test]
fn test_rollback_updates_tip() {
let (_guard, path) = temp_blockstore_dir();
let store = BlockStore::open(test_config(path)).expect("open");
let chain = build_chain(8);
for block in &chain {
store.extend_chain(block).expect("extend");
}
store.rollback_to_height(3).expect("rollback");
let tip = store.tip().expect("tip");
assert_eq!(tip.height, 3);
assert_eq!(tip.hash, chain[3].hash());
}
#[test]
fn test_rollback_preserves_block_data() {
let (_guard, path) = temp_blockstore_dir();
let store = BlockStore::open(test_config(path)).expect("open");
let chain = build_chain(6);
for block in &chain {
store.extend_chain(block).expect("extend");
}
store.rollback_to_height(2).expect("rollback");
for (i, block) in chain.iter().enumerate() {
let got = store
.get_block(&block.hash())
.expect("get")
.expect("present");
assert_eq!(got.hash(), block.hash(), "block {i} preserved");
}
}
#[test]
fn test_rollback_marks_records_non_canonical() {
let (_guard, path) = temp_blockstore_dir();
let store = BlockStore::open(test_config(path)).expect("open");
let chain = build_chain(5);
for block in &chain {
store.extend_chain(block).expect("extend");
}
store.rollback_to_height(2).expect("rollback");
for block in &chain[3..] {
let rec = store.get_record(&block.hash()).expect("rec").expect("some");
assert!(
!rec.in_canonical_chain,
"block at height {} should be non-canonical after rollback",
rec.height
);
}
for block in &chain[..=2] {
let rec = store.get_record(&block.hash()).expect("rec").expect("some");
assert!(
rec.in_canonical_chain,
"block at height {} should still be canonical",
rec.height
);
}
}
#[test]
fn test_rollback_at_tip_is_noop() {
let (_guard, path) = temp_blockstore_dir();
let store = BlockStore::open(test_config(path)).expect("open");
let chain = build_chain(5);
for block in &chain {
store.extend_chain(block).expect("extend");
}
let tip_before = store.tip();
let reverted = store.rollback_to_height(4).expect("at-tip rollback");
assert!(reverted.is_empty());
assert_eq!(store.tip(), tip_before);
}
#[test]
fn test_rollback_returns_descending_order() {
let (_guard, path) = temp_blockstore_dir();
let store = BlockStore::open(test_config(path)).expect("open");
let chain = build_chain(8);
for block in &chain {
store.extend_chain(block).expect("extend");
}
let reverted = store.rollback_to_height(3).expect("rollback");
assert_eq!(reverted.len(), 4);
for (i, hash) in reverted.iter().enumerate() {
let expected_height = 7 - i;
assert_eq!(
*hash,
chain[expected_height].hash(),
"index {i} = height {expected_height}"
);
}
}