fix(state): keep evicted forks recoverable - #1104
Conversation
This reverts commit 601d2c0.
And one more auto-invalidated finding. Analyzed eight files, diff |
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
We likely want to fix that v12 before merging. would be rare I think, but still. evicting a fork can delete UTXOs that an in-flight sibling block still needs.
Sequence:
Before this PR, evicted blocks stayed in This unit test fails on 5783931: /// Removing one sent block must keep outpoints that another sent block still exposes.
#[test]
fn sent_hashes_remove_keeps_outpoints_shared_with_sent_sibling() -> Result<()> {
let _init_guard = zakura_test::init();
let block: Arc<Block> =
zakura_test::vectors::BLOCK_MAINNET_419201_BYTES.zcash_deserialize_into()?;
let siblings = block.make_fake_siblings(2);
let evicted = siblings[0].clone().prepare();
let in_flight = siblings[1].clone().prepare();
assert_ne!(evicted.hash, in_flight.hash);
let mut sent = SentHashes::default();
sent.add(&evicted);
sent.add(&in_flight);
sent.remove(&evicted.hash);
assert!(sent.contains(&in_flight.hash));
for outpoint in in_flight.new_outputs.keys() {
assert!(
sent.utxo(outpoint).is_some(),
"removing a sibling must not drop outpoint {outpoint:?} of a sent block"
);
}
Ok(())
}we might be able to get by with a hackier solution, but this suggestion seems reasonable, although not all of it needs to be handled here |
evan-forbes
left a comment
There was a problem hiding this comment.
pre-approving pending either fixes here or flups
|
@v12-auditor review |
And one more auto-invalidated finding. Analyzed nine files, diff |
p0mvn
left a comment
There was a problem hiding this comment.
LGTM.
Some outstanding v12s might be good to address still but non-blocking
Motivation
At the fork limit, an accepted block can be discarded before a child extends it. Stale duplicate and header records can then prevent an evicted block from being downloaded again. Removing that block can also delete cached transaction outputs another queued block still needs, leaving its child waiting for an output that should be available.
Solution
Keep the newly accepted fork within the existing limit and clear evicted blocks from duplicate tracking and header verification records. Apply the same cleanup during reconsideration. Count the distinct blocks sharing each cached output, so eviction, rejection, and pruning preserve it until its last owner leaves. Batch eviction cleanup scans the tracking buffers once. Eviction preserves the selected full-state chain and clears obsolete ancestor failures for discarded blocks. Commit-time waiter notifications and the broader lifecycle refactor remain separate follow-up work. This PR keeps the current hash-based equal-work ordering. #1084 builds on it for first-received mining.
Testing
Regression tests cover eviction and redelivery, restart with missing backed-up bodies, reconsideration, shared outputs, and failed parents that later commit and get evicted. Both new regressions failed before their fixes. All 687 state and 289 header-chain library tests pass on Rust 1.97.1, with six ignored. All-target Clippy, formatting, and changelog validation pass. Used Codex for implementation and tests.
Follow-up Work
SentHasheshalf. TheQueuedBlockshalf and wakingAwaitUtxowaiters at commit time stay there.main, that child committed.Verifiedmarkers for bodies lost from the backup, so operator invalidate or reconsider fails after that restart. Pre-existing.KnownBlockrace between publishing andEvicted, which is new here but still better thanmain, and forks dropped by finalization, which is pre-existing.