Skip to content

Commit daf588f

Browse files
committed
feat(chain): optimize merge_chains
1 parent 77d3595 commit daf588f

File tree

1 file changed

+25
-10
lines changed

1 file changed

+25
-10
lines changed

crates/chain/src/local_chain.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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")]
722726
impl 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`.
724732
fn 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

Comments
 (0)