@@ -13,11 +13,22 @@ pub use bdk_core::{CheckPoint, CheckPointIter};
1313use bitcoin:: block:: Header ;
1414use bitcoin:: BlockHash ;
1515
16+ /// Error for `apply_changeset_to_checkpoint`.
17+ #[ derive( Debug ) ]
18+ struct ApplyChangeSetError < D > {
19+ cp : Option < CheckPoint < D > > ,
20+ }
21+
1622/// Apply `changeset` to the checkpoint.
23+ ///
24+ /// # Errors
25+ ///
26+ /// - If constructing the new chain from the provided `changeset` fails, then a
27+ /// [`ApplyChangeSetError`] is returned.
1728fn apply_changeset_to_checkpoint < D > (
1829 mut init_cp : CheckPoint < D > ,
1930 changeset : & ChangeSet < D > ,
20- ) -> Result < CheckPoint < D > , MissingGenesisError >
31+ ) -> Result < CheckPoint < D > , ApplyChangeSetError < D > >
2132where
2233 D : ToBlockHash + fmt:: Debug + Clone ,
2334{
5061 let new_tip = match base {
5162 Some ( base) => base
5263 . extend ( extension)
53- . expect ( "extension is strictly greater than base" ) ,
54- None => LocalChain :: from_blocks ( extension) ?. tip ( ) ,
64+ . map_err ( Option :: Some )
65+ . map_err ( |cp| ApplyChangeSetError { cp } ) ?,
66+ None => LocalChain :: from_blocks ( extension)
67+ . map_err ( |cp| ApplyChangeSetError { cp } ) ?
68+ . tip ( ) ,
5569 } ;
5670 init_cp = new_tip;
5771 }
@@ -255,30 +269,36 @@ where
255269 ///
256270 /// The [`BTreeMap`] enforces the height order. However, the caller must ensure the blocks are
257271 /// all of the same chain.
258- pub fn from_blocks ( blocks : BTreeMap < u32 , D > ) -> Result < Self , MissingGenesisError > {
259- let genesis_hash = blocks
260- . get ( & 0 )
261- . map ( ToBlockHash :: to_blockhash)
262- . ok_or ( MissingGenesisError ) ?;
272+ ///
273+ /// Returns `Err(None)` if `blocks` is empty or doesn't contain a value at height `0` a.k.a
274+ /// the "genesis" block.
275+ /// If `blocks` contains inconsistent or invalid data, then returns `Err(Some(..))`
276+ /// containing the last valid checkpoint.
277+ pub fn from_blocks ( blocks : BTreeMap < u32 , D > ) -> Result < Self , Option < CheckPoint < D > > > {
278+ let genesis_hash = blocks. get ( & 0 ) . map ( ToBlockHash :: to_blockhash) . ok_or ( None ) ?;
279+ let tip = CheckPoint :: from_blocks ( blocks) ?;
263280
264- Ok ( Self {
265- genesis_hash,
266- tip : CheckPoint :: from_blocks ( blocks) . expect ( "blocks must be in order" ) ,
267- } )
281+ Ok ( Self { genesis_hash, tip } )
268282 }
269283
270284 /// Construct a [`LocalChain`] from an initial `changeset`.
271- pub fn from_changeset ( changeset : ChangeSet < D > ) -> Result < Self , MissingGenesisError > {
285+ ///
286+ /// # Returns
287+ ///
288+ /// - `Ok(None)` if `changeset` doesn't contain a value at height 0, a.k.a "genesis".
289+ /// - `CannotConnectError` if the `changeset` can't be applied.
290+ /// - Otherwise returns a new [`LocalChain`] with the changeset applied
291+ pub fn from_changeset ( changeset : ChangeSet < D > ) -> Result < Option < Self > , CannotConnectError > {
272292 let genesis_entry = changeset. blocks . get ( & 0 ) . cloned ( ) . flatten ( ) ;
273293 let genesis_data = match genesis_entry {
274294 Some ( data) => data,
275- None => return Err ( MissingGenesisError ) ,
295+ None => return Ok ( None ) ,
276296 } ;
277297
278298 let ( mut chain, _) = Self :: from_genesis ( genesis_data) ;
279299 chain. apply_changeset ( & changeset) ?;
280300 debug_assert ! ( chain. _check_changeset_is_applied( & changeset) ) ;
281- Ok ( chain)
301+ Ok ( Some ( chain) )
282302 }
283303
284304 /// Construct a [`LocalChain`] from a given `checkpoint` tip.
@@ -317,9 +337,12 @@ where
317337 }
318338
319339 /// Apply the given `changeset`.
320- pub fn apply_changeset ( & mut self , changeset : & ChangeSet < D > ) -> Result < ( ) , MissingGenesisError > {
340+ pub fn apply_changeset ( & mut self , changeset : & ChangeSet < D > ) -> Result < ( ) , CannotConnectError > {
321341 let old_tip = self . tip . clone ( ) ;
322- let new_tip = apply_changeset_to_checkpoint ( old_tip, changeset) ?;
342+ let new_tip =
343+ apply_changeset_to_checkpoint ( old_tip, changeset) . map_err ( |e| CannotConnectError {
344+ try_include_height : e. cp . as_ref ( ) . map_or ( 0 , CheckPoint :: height) ,
345+ } ) ?;
323346 self . tip = new_tip;
324347 debug_assert ! ( self . _check_changeset_is_applied( changeset) ) ;
325348 Ok ( ( ) )
@@ -702,9 +725,9 @@ where
702725 }
703726
704727 // Apply changeset to tip.
705- let new_tip = apply_changeset_to_checkpoint ( self . tip ( ) , & changeset) . map_err ( |_ | {
728+ let new_tip = apply_changeset_to_checkpoint ( self . tip ( ) , & changeset) . map_err ( |e | {
706729 CannotConnectError {
707- try_include_height : 0 ,
730+ try_include_height : e . cp . as_ref ( ) . map_or ( 0 , CheckPoint :: height ) ,
708731 }
709732 } ) ?;
710733
0 commit comments