Merge bitcoindevkit/bdk#1276: Add LocalChain::disconnect_from
method
bf67519768d91b049bf3a47c234ff71a81d0ede9 feat(chain): add `LocalChain::disconnect_from` method (志宇) Pull request description: Closes #1271 ### Description Add a method for disconnecting a chain of blocks starting from the given `BlockId`. ### Notes to the reviewers I want to have this for https://github.com/utreexo/utreexod/pull/110 ### Changelog notice Added * `LocalChain::disconnect_from` method to evict a chain of blocks starting from a given `BlockId`. ### 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 `cargo fmt` and `cargo clippy` before committing #### New Features: * [x] I've added tests for the new feature * [x] I've added docs for the new feature ACKs for top commit: danielabrozzoni: ACK bf67519768d91b049bf3a47c234ff71a81d0ede9 Tree-SHA512: e6bd213b49b553355370994567722ad2c3460d11fcd81adc65a85e5d03822d3c38e4a4d7f967044991cf0187845467b67d035bf8904871f9fcc4ea61be761ef7
This commit is contained in:
commit
40f0765d30
@ -420,6 +420,28 @@ impl LocalChain {
|
||||
Ok(changeset)
|
||||
}
|
||||
|
||||
/// Removes blocks from (and inclusive of) the given `block_id`.
|
||||
///
|
||||
/// This will remove blocks with a height equal or greater than `block_id`, but only if
|
||||
/// `block_id` exists in the chain.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This will fail with [`MissingGenesisError`] if the caller attempts to disconnect from the
|
||||
/// genesis block.
|
||||
pub fn disconnect_from(&mut self, block_id: BlockId) -> Result<ChangeSet, MissingGenesisError> {
|
||||
if self.index.get(&block_id.height) != Some(&block_id.hash) {
|
||||
return Ok(ChangeSet::default());
|
||||
}
|
||||
|
||||
let changeset = self
|
||||
.index
|
||||
.range(block_id.height..)
|
||||
.map(|(&height, _)| (height, None))
|
||||
.collect::<ChangeSet>();
|
||||
self.apply_changeset(&changeset).map(|_| changeset)
|
||||
}
|
||||
|
||||
/// Reindex the heights in the chain from (and including) `from` height
|
||||
fn reindex(&mut self, from: u32) {
|
||||
let _ = self.index.split_off(&from);
|
||||
|
@ -1,5 +1,5 @@
|
||||
use bdk_chain::local_chain::{
|
||||
AlterCheckPointError, CannotConnectError, ChangeSet, LocalChain, Update,
|
||||
AlterCheckPointError, CannotConnectError, ChangeSet, LocalChain, MissingGenesisError, Update,
|
||||
};
|
||||
use bitcoin::BlockHash;
|
||||
|
||||
@ -350,3 +350,76 @@ fn local_chain_insert_block() {
|
||||
assert_eq!(chain, t.expected_final, "[{}] unexpected final chain", i,);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_chain_disconnect_from() {
|
||||
struct TestCase {
|
||||
name: &'static str,
|
||||
original: LocalChain,
|
||||
disconnect_from: (u32, BlockHash),
|
||||
exp_result: Result<ChangeSet, MissingGenesisError>,
|
||||
exp_final: LocalChain,
|
||||
}
|
||||
|
||||
let test_cases = [
|
||||
TestCase {
|
||||
name: "try_replace_genesis_should_fail",
|
||||
original: local_chain![(0, h!("_"))],
|
||||
disconnect_from: (0, h!("_")),
|
||||
exp_result: Err(MissingGenesisError),
|
||||
exp_final: local_chain![(0, h!("_"))],
|
||||
},
|
||||
TestCase {
|
||||
name: "try_replace_genesis_should_fail_2",
|
||||
original: local_chain![(0, h!("_")), (2, h!("B")), (3, h!("C"))],
|
||||
disconnect_from: (0, h!("_")),
|
||||
exp_result: Err(MissingGenesisError),
|
||||
exp_final: local_chain![(0, h!("_")), (2, h!("B")), (3, h!("C"))],
|
||||
},
|
||||
TestCase {
|
||||
name: "from_does_not_exist",
|
||||
original: local_chain![(0, h!("_")), (3, h!("C"))],
|
||||
disconnect_from: (2, h!("B")),
|
||||
exp_result: Ok(ChangeSet::default()),
|
||||
exp_final: local_chain![(0, h!("_")), (3, h!("C"))],
|
||||
},
|
||||
TestCase {
|
||||
name: "from_has_different_blockhash",
|
||||
original: local_chain![(0, h!("_")), (2, h!("B"))],
|
||||
disconnect_from: (2, h!("not_B")),
|
||||
exp_result: Ok(ChangeSet::default()),
|
||||
exp_final: local_chain![(0, h!("_")), (2, h!("B"))],
|
||||
},
|
||||
TestCase {
|
||||
name: "disconnect_one",
|
||||
original: local_chain![(0, h!("_")), (2, h!("B"))],
|
||||
disconnect_from: (2, h!("B")),
|
||||
exp_result: Ok(ChangeSet::from_iter([(2, None)])),
|
||||
exp_final: local_chain![(0, h!("_"))],
|
||||
},
|
||||
TestCase {
|
||||
name: "disconnect_three",
|
||||
original: local_chain![(0, h!("_")), (2, h!("B")), (3, h!("C")), (4, h!("D"))],
|
||||
disconnect_from: (2, h!("B")),
|
||||
exp_result: Ok(ChangeSet::from_iter([(2, None), (3, None), (4, None)])),
|
||||
exp_final: local_chain![(0, h!("_"))],
|
||||
},
|
||||
];
|
||||
|
||||
for (i, t) in test_cases.into_iter().enumerate() {
|
||||
println!("Case {}: {}", i, t.name);
|
||||
|
||||
let mut chain = t.original;
|
||||
let result = chain.disconnect_from(t.disconnect_from.into());
|
||||
assert_eq!(
|
||||
result, t.exp_result,
|
||||
"[{}:{}] unexpected changeset result",
|
||||
i, t.name
|
||||
);
|
||||
assert_eq!(
|
||||
chain, t.exp_final,
|
||||
"[{}:{}] unexpected final chain",
|
||||
i, t.name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user