@@ -364,13 +364,17 @@ impl LocalChain {
364364 ///
365365 /// [module-level documentation]: crate::local_chain
366366 pub fn apply_update ( & mut self , update : CheckPoint ) -> Result < ChangeSet , CannotConnectError > {
367- let changeset = merge_chains ( self . tip . clone ( ) , update. clone ( ) ) ?;
368- // `._check_index_is_consistent_with_tip` and `._check_changeset_is_applied` is called in
369- // `.apply_changeset`
370- self . apply_changeset ( & changeset)
371- . map_err ( |_| CannotConnectError {
372- try_include_height : 0 ,
373- } ) ?;
367+ let ( changeset, can_replace) = merge_chains ( self . tip . clone ( ) , update. clone ( ) ) ?;
368+ if can_replace {
369+ self . tip = update;
370+ } else {
371+ // `._check_index_is_consistent_with_tip` and `._check_changeset_is_applied` is called in
372+ // `.apply_changeset`
373+ self . apply_changeset ( & changeset)
374+ . map_err ( |_| CannotConnectError {
375+ try_include_height : 0 ,
376+ } ) ?;
377+ }
374378 Ok ( changeset)
375379 }
376380
@@ -721,10 +725,14 @@ impl core::fmt::Display for ApplyHeaderError {
721725#[ cfg( feature = "std" ) ]
722726impl std:: error:: Error for ApplyHeaderError { }
723727
728+ /// Applies `update_tip` onto `original_tip`.
729+ ///
730+ /// On success, a tuple is returned `(changeset, can_replace)`. If `can_replace` is true, then the
731+ /// `update_tip` can replace the `original_tip`.
724732fn merge_chains (
725733 original_tip : CheckPoint ,
726734 update_tip : CheckPoint ,
727- ) -> Result < ChangeSet , CannotConnectError > {
735+ ) -> Result < ( ChangeSet , bool ) , CannotConnectError > {
728736 let mut changeset = ChangeSet :: default ( ) ;
729737 let mut orig = original_tip. into_iter ( ) ;
730738 let mut update = update_tip. into_iter ( ) ;
@@ -736,6 +744,11 @@ fn merge_chains(
736744 let mut prev_orig_was_invalidated = false ;
737745 let mut potentially_invalidated_heights = vec ! [ ] ;
738746
747+ // Flag to set if heights are removed from original chain. If no heights are removed, and we
748+ // have a matching node pointer between the two chains, we can conclude that the update tip can
749+ // just replace the original tip.
750+ let mut has_removed_heights = false ;
751+
739752 // To find the difference between the new chain and the original we iterate over both of them
740753 // from the tip backwards in tandem. We always dealing with the highest one from either chain
741754 // first and move to the next highest. The crucial logic is applied when they have blocks at the
@@ -761,6 +774,8 @@ fn merge_chains(
761774 prev_orig_was_invalidated = false ;
762775 prev_orig = curr_orig. take ( ) ;
763776
777+ has_removed_heights = true ;
778+
764779 // OPTIMIZATION: we have run out of update blocks so we don't need to continue
765780 // iterating because there's no possibility of adding anything to changeset.
766781 if u. is_none ( ) {
@@ -786,7 +801,7 @@ fn merge_chains(
786801 // OPTIMIZATION 2 -- if we have the same underlying pointer at this point, we
787802 // can guarantee that no older blocks are introduced.
788803 if Arc :: as_ptr ( & o. 0 ) == Arc :: as_ptr ( & u. 0 ) {
789- return Ok ( changeset) ;
804+ return Ok ( ( changeset, !has_removed_heights ) ) ;
790805 }
791806 } else {
792807 // We have an invalidation height so we set the height to the updated hash and
@@ -820,5 +835,5 @@ fn merge_chains(
820835 }
821836 }
822837
823- Ok ( changeset)
838+ Ok ( ( changeset, false ) )
824839}
0 commit comments