Skip to content

Commit

Permalink
feat(chain): optimize merge_chains
Browse files Browse the repository at this point in the history
  • Loading branch information
evanlinjin committed Apr 17, 2024
1 parent 77d3595 commit daf588f
Showing 1 changed file with 25 additions and 10 deletions.
35 changes: 25 additions & 10 deletions crates/chain/src/local_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,17 @@ impl LocalChain {
///
/// [module-level documentation]: crate::local_chain
pub fn apply_update(&mut self, update: CheckPoint) -> Result<ChangeSet, CannotConnectError> {
let changeset = merge_chains(self.tip.clone(), update.clone())?;
// `._check_index_is_consistent_with_tip` and `._check_changeset_is_applied` is called in
// `.apply_changeset`
self.apply_changeset(&changeset)
.map_err(|_| CannotConnectError {
try_include_height: 0,
})?;
let (changeset, can_replace) = merge_chains(self.tip.clone(), update.clone())?;
if can_replace {
self.tip = update;
} else {
// `._check_index_is_consistent_with_tip` and `._check_changeset_is_applied` is called in
// `.apply_changeset`
self.apply_changeset(&changeset)
.map_err(|_| CannotConnectError {
try_include_height: 0,
})?;
}
Ok(changeset)
}

Expand Down Expand Up @@ -721,10 +725,14 @@ impl core::fmt::Display for ApplyHeaderError {
#[cfg(feature = "std")]
impl std::error::Error for ApplyHeaderError {}

/// Applies `update_tip` onto `original_tip`.
///
/// On success, a tuple is returned `(changeset, can_replace)`. If `can_replace` is true, then the
/// `update_tip` can replace the `original_tip`.
fn merge_chains(
original_tip: CheckPoint,
update_tip: CheckPoint,
) -> Result<ChangeSet, CannotConnectError> {
) -> Result<(ChangeSet, bool), CannotConnectError> {
let mut changeset = ChangeSet::default();
let mut orig = original_tip.into_iter();
let mut update = update_tip.into_iter();
Expand All @@ -736,6 +744,11 @@ fn merge_chains(
let mut prev_orig_was_invalidated = false;
let mut potentially_invalidated_heights = vec![];

// Flag to set if heights are removed from original chain. If no heights are removed, and we
// have a matching node pointer between the two chains, we can conclude that the update tip can
// just replace the original tip.
let mut has_removed_heights = false;

// To find the difference between the new chain and the original we iterate over both of them
// from the tip backwards in tandem. We always dealing with the highest one from either chain
// first and move to the next highest. The crucial logic is applied when they have blocks at the
Expand All @@ -761,6 +774,8 @@ fn merge_chains(
prev_orig_was_invalidated = false;
prev_orig = curr_orig.take();

has_removed_heights = true;

// OPTIMIZATION: we have run out of update blocks so we don't need to continue
// iterating because there's no possibility of adding anything to changeset.
if u.is_none() {
Expand All @@ -786,7 +801,7 @@ fn merge_chains(
// OPTIMIZATION 2 -- if we have the same underlying pointer at this point, we
// can guarantee that no older blocks are introduced.
if Arc::as_ptr(&o.0) == Arc::as_ptr(&u.0) {
return Ok(changeset);
return Ok((changeset, !has_removed_heights));
}
} else {
// We have an invalidation height so we set the height to the updated hash and
Expand Down Expand Up @@ -820,5 +835,5 @@ fn merge_chains(
}
}

Ok(changeset)
Ok((changeset, false))
}

0 comments on commit daf588f

Please sign in to comment.