From 5121ad2244abff429c906ee3bcdb2c85d747e33f Mon Sep 17 00:00:00 2001 From: yongkangc Date: Tue, 6 Jan 2026 23:26:01 +0000 Subject: [PATCH] fix: revert slow path optimization that caused regression The slow path optimization attempted to reuse ancestor cached overlays, but those overlays were built with a DIFFERENT anchor_hash. Since the slow path is triggered precisely because the anchor changed (after persist/reorg), reusing overlays from the old anchor produces incorrect results. Reverted to the original slow path that rebuilds from each ancestor's per-block state changes. The Arc::make_mut tracking metrics are kept. --- crates/chain-state/src/deferred_trie.rs | 34 +++++-------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/crates/chain-state/src/deferred_trie.rs b/crates/chain-state/src/deferred_trie.rs index 2bf9627632..1dfcb2f27e 100644 --- a/crates/chain-state/src/deferred_trie.rs +++ b/crates/chain-state/src/deferred_trie.rs @@ -256,36 +256,16 @@ impl DeferredTrieData { /// Merge all ancestors into a single overlay. /// /// This is the slow path used when the parent's overlay cannot be reused - /// (e.g., after persist when anchor changes). + /// (e.g., after persist when anchor changes). Iterates ancestors oldest -> newest + /// so newer state takes precedence. /// - /// # Optimization - /// Instead of iterating all ancestors from scratch, we find the most recent - /// ancestor that has a cached `anchored_trie_input` and use that as the base. - /// This reduces O(N) work to O(N - M) work where M is the cached depth. + /// Note: We intentionally do NOT reuse ancestor cached overlays here because + /// those overlays were built with a different anchor_hash. The slow path is + /// triggered precisely because the anchor changed, so we must rebuild from + /// each ancestor's per-block state changes. fn merge_ancestors_into_overlay(ancestors: &[Self]) -> TrieInputSorted { - // Find the most recent ancestor (searching from newest to oldest) that has - // a cached trie_input overlay. We can use that as our base and only merge - // the remaining ancestors. - let mut base_idx = 0; let mut overlay = TrieInputSorted::default(); - - for (idx, ancestor) in ancestors.iter().enumerate().rev() { - let ancestor_data = ancestor.wait_cloned(); - if let Some(anchored) = &ancestor_data.anchored_trie_input { - // Found a cached overlay! Use it as our base. - // We only need to merge ancestors after this one. - overlay = TrieInputSorted::new( - Arc::clone(&anchored.trie_input.nodes), - Arc::clone(&anchored.trie_input.state), - Default::default(), - ); - base_idx = idx + 1; // Start merging from the next ancestor - break; - } - } - - // Merge only the ancestors after the cached base (if any) - for ancestor in &ancestors[base_idx..] { + for ancestor in ancestors { let ancestor_data = ancestor.wait_cloned(); { let will_clone = Arc::strong_count(&overlay.state) > 1;