Summary
When a host saves and restores databaseContent (via chainHead_unstable_finalizedDatabase), the parachain runtime should be reusable without re-downloading the ~2 MiB :code from a peer. Currently, every parachain restart re-downloads the runtime even when the saved database contains valid runtime bytes.
Design
-
Persist the finalized parachain runtime snapshot in the database: :code bytes, merkle value, closest ancestor, bound to (blockHash, stateRoot).
-
On restore, verify the saved runtime via a lightweight merkle proof (~500 bytes) instead of re-downloading :code (~2 MiB). Request a sibling key in the trie that shares the same ancestor as :code — the proof includes the ancestor's child merkle values but not the leaf data. Compare the proof's merkle value to the stored hint.
-
Fall back to full peer download if verification fails (runtime upgraded, data corrupted, block pruned).
-
Pruning-window fallback: if the restored block is too old for peers to serve proofs (3 consecutive failures), discard the restored state and fetch a fresh finalized head from the relay chain.
Expected improvement
|
Cold start (no DB) |
Warm start (with DB) |
| Before |
6-21s (#3203 fixes this) |
6-21s (no benefit from saved DB) |
| After |
6-21s (unchanged) |
~1-3s (skip runtime download) |
Measured 22x speedup on Kusama, 20x on Paseo in prior benchmarking.
Dependencies
Prior work
The implementation existed as part of the original #3185 (17 commits), now split into A (#3185), B (#3203), and this issue (C). Implementation branch: restored-warmstart-performance. Needs rebasing onto current main after B lands (bitswap/statement store removals changed the API surface).
Review feedback to address
- @skunert: don't clone the multi-MB wasm blob in
DatabaseContent — use a flag or reference instead.
- @skunert: verify via merkle ancestor, not full
:code re-download — already implemented in branch.
Benchmarks
Available in smolbench:
bench/parachain-phase-breakdown.mjs — per-phase timing
bench/parachain-matrix.mjs — full network matrix
Summary
When a host saves and restores
databaseContent(viachainHead_unstable_finalizedDatabase), the parachain runtime should be reusable without re-downloading the ~2 MiB:codefrom a peer. Currently, every parachain restart re-downloads the runtime even when the saved database contains valid runtime bytes.Design
Persist the finalized parachain runtime snapshot in the database:
:codebytes, merkle value, closest ancestor, bound to(blockHash, stateRoot).On restore, verify the saved runtime via a lightweight merkle proof (~500 bytes) instead of re-downloading
:code(~2 MiB). Request a sibling key in the trie that shares the same ancestor as:code— the proof includes the ancestor's child merkle values but not the leaf data. Compare the proof's merkle value to the stored hint.Fall back to full peer download if verification fails (runtime upgraded, data corrupted, block pruned).
Pruning-window fallback: if the restored block is too old for peers to serve proofs (3 consecutive failures), discard the restored state and fetch a fresh finalized head from the relay chain.
Expected improvement
Measured 22x speedup on Kusama, 20x on Paseo in prior benchmarking.
Dependencies
bootstrap_parachain_consensusto return the runtime. This PR builds on that.Prior work
The implementation existed as part of the original #3185 (17 commits), now split into A (#3185), B (#3203), and this issue (C). Implementation branch:
restored-warmstart-performance. Needs rebasing onto current main after B lands (bitswap/statement store removals changed the API surface).Review feedback to address
DatabaseContent— use a flag or reference instead.:codere-download — already implemented in branch.Benchmarks
Available in smolbench:
bench/parachain-phase-breakdown.mjs— per-phase timingbench/parachain-matrix.mjs— full network matrix