@@ -213,6 +213,46 @@ impl CheckPoint {
213213        base. extend ( core:: iter:: once ( block_id) . chain ( tail. into_iter ( ) . rev ( ) ) ) 
214214            . expect ( "tail is in order" ) 
215215    } 
216+ 
217+     /// Apply `changeset` to the checkpoint. 
218+      fn  apply_changeset ( mut  self ,  changeset :  & ChangeSet )  -> Result < CheckPoint ,  MissingGenesisError >  { 
219+         if  let  Some ( start_height)  = changeset. keys ( ) . next ( ) . cloned ( )  { 
220+             // changes after point of agreement 
221+             let  mut  extension = BTreeMap :: default ( ) ; 
222+             // point of agreement 
223+             let  mut  base:  Option < CheckPoint >  = None ; 
224+ 
225+             for  cp in  self . iter ( )  { 
226+                 if  cp. height ( )  >= start_height { 
227+                     extension. insert ( cp. height ( ) ,  cp. hash ( ) ) ; 
228+                 }  else  { 
229+                     base = Some ( cp) ; 
230+                     break ; 
231+                 } 
232+             } 
233+ 
234+             for  ( & height,  & hash)  in  changeset { 
235+                 match  hash { 
236+                     Some ( hash)  => { 
237+                         extension. insert ( height,  hash) ; 
238+                     } 
239+                     None  => { 
240+                         extension. remove ( & height) ; 
241+                     } 
242+                 } ; 
243+             } 
244+ 
245+             let  new_tip = match  base { 
246+                 Some ( base)  => base
247+                     . extend ( extension. into_iter ( ) . map ( BlockId :: from) ) 
248+                     . expect ( "extension is strictly greater than base" ) , 
249+                 None  => LocalChain :: from_blocks ( extension) ?. tip ( ) , 
250+             } ; 
251+             self  = new_tip; 
252+         } 
253+ 
254+         Ok ( self ) 
255+     } 
216256} 
217257
218258/// Iterates over checkpoints backwards. 
@@ -348,32 +388,22 @@ impl LocalChain {
348388
349389    /// Applies the given `update` to the chain. 
350390     /// 
351-      /// The method returns [`ChangeSet`] on success. This represents the applied  changes to `self`. 
391+      /// The method returns [`ChangeSet`] on success. This represents the changes applied  to `self`. 
352392     /// 
353393     /// There must be no ambiguity about which of the existing chain's blocks are still valid and 
354394     /// which are now invalid. That is, the new chain must implicitly connect to a definite block in 
355395     /// the existing chain and invalidate the block after it (if it exists) by including a block at 
356396     /// the same height but with a different hash to explicitly exclude it as a connection point. 
357397     /// 
358-      /// Additionally, a chain with a single block can have it's block invalidated by an update 
359-      /// chain with a block at the same height but different hash. 
360-      /// 
361398     /// # Errors 
362399     /// 
363400     /// An error will occur if the update does not correctly connect with `self`. 
364401     /// 
365402     /// [module-level documentation]: crate::local_chain 
366403     pub  fn  apply_update ( & mut  self ,  update :  CheckPoint )  -> Result < ChangeSet ,  CannotConnectError >  { 
367-         let  ( changeset,  can_replace)  = merge_chains ( self . tip . clone ( ) ,  update. clone ( ) ) ?; 
368-         if  can_replace { 
369-             self . tip  = update; 
370-         }  else  { 
371-             // `._check_changeset_is_applied` is called in `.apply_changeset` 
372-             self . apply_changeset ( & changeset) 
373-                 . map_err ( |_| CannotConnectError  { 
374-                     try_include_height :  0 , 
375-                 } ) ?; 
376-         } 
404+         let  ( new_tip,  changeset)  = merge_chains ( self . tip . clone ( ) ,  update) ?; 
405+         self . tip  = new_tip; 
406+         self . _check_changeset_is_applied ( & changeset) ; 
377407        Ok ( changeset) 
378408    } 
379409
@@ -465,43 +495,10 @@ impl LocalChain {
465495
466496    /// Apply the given `changeset`. 
467497     pub  fn  apply_changeset ( & mut  self ,  changeset :  & ChangeSet )  -> Result < ( ) ,  MissingGenesisError >  { 
468-         if  let  Some ( start_height)  = changeset. keys ( ) . next ( ) . cloned ( )  { 
469-             // changes after point of agreement 
470-             let  mut  extension = BTreeMap :: default ( ) ; 
471-             // point of agreement 
472-             let  mut  base:  Option < CheckPoint >  = None ; 
473- 
474-             for  cp in  self . iter_checkpoints ( )  { 
475-                 if  cp. height ( )  >= start_height { 
476-                     extension. insert ( cp. height ( ) ,  cp. hash ( ) ) ; 
477-                 }  else  { 
478-                     base = Some ( cp) ; 
479-                     break ; 
480-                 } 
481-             } 
482- 
483-             for  ( & height,  & hash)  in  changeset { 
484-                 match  hash { 
485-                     Some ( hash)  => { 
486-                         extension. insert ( height,  hash) ; 
487-                     } 
488-                     None  => { 
489-                         extension. remove ( & height) ; 
490-                     } 
491-                 } ; 
492-             } 
493- 
494-             let  new_tip = match  base { 
495-                 Some ( base)  => base
496-                     . extend ( extension. into_iter ( ) . map ( BlockId :: from) ) 
497-                     . expect ( "extension is strictly greater than base" ) , 
498-                 None  => LocalChain :: from_blocks ( extension) ?. tip ( ) , 
499-             } ; 
500-             self . tip  = new_tip; 
501- 
502-             debug_assert ! ( self . _check_changeset_is_applied( changeset) ) ; 
503-         } 
504- 
498+         let  old_tip = self . tip . clone ( ) ; 
499+         let  new_tip = old_tip. apply_changeset ( changeset) ?; 
500+         self . tip  = new_tip; 
501+         debug_assert ! ( self . _check_changeset_is_applied( changeset) ) ; 
505502        Ok ( ( ) ) 
506503    } 
507504
@@ -731,10 +728,10 @@ impl std::error::Error for ApplyHeaderError {}
731728fn  merge_chains ( 
732729    original_tip :  CheckPoint , 
733730    update_tip :  CheckPoint , 
734- )  -> Result < ( ChangeSet ,   bool ) ,  CannotConnectError >  { 
731+ )  -> Result < ( CheckPoint ,   ChangeSet ) ,  CannotConnectError >  { 
735732    let  mut  changeset = ChangeSet :: default ( ) ; 
736-     let  mut  orig = original_tip. into_iter ( ) ; 
737-     let  mut  update = update_tip. into_iter ( ) ; 
733+     let  mut  orig = original_tip. iter ( ) ; 
734+     let  mut  update = update_tip. iter ( ) ; 
738735    let  mut  curr_orig = None ; 
739736    let  mut  curr_update = None ; 
740737    let  mut  prev_orig:  Option < CheckPoint >  = None ; 
@@ -743,10 +740,11 @@ fn merge_chains(
743740    let  mut  prev_orig_was_invalidated = false ; 
744741    let  mut  potentially_invalidated_heights = vec ! [ ] ; 
745742
746-     // Flag to set if heights are removed from original chain. If no heights are removed, and we 
747-     // have a matching node pointer between the two chains, we can conclude that the update tip can 
748-     // just replace the original tip. 
749-     let  mut  has_removed_heights = false ; 
743+     // If we can, we want to return the update tip as the new tip because this allows checkpoints 
744+     // in multiple locations to keep the same `Arc` pointers when they are being updated from each 
745+     // other using this function. We can do this as long as long as the update contains every 
746+     // block's height of the original chain. 
747+     let  mut  is_update_height_superset_of_original = true ; 
750748
751749    // To find the difference between the new chain and the original we iterate over both of them 
752750    // from the tip backwards in tandem. We always dealing with the highest one from either chain 
@@ -773,7 +771,7 @@ fn merge_chains(
773771                prev_orig_was_invalidated = false ; 
774772                prev_orig = curr_orig. take ( ) ; 
775773
776-                 has_removed_heights  = true ; 
774+                 is_update_height_superset_of_original  = false ; 
777775
778776                // OPTIMIZATION: we have run out of update blocks so we don't need to continue 
779777                // iterating because there's no possibility of adding anything to changeset. 
@@ -800,7 +798,17 @@ fn merge_chains(
800798                    // OPTIMIZATION 2 -- if we have the same underlying pointer at this point, we 
801799                    // can guarantee that no older blocks are introduced. 
802800                    if  Arc :: as_ptr ( & o. 0 )  == Arc :: as_ptr ( & u. 0 )  { 
803-                         return  Ok ( ( changeset,  !has_removed_heights) ) ; 
801+                         if  is_update_height_superset_of_original { 
802+                             return  Ok ( ( update_tip,  changeset) ) ; 
803+                         }  else  { 
804+                             let  new_tip =
805+                                 original_tip. apply_changeset ( & changeset) . map_err ( |_| { 
806+                                     CannotConnectError  { 
807+                                         try_include_height :  0 , 
808+                                     } 
809+                                 } ) ?; 
810+                             return  Ok ( ( new_tip,  changeset) ) ; 
811+                         } 
804812                    } 
805813                }  else  { 
806814                    // We have an invalidation height so we set the height to the updated hash and 
@@ -834,5 +842,10 @@ fn merge_chains(
834842        } 
835843    } 
836844
837-     Ok ( ( changeset,  false ) ) 
845+     let  new_tip = original_tip
846+         . apply_changeset ( & changeset) 
847+         . map_err ( |_| CannotConnectError  { 
848+             try_include_height :  0 , 
849+         } ) ?; 
850+     Ok ( ( new_tip,  changeset) ) 
838851} 
0 commit comments