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) |         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
 |     /// Reindex the heights in the chain from (and including) `from` height
 | ||||||
|     fn reindex(&mut self, from: u32) { |     fn reindex(&mut self, from: u32) { | ||||||
|         let _ = self.index.split_off(&from); |         let _ = self.index.split_off(&from); | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| use bdk_chain::local_chain::{ | use bdk_chain::local_chain::{ | ||||||
|     AlterCheckPointError, CannotConnectError, ChangeSet, LocalChain, Update, |     AlterCheckPointError, CannotConnectError, ChangeSet, LocalChain, MissingGenesisError, Update, | ||||||
| }; | }; | ||||||
| use bitcoin::BlockHash; | use bitcoin::BlockHash; | ||||||
| 
 | 
 | ||||||
| @ -350,3 +350,76 @@ fn local_chain_insert_block() { | |||||||
|         assert_eq!(chain, t.expected_final, "[{}] unexpected final chain", i,); |         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