fix(anvil): continue the saved timeline after loading state - #15760
fix(anvil): continue the saved timeline after loading state#157600xMars42 wants to merge 2 commits into
Conversation
Backend::load_state restores the block env, base fee and blob params from the saved head but never re-anchors the TimeManager, so the first block mined after loading a state file took its timestamp from the node's startup anchor (genesis or fork block) instead of the saved chain head, jumping back in time. Reset block time to the loaded head's timestamp where the fees are already restored from that header, like the rollback path does, skipping the case where the canonical head is a newer fork block.
mattsse
left a comment
There was a problem hiding this comment.
The equal-height fork case can still replace the canonical fork time anchor with the loaded state's timestamp. Please use canonical head identity for this guard and add coverage for a loaded state and fork head at the same height.
| // Re-anchor block time to the loaded head so subsequent blocks continue the saved | ||
| // timeline instead of the node's startup anchor (genesis or fork block). Skip when | ||
| // the canonical head is not the loaded one (state file older than the fork block). | ||
| if header.number == self.blockchain.storage.read().best_number { |
There was a problem hiding this comment.
When the loaded state head and fork head have the same block number, the selection above keeps the fork head (best_number > number is false), but this height-only check still treats the loaded header as canonical and resets TimeManager from its timestamp. I reproduced this with a future-dated local block at height N loaded onto a fork also at N; the next block followed the stale state timeline. Comparing the actual canonical hash fixes it:
| if header.number == self.blockchain.storage.read().best_number { | |
| if header.hash_slow() == self.blockchain.storage.read().best_hash { |
Please also add an equal-height fork regression test.
Compare the loaded head's hash with the canonical best hash instead of comparing block numbers, so an equal-height fork head keeps its time anchor. Covered by a test that forks a node at the same height as the loaded state's head.
0xMars42
left a comment
There was a problem hiding this comment.
Good catch, the equal-height case did fire the reset: reproduced with a test forking a node at the same height as the loaded state's head, which failed on the previous guard and passes now. The guard compares the loaded head's hash with the canonical best hash instead of the numbers.
mattsse
left a comment
There was a problem hiding this comment.
The follow-up now guards the time re-anchor by canonical head identity and adds the requested equal-height fork regression. The focused regression test passes locally.
Motivation
Fixes #10331.
Backend::load_staterestores the block env, base fee and blob params from the saved head, but never re-anchors theTimeManager. The first block mined after loading a state file therefore takes its timestamp from the node's startup anchor (genesis or fork block) instead of the saved chain head: the loaded chain sits on its saved timeline while the next block jumps back to wall-clock time, which can produce a non-monotonic chain.Solution
Reset block time to the loaded head's timestamp right where the fees are already restored from that header, mirroring what the rollback path does after resetting the block env. The reset is skipped when the canonical head is not the loaded one (state file older than the fork block, see #9215), where the fork anchor stays correct.
This also applies to the
anvil_loadStateRPC, which goes through the same path: loading a state at runtime now re-anchors block time to the loaded head as well, consistent with--load-stateat startup.The regression test moves the chain one year ahead of wall-clock time, saves the state, reloads it on a fresh node and asserts the next mined block continues the saved timeline. It fails on master with
block after load_state went back in time: 1784120095 < 1815656095and passes with the fix.PR Checklist