From 9881e75a6b5ab37b29a47c1804cff17e82eb8654 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 13:00:23 -0400 Subject: [PATCH 1/5] chore(release): set version to 0.9.1 (#2934) Co-authored-by: github-actions[bot] --- Cargo.lock | 254 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 128 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03aa9c64dd..8a8e726093 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1888,7 +1888,7 @@ dependencies = [ [[package]] name = "audit-archiver" -version = "0.9.0" +version = "0.9.1" dependencies = [ "anyhow", "audit-archiver-lib", @@ -1907,7 +1907,7 @@ dependencies = [ [[package]] name = "audit-archiver-lib" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -2713,7 +2713,7 @@ dependencies = [ [[package]] name = "base" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-cli-utils", "base-common-chains", @@ -2726,7 +2726,7 @@ dependencies = [ [[package]] name = "base-access-lists" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-contract", @@ -2744,7 +2744,7 @@ dependencies = [ [[package]] name = "base-action-harness" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -2803,7 +2803,7 @@ dependencies = [ [[package]] name = "base-balance-monitor" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-network", "alloy-node-bindings", @@ -2816,7 +2816,7 @@ dependencies = [ [[package]] name = "base-batcher-admin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-batcher-core", "derive_more 2.1.1", @@ -2827,7 +2827,7 @@ dependencies = [ [[package]] name = "base-batcher-bin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-signer-local", @@ -2846,7 +2846,7 @@ dependencies = [ [[package]] name = "base-batcher-core" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -2871,7 +2871,7 @@ dependencies = [ [[package]] name = "base-batcher-encoder" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -2891,7 +2891,7 @@ dependencies = [ [[package]] name = "base-batcher-service" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -2925,7 +2925,7 @@ dependencies = [ [[package]] name = "base-batcher-source" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -2942,7 +2942,7 @@ dependencies = [ [[package]] name = "base-blobs" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -2953,7 +2953,7 @@ dependencies = [ [[package]] name = "base-builder-bin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "base-builder-core", @@ -2973,7 +2973,7 @@ dependencies = [ [[package]] name = "base-builder-core" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-contract", @@ -3105,7 +3105,7 @@ dependencies = [ [[package]] name = "base-builder-metering" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "base-builder-core", @@ -3118,7 +3118,7 @@ dependencies = [ [[package]] name = "base-builder-publish" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-metrics", "base-ring-buffer", @@ -3138,7 +3138,7 @@ dependencies = [ [[package]] name = "base-bundle-extension" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-execution-txpool", "base-node-runner", @@ -3151,7 +3151,7 @@ dependencies = [ [[package]] name = "base-bundles" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -3168,7 +3168,7 @@ dependencies = [ [[package]] name = "base-challenger" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -3212,7 +3212,7 @@ dependencies = [ [[package]] name = "base-challenger-bin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-challenger", "base-cli-utils", @@ -3222,7 +3222,7 @@ dependencies = [ [[package]] name = "base-cli-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "backtrace", "base-metrics", @@ -3245,7 +3245,7 @@ dependencies = [ [[package]] name = "base-common-chains" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-eips", @@ -3261,7 +3261,7 @@ dependencies = [ [[package]] name = "base-common-consensus" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3293,7 +3293,7 @@ dependencies = [ [[package]] name = "base-common-evm" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3317,7 +3317,7 @@ dependencies = [ [[package]] name = "base-common-flashblocks" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -3333,7 +3333,7 @@ dependencies = [ [[package]] name = "base-common-flz" -version = "0.9.0" +version = "0.9.1" dependencies = [ "hex-literal 1.1.0", "rstest", @@ -3341,7 +3341,7 @@ dependencies = [ [[package]] name = "base-common-genesis" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -3362,7 +3362,7 @@ dependencies = [ [[package]] name = "base-common-network" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-network", @@ -3381,7 +3381,7 @@ dependencies = [ [[package]] name = "base-common-precompiles" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-common-chains", "revm", @@ -3389,7 +3389,7 @@ dependencies = [ [[package]] name = "base-common-rpc-types" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3414,7 +3414,7 @@ dependencies = [ [[package]] name = "base-common-rpc-types-engine" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3437,7 +3437,7 @@ dependencies = [ [[package]] name = "base-common-signer" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3457,7 +3457,7 @@ dependencies = [ [[package]] name = "base-comp" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3482,7 +3482,7 @@ dependencies = [ [[package]] name = "base-consensus" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-cli-utils", "base-consensus-cli", @@ -3491,7 +3491,7 @@ dependencies = [ [[package]] name = "base-consensus-cli" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-genesis", @@ -3533,7 +3533,7 @@ dependencies = [ [[package]] name = "base-consensus-derive" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3562,7 +3562,7 @@ dependencies = [ [[package]] name = "base-consensus-disc" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-rlp", "backon", @@ -3585,7 +3585,7 @@ dependencies = [ [[package]] name = "base-consensus-engine" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3628,7 +3628,7 @@ dependencies = [ [[package]] name = "base-consensus-gossip" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -3667,7 +3667,7 @@ dependencies = [ [[package]] name = "base-consensus-node" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -3733,7 +3733,7 @@ dependencies = [ [[package]] name = "base-consensus-peers" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -3759,7 +3759,7 @@ dependencies = [ [[package]] name = "base-consensus-providers" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3796,7 +3796,7 @@ dependencies = [ [[package]] name = "base-consensus-rpc" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -3822,7 +3822,7 @@ dependencies = [ [[package]] name = "base-consensus-safedb" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -3838,7 +3838,7 @@ dependencies = [ [[package]] name = "base-consensus-sources" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-rpc-client", @@ -3860,7 +3860,7 @@ dependencies = [ [[package]] name = "base-consensus-upgrades" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -3872,7 +3872,7 @@ dependencies = [ [[package]] name = "base-engine-tree" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -3923,7 +3923,7 @@ dependencies = [ [[package]] name = "base-execution-chainspec" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -3945,7 +3945,7 @@ dependencies = [ [[package]] name = "base-execution-cli" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "backon", @@ -4000,7 +4000,7 @@ dependencies = [ [[package]] name = "base-execution-consensus" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4030,7 +4030,7 @@ dependencies = [ [[package]] name = "base-execution-evm" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4057,7 +4057,7 @@ dependencies = [ [[package]] name = "base-execution-exex" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4100,7 +4100,7 @@ dependencies = [ [[package]] name = "base-execution-payload-builder" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4139,7 +4139,7 @@ dependencies = [ [[package]] name = "base-execution-rpc" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4202,7 +4202,7 @@ dependencies = [ [[package]] name = "base-execution-trie" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4248,7 +4248,7 @@ dependencies = [ [[package]] name = "base-execution-txpool" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4356,7 +4356,7 @@ dependencies = [ [[package]] name = "base-flashblocks" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4411,7 +4411,7 @@ dependencies = [ [[package]] name = "base-flashblocks-node" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-contract", @@ -4468,7 +4468,7 @@ dependencies = [ [[package]] name = "base-health" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-transport-http", "async-trait", @@ -4490,7 +4490,7 @@ dependencies = [ [[package]] name = "base-jwt" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-provider", @@ -4506,7 +4506,7 @@ dependencies = [ [[package]] name = "base-load-tester-bin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-network", "alloy-primitives", @@ -4528,7 +4528,7 @@ dependencies = [ [[package]] name = "base-load-tests" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4566,7 +4566,7 @@ dependencies = [ [[package]] name = "base-metering" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4621,7 +4621,7 @@ dependencies = [ [[package]] name = "base-metrics" -version = "0.9.0" +version = "0.9.1" dependencies = [ "ctor", "metrics", @@ -4631,7 +4631,7 @@ dependencies = [ [[package]] name = "base-node-core" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4701,7 +4701,7 @@ dependencies = [ [[package]] name = "base-node-runner" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-genesis", @@ -4752,7 +4752,7 @@ dependencies = [ [[package]] name = "base-proof" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4790,7 +4790,7 @@ dependencies = [ [[package]] name = "base-proof-client" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-evm", @@ -4813,7 +4813,7 @@ dependencies = [ [[package]] name = "base-proof-contracts" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-contract", "alloy-primitives", @@ -4830,7 +4830,7 @@ dependencies = [ [[package]] name = "base-proof-driver" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-evm", @@ -4850,7 +4850,7 @@ dependencies = [ [[package]] name = "base-proof-executor" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4884,7 +4884,7 @@ dependencies = [ [[package]] name = "base-proof-host" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4925,7 +4925,7 @@ dependencies = [ [[package]] name = "base-proof-mpt" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -4946,7 +4946,7 @@ dependencies = [ [[package]] name = "base-proof-preimage" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "async-channel", @@ -4960,7 +4960,7 @@ dependencies = [ [[package]] name = "base-proof-primitives" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-eips", @@ -4978,7 +4978,7 @@ dependencies = [ [[package]] name = "base-proof-rpc" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-network", @@ -5005,7 +5005,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-build-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "cargo_metadata 0.18.1", "sp1-build", @@ -5013,7 +5013,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-client-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5049,7 +5049,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-elfs" -version = "0.9.0" +version = "0.9.1" dependencies = [ "serde", "sha2 0.10.9", @@ -5058,7 +5058,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-ethereum-client-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-genesis", "anyhow", @@ -5075,7 +5075,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-ethereum-host-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -5094,7 +5094,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-host-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-contract", @@ -5148,7 +5148,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-proof-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "anyhow", "base-proof-succinct-elfs", @@ -5168,7 +5168,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-prove" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-contract", "alloy-eips", @@ -5204,7 +5204,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-scripts" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-network", @@ -5237,7 +5237,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-signer-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5258,7 +5258,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-validity" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -5297,7 +5297,7 @@ dependencies = [ [[package]] name = "base-proof-tee-nitro-attestation-prover" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-signer-local", @@ -5316,7 +5316,7 @@ dependencies = [ [[package]] name = "base-proof-tee-nitro-enclave" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-eips", @@ -5349,7 +5349,7 @@ dependencies = [ [[package]] name = "base-proof-tee-nitro-host" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-signer", @@ -5373,7 +5373,7 @@ dependencies = [ [[package]] name = "base-proof-tee-nitro-verifier" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -5390,7 +5390,7 @@ dependencies = [ [[package]] name = "base-proof-tee-registrar" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -5427,7 +5427,7 @@ dependencies = [ [[package]] name = "base-proof-tee-registrar-bin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-provider", @@ -5456,7 +5456,7 @@ dependencies = [ [[package]] name = "base-proofs-extension" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-execution-exex", "base-execution-rpc", @@ -5473,7 +5473,7 @@ dependencies = [ [[package]] name = "base-proposer" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -5511,7 +5511,7 @@ dependencies = [ [[package]] name = "base-proposer-bin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-cli-utils", "base-proposer", @@ -5522,7 +5522,7 @@ dependencies = [ [[package]] name = "base-protocol" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloc-no-stdlib", "alloy-consensus", @@ -5558,7 +5558,7 @@ dependencies = [ [[package]] name = "base-prover-nitro-enclave" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-proof-tee-nitro-enclave", "eyre", @@ -5567,7 +5567,7 @@ dependencies = [ [[package]] name = "base-prover-nitro-host" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "base-cli-utils", @@ -5584,7 +5584,7 @@ dependencies = [ [[package]] name = "base-prover-zk" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-cli-utils", "base-proof-succinct-host-utils", @@ -5612,14 +5612,14 @@ dependencies = [ [[package]] name = "base-reth-cli" -version = "0.9.0" +version = "0.9.1" dependencies = [ "reth-node-core", ] [[package]] name = "base-reth-node" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-cli-utils", "base-execution-cli", @@ -5637,11 +5637,11 @@ dependencies = [ [[package]] name = "base-ring-buffer" -version = "0.9.0" +version = "0.9.1" [[package]] name = "base-runtime" -version = "0.9.0" +version = "0.9.1" dependencies = [ "futures", "rand 0.9.4", @@ -5653,7 +5653,7 @@ dependencies = [ [[package]] name = "base-snark-e2e" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-zk-service", "tokio", @@ -5663,7 +5663,7 @@ dependencies = [ [[package]] name = "base-test-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-contract", @@ -5681,7 +5681,7 @@ dependencies = [ [[package]] name = "base-tx-forwarding" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-execution-txpool", "base-node-runner", @@ -5691,7 +5691,7 @@ dependencies = [ [[package]] name = "base-tx-manager" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5725,7 +5725,7 @@ dependencies = [ [[package]] name = "base-txpool-rpc" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "base-node-runner", @@ -5742,7 +5742,7 @@ dependencies = [ [[package]] name = "base-txpool-tracing" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5773,7 +5773,7 @@ dependencies = [ [[package]] name = "base-witness-diff" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-provider", @@ -5799,7 +5799,7 @@ checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] name = "base-zk-client" -version = "0.9.0" +version = "0.9.1" dependencies = [ "async-trait", "prost 0.14.3", @@ -5815,7 +5815,7 @@ dependencies = [ [[package]] name = "base-zk-db" -version = "0.9.0" +version = "0.9.1" dependencies = [ "anyhow", "chrono", @@ -5831,7 +5831,7 @@ dependencies = [ [[package]] name = "base-zk-outbox" -version = "0.9.0" +version = "0.9.1" dependencies = [ "anyhow", "async-trait", @@ -5844,7 +5844,7 @@ dependencies = [ [[package]] name = "base-zk-service" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-provider", @@ -5930,7 +5930,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basectl" -version = "0.9.0" +version = "0.9.1" dependencies = [ "anyhow", "basectl-cli", @@ -5941,7 +5941,7 @@ dependencies = [ [[package]] name = "basectl-cli" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-contract", @@ -5977,7 +5977,7 @@ dependencies = [ [[package]] name = "based" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -5993,7 +5993,7 @@ dependencies = [ [[package]] name = "based-bin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-cli-utils", "based", @@ -8090,7 +8090,7 @@ dependencies = [ [[package]] name = "devnet" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10773,7 +10773,7 @@ dependencies = [ [[package]] name = "ingress-rpc" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-provider", "anyhow", @@ -10793,7 +10793,7 @@ dependencies = [ [[package]] name = "ingress-rpc-lib" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -12388,7 +12388,7 @@ dependencies = [ [[package]] name = "mempool-rebroadcaster" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -12405,7 +12405,7 @@ dependencies = [ [[package]] name = "mempool-rebroadcaster-bin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-cli-utils", "clap", @@ -24278,7 +24278,7 @@ dependencies = [ [[package]] name = "websocket-proxy" -version = "0.9.0" +version = "0.9.1" dependencies = [ "axum 0.8.9", "backoff", @@ -24299,7 +24299,7 @@ dependencies = [ [[package]] name = "websocket-proxy-bin" -version = "0.9.0" +version = "0.9.1" dependencies = [ "axum 0.8.9", "base-cli-utils", diff --git a/Cargo.toml b/Cargo.toml index da6aa9d56f..c11bc270d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "0.9.0" +version = "0.9.1" edition = "2024" rust-version = "1.93" license = "MIT" From 575f2159c31d2300f56604e56b82553127540d11 Mon Sep 17 00:00:00 2001 From: Haardik Date: Wed, 27 May 2026 12:33:16 -0400 Subject: [PATCH 2/5] backport(v0.9.1): recover pruned forkchoice checkpoints (#2698) (#2936) * fix(consensus): recover pruned forkchoice checkpoints Persist safe and finalized forkchoice checkpoints in the consensus service so validator restarts can recover when reth has pruned historical block bodies. Use checkpoints only after the specific missing L1 info deposit error and validate them against the reth-labeled block header before sync start accepts them. Co-authored-by: Codex (cherry picked from commit 0bfc265fa290e0f64b920da7120364499afcd1ba) * chore(succinct): refresh ELF manifest to match main The reproducible SP1 ELF hashes pinned in manifest.toml drifted from what the build environment currently produces. Main already updated these hashes (commit a052beb37); this brings the v0.9.1 backport branch in line so CI can verify reproducibility. Unrelated to the checkpoint backport itself - none of the diff in this branch touches the SP1 program source or its dependency tree. * chore: update succinct manifest and program cargo lock * fix(execution): use LenientRpcModuleValidator to allow base RPC namespace The default RpcModuleValidator rejects unknown namespaces, including 'base'. When operators include 'base' in --http.api, the node crashlooped with 'Invalid RPC module base in http.api: Unknown RPC module: base'. Switch the default Rpc generic on Cli to LenientRpcModuleValidator so operator-configured custom namespaces (like 'base') are accepted. * fix(consensus): recover to earliest unpruned block on pruned forkchoice When base-consensus starts from a GBS snapshot with a stale safe/finalized head whose block body has been pruned, and no forkchoice checkpoint exists, instead of crashing with MissingL1InfoDeposit, binary search between the pruned block and the latest block to find the earliest unpruned block and use that as the recovery point. For safe head: falls back to the recovered finalized value. For finalized head: binary searches for the prune boundary. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --------- Co-authored-by: Andreas Bigger Co-authored-by: Codex Co-authored-by: Mihir Wadekar Co-authored-by: Sisyphus --- Cargo.lock | 2 + crates/consensus/cli/src/node.rs | 8 + crates/consensus/engine/src/lib.rs | 6 +- .../consensus/engine/src/sync/checkpoint.rs | 56 ++++ crates/consensus/engine/src/sync/error.rs | 5 + .../consensus/engine/src/sync/forkchoice.rs | 185 ++++++++++- crates/consensus/engine/src/sync/mod.rs | 41 ++- .../consensus/engine/src/task_queue/core.rs | 33 +- crates/consensus/service/Cargo.toml | 2 + .../service/src/actors/checkpoint/actor.rs | 60 ++++ .../service/src/actors/checkpoint/client.rs | 106 +++++++ .../service/src/actors/checkpoint/db.rs | 171 +++++++++++ .../service/src/actors/checkpoint/error.rs | 15 + .../service/src/actors/checkpoint/mod.rs | 13 + .../actors/engine/engine_request_processor.rs | 288 +++++++++++++++++- .../service/src/actors/engine/error.rs | 5 + crates/consensus/service/src/actors/mod.rs | 6 + crates/consensus/service/src/lib.rs | 25 +- .../consensus/service/src/service/builder.rs | 23 ++ crates/consensus/service/src/service/node.rs | 41 ++- crates/execution/cli/src/lib.rs | 4 +- crates/proof/succinct/elf/manifest.toml | 11 +- crates/proof/succinct/programs/Cargo.lock | 38 +-- 23 files changed, 1066 insertions(+), 78 deletions(-) create mode 100644 crates/consensus/engine/src/sync/checkpoint.rs create mode 100644 crates/consensus/service/src/actors/checkpoint/actor.rs create mode 100644 crates/consensus/service/src/actors/checkpoint/client.rs create mode 100644 crates/consensus/service/src/actors/checkpoint/db.rs create mode 100644 crates/consensus/service/src/actors/checkpoint/error.rs create mode 100644 crates/consensus/service/src/actors/checkpoint/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 8a8e726093..d73fa1d926 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3718,9 +3718,11 @@ dependencies = [ "metrics", "mockall", "rand 0.9.4", + "redb", "rstest", "serde", "strum", + "tempfile", "thiserror 2.0.18", "tokio", "tokio-stream", diff --git a/crates/consensus/cli/src/node.rs b/crates/consensus/cli/src/node.rs index 6daa812897..f070ef560a 100644 --- a/crates/consensus/cli/src/node.rs +++ b/crates/consensus/cli/src/node.rs @@ -135,6 +135,10 @@ pub struct ConsensusNodeConfigArgs { /// Path to the `SafeDB` directory. If not set, safe head tracking is disabled. #[arg(long = "safedb.path", env = "BASE_NODE_SAFEDB_PATH")] pub safedb_path: Option, + + /// Path to the checkpoint database. If not set, a default path under `~/.base` is used. + #[arg(long = "checkpoint.path", env = "BASE_NODE_CHECKPOINT_PATH")] + pub checkpoint_path: Option, } impl ConsensusNodeArgs { @@ -241,6 +245,9 @@ impl ConsensusNodeArgs { ) .with_sequencer_config(self.config.sequencer_flags.config()); + if let Some(path) = self.config.checkpoint_path.clone() { + builder = builder.with_checkpoint_path(path); + } if let Some(path) = self.config.safedb_path.clone() { builder = builder.with_safedb_path(path); } @@ -304,6 +311,7 @@ mod tests { p2p_flags: P2PArgs::default(), rpc_flags: RpcArgs::default(), sequencer_flags: SequencerArgs::default(), + checkpoint_path: None, safedb_path: None, } } diff --git a/crates/consensus/engine/src/lib.rs b/crates/consensus/engine/src/lib.rs index 5389bfc5a4..65f183bb82 100644 --- a/crates/consensus/engine/src/lib.rs +++ b/crates/consensus/engine/src/lib.rs @@ -44,7 +44,11 @@ mod metrics; pub use metrics::Metrics; mod sync; -pub use sync::{L2ForkchoiceState, SyncStartError, find_starting_forkchoice}; +pub use sync::{ + ForkchoiceCheckpointError, ForkchoiceCheckpointLabel, ForkchoiceCheckpointReader, + L2ForkchoiceState, NoopForkchoiceCheckpointReader, SyncStartError, find_starting_forkchoice, + find_starting_forkchoice_with_checkpoint_reader, +}; #[cfg(any(test, feature = "test-utils"))] /// Utilities that are useful when creating unit tests using structs within this library. diff --git a/crates/consensus/engine/src/sync/checkpoint.rs b/crates/consensus/engine/src/sync/checkpoint.rs new file mode 100644 index 0000000000..5c34477f3d --- /dev/null +++ b/crates/consensus/engine/src/sync/checkpoint.rs @@ -0,0 +1,56 @@ +//! Forkchoice checkpoint interfaces for sync start recovery. + +use async_trait::async_trait; +use base_protocol::L2BlockInfo; +use thiserror::Error; + +/// Forkchoice labels that may be recovered from a checkpoint. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ForkchoiceCheckpointLabel { + /// The safe L2 head. + Safe, + /// The finalized L2 head. + Finalized, +} + +impl ForkchoiceCheckpointLabel { + /// Returns the label as a static string. + pub const fn as_str(self) -> &'static str { + match self { + Self::Safe => "safe", + Self::Finalized => "finalized", + } + } +} + +/// Error returned when reading forkchoice checkpoints. +#[derive(Debug, Error)] +pub enum ForkchoiceCheckpointError { + /// The checkpoint reader is unavailable. + #[error("forkchoice checkpoint reader unavailable: {0}")] + Unavailable(String), +} + +/// Reads forkchoice checkpoints. +#[async_trait] +pub trait ForkchoiceCheckpointReader: Send + Sync + std::fmt::Debug { + /// Returns the checkpoint for the requested label, if present. + async fn checkpoint( + &self, + label: ForkchoiceCheckpointLabel, + ) -> Result, ForkchoiceCheckpointError>; +} + +/// Checkpoint reader that never returns checkpoints. +#[derive(Debug, Default)] +pub struct NoopForkchoiceCheckpointReader; + +#[async_trait] +impl ForkchoiceCheckpointReader for NoopForkchoiceCheckpointReader { + async fn checkpoint( + &self, + _label: ForkchoiceCheckpointLabel, + ) -> Result, ForkchoiceCheckpointError> { + Ok(None) + } +} diff --git a/crates/consensus/engine/src/sync/error.rs b/crates/consensus/engine/src/sync/error.rs index 8d7bc8a160..879a5020e0 100644 --- a/crates/consensus/engine/src/sync/error.rs +++ b/crates/consensus/engine/src/sync/error.rs @@ -6,6 +6,8 @@ use alloy_transport::{RpcError, TransportErrorKind}; use base_protocol::FromBlockError; use thiserror::Error; +use super::checkpoint::ForkchoiceCheckpointError; + /// An error that can occur during the sync start process. #[derive(Error, Debug)] pub enum SyncStartError { @@ -17,6 +19,9 @@ pub enum SyncStartError { /// [`L2BlockInfo`]: base_protocol::L2BlockInfo #[error(transparent)] FromBlock(#[from] FromBlockError), + /// An error occurred while reading a forkchoice checkpoint. + #[error(transparent)] + ForkchoiceCheckpoint(#[from] ForkchoiceCheckpointError), /// A block could not be found. #[error("Block not found: {0}")] BlockNotFound(BlockId), diff --git a/crates/consensus/engine/src/sync/forkchoice.rs b/crates/consensus/engine/src/sync/forkchoice.rs index 0996dd1ced..633725a33e 100644 --- a/crates/consensus/engine/src/sync/forkchoice.rs +++ b/crates/consensus/engine/src/sync/forkchoice.rs @@ -4,12 +4,15 @@ use std::fmt::Display; use alloy_eips::{BlockId, BlockNumberOrTag}; use alloy_provider::Network; +use alloy_rpc_types_eth::Block as RpcBlock; use alloy_transport::TransportResult; use base_common_genesis::RollupConfig; use base_common_network::Base; -use base_protocol::L2BlockInfo; +use base_common_rpc_types::Transaction; +use base_protocol::{BlockInfo, FromBlockError, L2BlockInfo}; +use tracing::warn; -use crate::{EngineClient, SyncStartError}; +use crate::{EngineClient, ForkchoiceCheckpointLabel, ForkchoiceCheckpointReader, SyncStartError}; /// An unsafe, safe, and finalized [`L2BlockInfo`] returned by the [`crate::find_starting_forkchoice`] /// function. @@ -48,6 +51,24 @@ impl L2ForkchoiceState { pub async fn current( cfg: &RollupConfig, engine_client: &EngineClient_, + ) -> Result { + Self::current_with_checkpoint_reader( + cfg, + engine_client, + &crate::NoopForkchoiceCheckpointReader, + ) + .await + } + + /// Fetches the current forkchoice state of the L2 execution layer, using checkpoints only when + /// reth reports a safe or finalized block whose body has been pruned. + pub async fn current_with_checkpoint_reader< + EngineClient_: EngineClient, + CheckpointReader: ForkchoiceCheckpointReader + ?Sized, + >( + cfg: &RollupConfig, + engine_client: &EngineClient_, + checkpoint_reader: &CheckpointReader, ) -> Result { let finalized = { let rpc_block = @@ -59,19 +80,54 @@ impl L2ForkchoiceState { .await? .ok_or(SyncStartError::BlockNotFound(cfg.genesis.l2.number.into()))?, Err(e) => return Err(e.into()), - } - .into_consensus(); + }; - L2BlockInfo::from_block_and_genesis( - &rpc_block.map_transactions(|tx| tx.inner.inner.into_inner()), - &cfg.genesis, - )? + let rpc_block_number = rpc_block.header.number; + match block_info_from_reth_or_checkpoint( + cfg, + ForkchoiceCheckpointLabel::Finalized, + rpc_block, + checkpoint_reader, + ) + .await + { + Ok(info) => info, + Err(SyncStartError::FromBlock(FromBlockError::MissingL1InfoDeposit(hash))) => { + warn!( + target: "sync_start", + block_hash = %hash, + block_number = rpc_block_number, + "finalized block body is pruned and no valid checkpoint exists, \ + recovering to earliest unpruned block" + ); + find_earliest_unpruned_block(cfg, engine_client, rpc_block_number).await? + } + Err(e) => return Err(e), + } }; let safe = match get_block_compat(engine_client, BlockNumberOrTag::Safe.into()).await { - Ok(Some(block)) => L2BlockInfo::from_block_and_genesis( - &block.into_consensus().map_transactions(|tx| tx.inner.inner.into_inner()), - &cfg.genesis, - )?, + Ok(Some(block)) => { + match block_info_from_reth_or_checkpoint( + cfg, + ForkchoiceCheckpointLabel::Safe, + block, + checkpoint_reader, + ) + .await + { + Ok(info) => info, + Err(SyncStartError::FromBlock(FromBlockError::MissingL1InfoDeposit(hash))) => { + warn!( + target: "sync_start", + block_hash = %hash, + "safe block body is pruned and no valid checkpoint exists, \ + falling back to finalized" + ); + finalized + } + Err(e) => return Err(e), + } + } Ok(None) => finalized, Err(e) => return Err(e.into()), }; @@ -89,6 +145,111 @@ impl L2ForkchoiceState { } } +async fn block_info_from_reth_or_checkpoint< + CheckpointReader: ForkchoiceCheckpointReader + ?Sized, +>( + cfg: &RollupConfig, + label: ForkchoiceCheckpointLabel, + rpc_block: RpcBlock, + checkpoint_reader: &CheckpointReader, +) -> Result { + let block = rpc_block.into_consensus().map_transactions(|tx| tx.inner.inner.into_inner()); + match L2BlockInfo::from_block_and_genesis(&block, &cfg.genesis) { + Ok(block_info) => Ok(block_info), + Err(err @ FromBlockError::MissingL1InfoDeposit(_)) => { + let header = BlockInfo::from(&block); + let Some(checkpoint) = checkpoint_reader.checkpoint(label).await? else { + return Err(err.into()); + }; + + if checkpoint.block_info != header { + warn!( + target: "sync_start", + label = label.as_str(), + reth_number = header.number, + reth_hash = %header.hash, + reth_parent_hash = %header.parent_hash, + reth_timestamp = header.timestamp, + checkpoint_number = checkpoint.block_info.number, + checkpoint_hash = %checkpoint.block_info.hash, + checkpoint_parent_hash = %checkpoint.block_info.parent_hash, + checkpoint_timestamp = checkpoint.block_info.timestamp, + "forkchoice checkpoint does not match reth labeled block header" + ); + return Err(err.into()); + } + + warn!( + target: "sync_start", + label = label.as_str(), + number = checkpoint.block_info.number, + hash = %checkpoint.block_info.hash, + "using forkchoice checkpoint because reth block body is pruned" + ); + Ok(checkpoint) + } + Err(err) => Err(err.into()), + } +} + +/// When the labeled safe or finalized block's body is pruned and no checkpoint is available, +/// finds the earliest L2 block whose body has not been pruned by performing a binary search +/// between the pruned block and the latest block. Used as a recovery fallback instead of +/// crashing or falling back to genesis (which would trigger a months-long re-derivation). +async fn find_earliest_unpruned_block( + cfg: &RollupConfig, + engine_client: &EngineClient_, + pruned_block_number: u64, +) -> Result { + let latest = get_block_compat(engine_client, BlockNumberOrTag::Latest.into()) + .await? + .ok_or(SyncStartError::BlockNotFound(BlockNumberOrTag::Latest.into()))?; + let latest_number = latest.header.number; + + // Binary search for the prune boundary between the known-pruned block and the latest block. + // Invariant: blocks at `lo` have pruned bodies, blocks at `hi` have available bodies. + let mut lo = pruned_block_number; + let mut hi = latest_number; + + warn!( + target: "sync_start", + lo, + hi, + "binary searching for earliest unpruned block" + ); + + while lo < hi { + let mid = lo + (hi - lo) / 2; + let block = engine_client + .get_l2_block(mid.into()) + .full() + .await? + .ok_or(SyncStartError::BlockNotFound(mid.into()))?; + let consensus_block = + block.into_consensus().map_transactions(|tx| tx.inner.inner.into_inner()); + + match L2BlockInfo::from_block_and_genesis(&consensus_block, &cfg.genesis) { + Ok(_) => hi = mid, + Err(FromBlockError::MissingL1InfoDeposit(_)) => lo = mid + 1, + Err(err) => return Err(err.into()), + } + } + + warn!( + target: "sync_start", + block_number = lo, + "found earliest unpruned block" + ); + + let block = engine_client + .get_l2_block(lo.into()) + .full() + .await? + .ok_or(SyncStartError::BlockNotFound(lo.into()))?; + let consensus_block = block.into_consensus().map_transactions(|tx| tx.inner.inner.into_inner()); + L2BlockInfo::from_block_and_genesis(&consensus_block, &cfg.genesis).map_err(Into::into) +} + /// Wrapper function around [`EngineClient::get_l2_block`] to handle compatibility issues with geth /// and erigon. When serving a block-by-number request, these clients will return non-standard /// errors for the safe and finalized heads when the chain has just started and nothing is marked as diff --git a/crates/consensus/engine/src/sync/mod.rs b/crates/consensus/engine/src/sync/mod.rs index 86e4f3b1f5..4b6c3ed59e 100644 --- a/crates/consensus/engine/src/sync/mod.rs +++ b/crates/consensus/engine/src/sync/mod.rs @@ -3,6 +3,12 @@ use base_common_genesis::RollupConfig; use base_protocol::L2BlockInfo; +mod checkpoint; +pub use checkpoint::{ + ForkchoiceCheckpointError, ForkchoiceCheckpointLabel, ForkchoiceCheckpointReader, + NoopForkchoiceCheckpointReader, +}; + mod forkchoice; pub use forkchoice::L2ForkchoiceState; @@ -28,7 +34,28 @@ pub async fn find_starting_forkchoice( cfg: &RollupConfig, engine_client: &EngineClient_, ) -> Result { - let mut current_fc = L2ForkchoiceState::current(cfg, engine_client).await?; + find_starting_forkchoice_with_checkpoint_reader( + cfg, + engine_client, + &NoopForkchoiceCheckpointReader, + ) + .await +} + +/// Searches for the latest [`L2ForkchoiceState`] that we can use to start the sync process with, +/// using the supplied checkpoint reader only when reth returns a labeled safe/finalized block whose +/// body is pruned. +pub async fn find_starting_forkchoice_with_checkpoint_reader< + EngineClient_: EngineClient, + CheckpointReader: ForkchoiceCheckpointReader + ?Sized, +>( + cfg: &RollupConfig, + engine_client: &EngineClient_, + checkpoint_reader: &CheckpointReader, +) -> Result { + let mut current_fc = + L2ForkchoiceState::current_with_checkpoint_reader(cfg, engine_client, checkpoint_reader) + .await?; info!( target: "sync_start", unsafe = %current_fc.un_safe.block_info.number, @@ -88,13 +115,15 @@ pub async fn find_starting_forkchoice( let is_behind_sequence_window = current_fc.un_safe.l1_origin.number.saturating_sub(cfg.seq_window_size) > safe_cursor.l1_origin.number; + let is_labeled_safe = safe_cursor.block_info.hash == current_fc.safe.block_info.hash; let is_finalized = safe_cursor.block_info.hash == current_fc.finalized.block_info.hash; let is_genesis = safe_cursor.block_info.hash == cfg.genesis.l2.hash; - if is_behind_sequence_window || is_finalized || is_genesis { + if is_behind_sequence_window || is_labeled_safe || is_finalized || is_genesis { info!( target: "sync_start", l2_safe = %safe_cursor.block_info.number, is_behind_sequence_window, + is_labeled_safe, is_finalized, is_genesis, "Found suitable L2 safe block" @@ -102,6 +131,14 @@ pub async fn find_starting_forkchoice( current_fc.safe = safe_cursor; break; } + if safe_cursor.block_info.parent_hash == current_fc.safe.block_info.hash { + safe_cursor = current_fc.safe; + continue; + } + if safe_cursor.block_info.parent_hash == current_fc.finalized.block_info.hash { + safe_cursor = current_fc.finalized; + continue; + } let block = engine_client .get_l2_block(safe_cursor.block_info.parent_hash.into()) .full() diff --git a/crates/consensus/engine/src/task_queue/core.rs b/crates/consensus/engine/src/task_queue/core.rs index 9c4156a4be..8fac36cf5c 100644 --- a/crates/consensus/engine/src/task_queue/core.rs +++ b/crates/consensus/engine/src/task_queue/core.rs @@ -10,8 +10,9 @@ use tokio::sync::watch::Sender; use super::EngineTaskExt; use crate::{ EngineClient, EngineState, EngineSyncStateUpdate, EngineTask, EngineTaskError, - EngineTaskErrorSeverity, Metrics, SyncStartError, SynchronizeTask, SynchronizeTaskError, - find_starting_forkchoice, task_queue::EngineTaskErrors, + EngineTaskErrorSeverity, ForkchoiceCheckpointReader, Metrics, NoopForkchoiceCheckpointReader, + SyncStartError, SynchronizeTask, SynchronizeTaskError, + find_starting_forkchoice_with_checkpoint_reader, task_queue::EngineTaskErrors, }; /// The [`Engine`] task queue. @@ -93,10 +94,29 @@ impl Engine { client: Arc, config: Arc, ) -> Result { + self.reset_with_checkpoint_reader(client, config, &NoopForkchoiceCheckpointReader).await + } + + /// Resets the engine by finding a plausible sync starting point via + /// [`find_starting_forkchoice_with_checkpoint_reader`]. + pub async fn reset_with_checkpoint_reader( + &mut self, + client: Arc, + config: Arc, + checkpoint_reader: &CheckpointReader, + ) -> Result + where + CheckpointReader: ForkchoiceCheckpointReader + ?Sized, + { // Clear any outstanding tasks to prepare for the reset. self.clear(); - let mut start = find_starting_forkchoice(&config, client.as_ref()).await?; + let mut start = find_starting_forkchoice_with_checkpoint_reader( + &config, + client.as_ref(), + checkpoint_reader, + ) + .await?; // Retry to synchronize the engine until we succeeds or a critical error occurs. while let Err(err) = SynchronizeTask::new( @@ -117,7 +137,12 @@ impl Engine { | EngineTaskErrorSeverity::Flush | EngineTaskErrorSeverity::Reset => { warn!(target: "engine", ?err, "Forkchoice update failed during reset. Trying again..."); - start = find_starting_forkchoice(&config, client.as_ref()).await?; + start = find_starting_forkchoice_with_checkpoint_reader( + &config, + client.as_ref(), + checkpoint_reader, + ) + .await?; } EngineTaskErrorSeverity::Critical => { return Err(EngineResetError::Forkchoice(err)); diff --git a/crates/consensus/service/Cargo.toml b/crates/consensus/service/Cargo.toml index a2bfd18366..386d64f98c 100644 --- a/crates/consensus/service/Cargo.toml +++ b/crates/consensus/service/Cargo.toml @@ -50,6 +50,7 @@ base-common-rpc-types-engine = { workspace = true, features = ["std"] } # general url.workspace = true http.workspace = true +redb.workspace = true serde.workspace = true tower = { workspace = true, features = ["limit", "load-shed"] } bytes.workspace = true @@ -80,6 +81,7 @@ rand.workspace = true rstest.workspace = true anyhow.workspace = true mockall.workspace = true +tempfile.workspace = true arbitrary.workspace = true base-common-rpc-types.workspace = true alloy-primitives = { workspace = true, features = ["k256"] } diff --git a/crates/consensus/service/src/actors/checkpoint/actor.rs b/crates/consensus/service/src/actors/checkpoint/actor.rs new file mode 100644 index 0000000000..ad944e2d14 --- /dev/null +++ b/crates/consensus/service/src/actors/checkpoint/actor.rs @@ -0,0 +1,60 @@ +//! Checkpoint actor. + +use async_trait::async_trait; +use tokio::sync::mpsc; +use tokio_util::sync::CancellationToken; + +use super::{CheckpointDB, CheckpointError, CheckpointRequest}; +use crate::NodeActor; + +/// Actor that owns durable checkpoint storage. +#[derive(Debug)] +pub struct CheckpointActor { + db: CheckpointDB, + request_rx: mpsc::Receiver, +} + +impl CheckpointActor { + /// Creates a new checkpoint actor. + pub const fn new(db: CheckpointDB, request_rx: mpsc::Receiver) -> Self { + Self { db, request_rx } + } +} + +#[async_trait] +impl NodeActor for CheckpointActor { + type Error = CheckpointError; + type StartData = CancellationToken; + + async fn start(mut self, cancellation: Self::StartData) -> Result<(), Self::Error> { + loop { + tokio::select! { + _ = cancellation.cancelled() => { + info!(target: "checkpoint", "checkpoint actor received shutdown signal"); + return Ok(()); + } + request = self.request_rx.recv() => { + let Some(request) = request else { + warn!(target: "checkpoint", "checkpoint request channel closed"); + return Ok(()); + }; + + match request { + CheckpointRequest::Read { label, response_tx } => { + let result = self.db.checkpoint(label).await; + if response_tx.send(result).is_err() { + debug!(target: "checkpoint", label = label.as_str(), "checkpoint read response receiver dropped"); + } + } + CheckpointRequest::Write { label, block, response_tx } => { + let result = self.db.update(label, block).await; + if response_tx.send(result).is_err() { + debug!(target: "checkpoint", label = label.as_str(), "checkpoint write response receiver dropped"); + } + } + } + } + } + } + } +} diff --git a/crates/consensus/service/src/actors/checkpoint/client.rs b/crates/consensus/service/src/actors/checkpoint/client.rs new file mode 100644 index 0000000000..1f78e4f571 --- /dev/null +++ b/crates/consensus/service/src/actors/checkpoint/client.rs @@ -0,0 +1,106 @@ +//! Checkpoint actor client. + +use async_trait::async_trait; +use base_consensus_engine::{ + ForkchoiceCheckpointError, ForkchoiceCheckpointLabel, ForkchoiceCheckpointReader, +}; +use base_protocol::L2BlockInfo; +use tokio::sync::{mpsc, oneshot}; + +use super::CheckpointError; + +/// Writes forkchoice checkpoints. +#[async_trait] +pub trait CheckpointWriter: Send + Sync + std::fmt::Debug { + /// Updates the checkpoint for the requested label. + async fn update_checkpoint( + &self, + label: ForkchoiceCheckpointLabel, + block: L2BlockInfo, + ) -> Result<(), CheckpointError>; +} + +/// Checkpoint writer that drops all updates. +#[derive(Debug, Default)] +pub struct NoopCheckpointWriter; + +#[async_trait] +impl CheckpointWriter for NoopCheckpointWriter { + async fn update_checkpoint( + &self, + _label: ForkchoiceCheckpointLabel, + _block: L2BlockInfo, + ) -> Result<(), CheckpointError> { + Ok(()) + } +} + +/// Client used to communicate with the checkpoint actor. +#[derive(Debug, Clone)] +pub struct CheckpointClient { + request_tx: mpsc::Sender, +} + +impl CheckpointClient { + /// Creates a new checkpoint client. + pub const fn new(request_tx: mpsc::Sender) -> Self { + Self { request_tx } + } + + async fn send(&self, request: CheckpointRequest) -> Result<(), CheckpointError> { + self.request_tx.send(request).await.map_err(|_| CheckpointError::ChannelClosed) + } +} + +/// Request sent to the checkpoint actor. +#[derive(Debug)] +pub enum CheckpointRequest { + /// Read the checkpoint for a label. + Read { + /// The label to read. + label: ForkchoiceCheckpointLabel, + /// Response channel. + response_tx: oneshot::Sender, CheckpointError>>, + }, + /// Write the checkpoint for a label. + Write { + /// The label to write. + label: ForkchoiceCheckpointLabel, + /// The checkpoint block. + block: L2BlockInfo, + /// Response channel. + response_tx: oneshot::Sender>, + }, +} + +#[async_trait] +impl ForkchoiceCheckpointReader for CheckpointClient { + async fn checkpoint( + &self, + label: ForkchoiceCheckpointLabel, + ) -> Result, ForkchoiceCheckpointError> { + let (response_tx, response_rx) = oneshot::channel(); + self.send(CheckpointRequest::Read { label, response_tx }) + .await + .map_err(|e| ForkchoiceCheckpointError::Unavailable(e.to_string()))?; + response_rx + .await + .map_err(|_| { + ForkchoiceCheckpointError::Unavailable(CheckpointError::ResponseDropped.to_string()) + })? + .map_err(|e| ForkchoiceCheckpointError::Unavailable(e.to_string())) + } +} + +#[async_trait] +impl CheckpointWriter for CheckpointClient { + async fn update_checkpoint( + &self, + label: ForkchoiceCheckpointLabel, + block: L2BlockInfo, + ) -> Result<(), CheckpointError> { + let (response_tx, response_rx) = oneshot::channel(); + self.send(CheckpointRequest::Write { label, block, response_tx }).await?; + response_rx.await.map_err(|_| CheckpointError::ResponseDropped)? + } +} diff --git a/crates/consensus/service/src/actors/checkpoint/db.rs b/crates/consensus/service/src/actors/checkpoint/db.rs new file mode 100644 index 0000000000..6b4a2c22e9 --- /dev/null +++ b/crates/consensus/service/src/actors/checkpoint/db.rs @@ -0,0 +1,171 @@ +//! Durable checkpoint database. + +use std::{path::Path, sync::Arc}; + +use alloy_primitives::B256; +use base_consensus_engine::ForkchoiceCheckpointLabel; +use base_protocol::{BlockInfo, L2BlockInfo}; +use redb::{Database, TableDefinition}; +use tokio::task; + +use super::CheckpointError; + +const CHECKPOINT_VALUE_LEN: usize = 128; +const CHECKPOINTS: TableDefinition<'_, u8, &[u8; CHECKPOINT_VALUE_LEN]> = + TableDefinition::new("checkpoints"); + +/// Redb-backed checkpoint database. +#[derive(Debug, Clone)] +pub struct CheckpointDB { + db: Arc, +} + +impl CheckpointDB { + /// Encoded [`L2BlockInfo`] length. + pub const VALUE_LEN: usize = CHECKPOINT_VALUE_LEN; + + /// Opens a checkpoint database at the given path. + pub fn open(path: impl AsRef) -> Result { + if let Some(parent) = path.as_ref().parent() { + std::fs::create_dir_all(parent).map_err(|e| { + CheckpointError::Database(format!("failed to create directory: {e}")) + })?; + } + + let db = Database::create(path).map_err(|e| CheckpointError::Database(e.to_string()))?; + let txn = db.begin_write().map_err(|e| CheckpointError::Database(e.to_string()))?; + txn.open_table(CHECKPOINTS).map_err(|e| CheckpointError::Database(e.to_string()))?; + txn.commit().map_err(|e| CheckpointError::Database(e.to_string()))?; + + Ok(Self { db: Arc::new(db) }) + } + + /// Stores a checkpoint for the given label. + pub async fn update( + &self, + label: ForkchoiceCheckpointLabel, + block: L2BlockInfo, + ) -> Result<(), CheckpointError> { + let db = Arc::clone(&self.db); + task::spawn_blocking(move || { + let txn = db.begin_write().map_err(|e| CheckpointError::Database(e.to_string()))?; + { + let mut table = txn + .open_table(CHECKPOINTS) + .map_err(|e| CheckpointError::Database(e.to_string()))?; + table + .insert(label_key(label), &Self::encode(block)) + .map_err(|e| CheckpointError::Database(e.to_string()))?; + } + txn.commit().map_err(|e| CheckpointError::Database(e.to_string())) + }) + .await + .map_err(|e| CheckpointError::Database(format!("blocking task panicked: {e}")))? + } + + /// Returns a checkpoint for the given label. + pub async fn checkpoint( + &self, + label: ForkchoiceCheckpointLabel, + ) -> Result, CheckpointError> { + let db = Arc::clone(&self.db); + task::spawn_blocking(move || { + let txn = db.begin_read().map_err(|e| CheckpointError::Database(e.to_string()))?; + let table = txn + .open_table(CHECKPOINTS) + .map_err(|e| CheckpointError::Database(e.to_string()))?; + Ok(table + .get(label_key(label)) + .map_err(|e| CheckpointError::Database(e.to_string()))? + .map(|value| Self::decode(value.value()))) + }) + .await + .map_err(|e| CheckpointError::Database(format!("blocking task panicked: {e}")))? + } + + fn encode(block: L2BlockInfo) -> [u8; Self::VALUE_LEN] { + let mut bytes = [0; Self::VALUE_LEN]; + put_b256(&mut bytes, 0, block.block_info.hash); + put_u64(&mut bytes, 32, block.block_info.number); + put_b256(&mut bytes, 40, block.block_info.parent_hash); + put_u64(&mut bytes, 72, block.block_info.timestamp); + put_u64(&mut bytes, 80, block.l1_origin.number); + put_b256(&mut bytes, 88, block.l1_origin.hash); + put_u64(&mut bytes, 120, block.seq_num); + bytes + } + + fn decode(bytes: &[u8; Self::VALUE_LEN]) -> L2BlockInfo { + L2BlockInfo { + block_info: BlockInfo { + hash: get_b256(bytes, 0), + number: get_u64(bytes, 32), + parent_hash: get_b256(bytes, 40), + timestamp: get_u64(bytes, 72), + }, + l1_origin: alloy_eips::BlockNumHash { + number: get_u64(bytes, 80), + hash: get_b256(bytes, 88), + }, + seq_num: get_u64(bytes, 120), + } + } +} + +const fn label_key(label: ForkchoiceCheckpointLabel) -> u8 { + match label { + ForkchoiceCheckpointLabel::Safe => 0, + ForkchoiceCheckpointLabel::Finalized => 1, + } +} + +fn put_b256(bytes: &mut [u8; CheckpointDB::VALUE_LEN], offset: usize, value: B256) { + bytes[offset..offset + 32].copy_from_slice(value.as_slice()); +} + +fn put_u64(bytes: &mut [u8; CheckpointDB::VALUE_LEN], offset: usize, value: u64) { + bytes[offset..offset + 8].copy_from_slice(&value.to_be_bytes()); +} + +fn get_b256(bytes: &[u8; CheckpointDB::VALUE_LEN], offset: usize) -> B256 { + B256::from_slice(&bytes[offset..offset + 32]) +} + +fn get_u64(bytes: &[u8; CheckpointDB::VALUE_LEN], offset: usize) -> u64 { + u64::from_be_bytes(bytes[offset..offset + 8].try_into().expect("slice length is 8")) +} + +#[cfg(test)] +mod tests { + use alloy_eips::BlockNumHash; + use alloy_primitives::B256; + use base_consensus_engine::ForkchoiceCheckpointLabel; + use base_protocol::{BlockInfo, L2BlockInfo}; + + use super::CheckpointDB; + + #[tokio::test] + async fn checkpoint_survives_reopen() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("checkpoint.redb"); + let checkpoint = L2BlockInfo { + block_info: BlockInfo { + hash: B256::with_last_byte(1), + number: 10, + parent_hash: B256::with_last_byte(2), + timestamp: 30, + }, + l1_origin: BlockNumHash { number: 4, hash: B256::with_last_byte(5) }, + seq_num: 6, + }; + + { + let db = CheckpointDB::open(&path).unwrap(); + db.update(ForkchoiceCheckpointLabel::Safe, checkpoint).await.unwrap(); + } + + let db = CheckpointDB::open(&path).unwrap(); + let stored = db.checkpoint(ForkchoiceCheckpointLabel::Safe).await.unwrap(); + assert_eq!(stored, Some(checkpoint)); + } +} diff --git a/crates/consensus/service/src/actors/checkpoint/error.rs b/crates/consensus/service/src/actors/checkpoint/error.rs new file mode 100644 index 0000000000..e838af70fd --- /dev/null +++ b/crates/consensus/service/src/actors/checkpoint/error.rs @@ -0,0 +1,15 @@ +//! Checkpoint actor error types. + +/// Error returned by checkpoint actor operations. +#[derive(Debug, thiserror::Error)] +pub enum CheckpointError { + /// Database error. + #[error("checkpoint database error: {0}")] + Database(String), + /// Checkpoint actor channel closed. + #[error("checkpoint actor channel closed")] + ChannelClosed, + /// Checkpoint actor response was dropped. + #[error("checkpoint actor response dropped")] + ResponseDropped, +} diff --git a/crates/consensus/service/src/actors/checkpoint/mod.rs b/crates/consensus/service/src/actors/checkpoint/mod.rs new file mode 100644 index 0000000000..f6f95c169f --- /dev/null +++ b/crates/consensus/service/src/actors/checkpoint/mod.rs @@ -0,0 +1,13 @@ +//! Checkpoint actor and storage for durable consensus restart state. + +mod actor; +pub use actor::CheckpointActor; + +mod client; +pub use client::{CheckpointClient, CheckpointRequest, CheckpointWriter, NoopCheckpointWriter}; + +mod db; +pub use db::CheckpointDB; + +mod error; +pub use error::CheckpointError; diff --git a/crates/consensus/service/src/actors/engine/engine_request_processor.rs b/crates/consensus/service/src/actors/engine/engine_request_processor.rs index 6c4f02fc69..f1b86f47fa 100644 --- a/crates/consensus/service/src/actors/engine/engine_request_processor.rs +++ b/crates/consensus/service/src/actors/engine/engine_request_processor.rs @@ -7,8 +7,9 @@ use base_consensus_derive::{ResetSignal, Signal}; use base_consensus_engine::{ BuildTask, ConsolidateInput, ConsolidateTask, DelegatedForkchoiceTask, DelegatedForkchoiceUpdate, Engine, EngineClient, EngineSyncStateUpdate, EngineTask, - EngineTaskError, EngineTaskErrorSeverity, FinalizeTask, GetPayloadTask, InsertPayloadSafety, - InsertTask, Metrics as EngineMetrics, SealTask, + EngineTaskError, EngineTaskErrorSeverity, FinalizeTask, ForkchoiceCheckpointLabel, + ForkchoiceCheckpointReader, GetPayloadTask, InsertPayloadSafety, InsertTask, + Metrics as EngineMetrics, NoopForkchoiceCheckpointReader, SealTask, }; use base_protocol::L2BlockInfo; use tokio::{ @@ -17,8 +18,8 @@ use tokio::{ }; use crate::{ - BuildRequest, Conductor, EngineClientError, EngineDerivationClient, EngineError, - GetPayloadRequest, NodeMode, ResetRequest, SealRequest, + BuildRequest, CheckpointWriter, Conductor, EngineClientError, EngineDerivationClient, + EngineError, GetPayloadRequest, NodeMode, NoopCheckpointWriter, ResetRequest, SealRequest, }; /// Requires that the implementor handles [`EngineProcessingRequest`]s via the provided channel. @@ -125,6 +126,10 @@ where node_mode: NodeMode, /// The last safe head update sent. last_safe_head_sent: L2BlockInfo, + /// The last safe head checkpoint written. + last_safe_head_checkpointed: L2BlockInfo, + /// The last finalized head checkpoint written. + last_finalized_head_checkpointed: L2BlockInfo, /// The [`RollupConfig`] . /// A channel to use to relay the current unsafe head. /// ## Note @@ -145,6 +150,10 @@ where client: Arc, /// The [`Engine`] task queue. engine: Engine, + /// Reads checkpointed forkchoice state during reset. + checkpoint_reader: Arc, + /// Writes checkpointed forkchoice state after engine state changes. + checkpoint_writer: Arc, } impl EngineProcessor @@ -159,13 +168,38 @@ where derivation_client: DerivationClient, engine: Engine, options: EngineProcessorOptions, + ) -> Self { + Self::new_with_checkpoint( + client, + config, + derivation_client, + engine, + options, + Arc::new(NoopForkchoiceCheckpointReader), + Arc::new(NoopCheckpointWriter), + ) + } + + /// Constructs a new [`EngineProcessor`] with checkpoint persistence. + pub fn new_with_checkpoint( + client: Arc, + config: Arc, + derivation_client: DerivationClient, + engine: Engine, + options: EngineProcessorOptions, + checkpoint_reader: Arc, + checkpoint_writer: Arc, ) -> Self { Self { + checkpoint_reader, + checkpoint_writer, client, conductor: options.conductor, derivation_client, el_sync_complete: false, engine, + last_finalized_head_checkpointed: L2BlockInfo::default(), + last_safe_head_checkpointed: L2BlockInfo::default(), last_safe_head_sent: L2BlockInfo::default(), node_mode: options.node_mode, rollup: config, @@ -177,8 +211,16 @@ where /// Resets the inner [`Engine`] and propagates the reset to the derivation actor. async fn reset(&mut self) -> Result<(), EngineError> { // Reset the engine. - let l2_safe_head = - self.engine.reset(Arc::clone(&self.client), Arc::clone(&self.rollup)).await?; + let l2_safe_head = self + .engine + .reset_with_checkpoint_reader( + Arc::clone(&self.client), + Arc::clone(&self.rollup), + self.checkpoint_reader.as_ref(), + ) + .await?; + + self.checkpoint_forkchoice_state_if_updated().await?; // Signal the derivation actor to reset. let signal = ResetSignal { l2_safe_head }; @@ -234,6 +276,7 @@ where } } + self.checkpoint_forkchoice_state_if_updated().await?; self.send_derivation_actor_safe_head_if_updated().await?; if !self.el_sync_complete && self.engine.state().el_sync_finished { @@ -243,6 +286,28 @@ where Ok(()) } + async fn checkpoint_forkchoice_state_if_updated(&mut self) -> Result<(), EngineError> { + let safe_head = self.engine.state().sync_state.safe_head(); + if safe_head != L2BlockInfo::default() && safe_head != self.last_safe_head_checkpointed { + self.checkpoint_writer + .update_checkpoint(ForkchoiceCheckpointLabel::Safe, safe_head) + .await?; + self.last_safe_head_checkpointed = safe_head; + } + + let finalized_head = self.engine.state().sync_state.finalized_head(); + if finalized_head != L2BlockInfo::default() + && finalized_head != self.last_finalized_head_checkpointed + { + self.checkpoint_writer + .update_checkpoint(ForkchoiceCheckpointLabel::Finalized, finalized_head) + .await?; + self.last_finalized_head_checkpointed = finalized_head; + } + + Ok(()) + } + fn enqueue_unsafe_payload_insert(&mut self, envelope: BaseExecutionPayloadEnvelope) { self.log_follower_upgrade_activation(&envelope); let task = EngineTask::Insert(Box::new(InsertTask::unsafe_payload( @@ -759,31 +824,34 @@ where mod tests { use std::sync::Arc; - use alloy_eips::{BlockId, BlockNumHash, BlockNumberOrTag, NumHash}; + use alloy_eips::{BlockId, BlockNumHash, BlockNumberOrTag, NumHash, eip2718::Encodable2718}; use alloy_primitives::{Address, B256, Bloom, U256}; use alloy_rpc_types_engine::{ ExecutionPayloadV1, ForkchoiceUpdated, PayloadStatus, PayloadStatusEnum, }; use alloy_rpc_types_eth::Block as RpcBlock; + use async_trait::async_trait; + use base_common_consensus::{BaseTxEnvelope, TxDeposit}; use base_common_genesis::{ChainGenesis, RollupConfig, SystemConfig}; use base_common_rpc_types::Transaction as BaseTransaction; use base_common_rpc_types_engine::{BaseExecutionPayload, BaseExecutionPayloadEnvelope}; use base_consensus_derive::Signal; use base_consensus_engine::{ - Engine, EngineState, + Engine, EngineState, ForkchoiceCheckpointError, ForkchoiceCheckpointLabel, + ForkchoiceCheckpointReader, test_utils::{ TestAttributesBuilder, TestEngineStateBuilder, test_block_info, test_engine_client_builder, }, }; - use base_protocol::{BlockInfo, L2BlockInfo}; + use base_protocol::{BlockInfo, L1BlockInfoBedrock, L2BlockInfo}; use rstest::rstest; use tokio::sync::{mpsc, watch}; use crate::{ BuildRequest, EngineClientError, EngineProcessingRequest, EngineProcessor, - EngineProcessorOptions, EngineRequestReceiver, MockConductor, NodeMode, ResetRequest, - actors::engine::client::MockEngineDerivationClient, + EngineProcessorOptions, EngineRequestReceiver, MockConductor, NodeMode, + NoopCheckpointWriter, ResetRequest, actors::engine::client::MockEngineDerivationClient, }; /// Returns a default all-zero L2 block and its canonical hash. @@ -823,6 +891,33 @@ mod tests { } } + #[derive(Debug)] + struct TestCheckpointReader { + safe: Option, + finalized: Option, + } + + #[async_trait] + impl ForkchoiceCheckpointReader for TestCheckpointReader { + async fn checkpoint( + &self, + label: ForkchoiceCheckpointLabel, + ) -> Result, ForkchoiceCheckpointError> { + Ok(match label { + ForkchoiceCheckpointLabel::Safe => self.safe, + ForkchoiceCheckpointLabel::Finalized => self.finalized, + }) + } + } + + fn l1_info_deposit_tx() -> Vec { + BaseTxEnvelope::from(TxDeposit { + input: L1BlockInfoBedrock::default().encode_calldata(), + ..Default::default() + }) + .encoded_2718() + } + fn unsafe_payload( block_number: u64, parent_hash: B256, @@ -849,6 +944,177 @@ mod tests { } } + fn unsafe_payload_with_l1_info( + block_number: u64, + parent_hash: B256, + block_hash: B256, + ) -> BaseExecutionPayloadEnvelope { + BaseExecutionPayloadEnvelope { + parent_beacon_block_root: None, + execution_payload: BaseExecutionPayload::V1(ExecutionPayloadV1 { + parent_hash, + fee_recipient: Address::ZERO, + state_root: B256::ZERO, + receipts_root: B256::ZERO, + logs_bloom: Bloom::ZERO, + prev_randao: B256::ZERO, + block_number, + gas_limit: 30_000_000, + gas_used: 0, + timestamp: block_number, + extra_data: Default::default(), + base_fee_per_gas: U256::ZERO, + block_hash, + transactions: vec![l1_info_deposit_tx().into()], + }), + } + } + + fn pruned_reth_l2_block(number: u64, parent_hash: B256) -> RpcBlock { + RpcBlock:: { + header: alloy_rpc_types_eth::Header { + inner: alloy_consensus::Header { + number, + parent_hash, + timestamp: number, + ..Default::default() + }, + ..Default::default() + }, + transactions: alloy_rpc_types_eth::BlockTransactions::Full(vec![]), + ..Default::default() + } + } + + fn l1_info_rpc_transaction(block_number: u64) -> BaseTransaction { + let tx = alloy_rpc_types_eth::Transaction { + inner: alloy_consensus::transaction::Recovered::new_unchecked( + BaseTxEnvelope::Deposit(alloy_primitives::Sealed::new(TxDeposit { + input: L1BlockInfoBedrock::default().encode_calldata(), + ..Default::default() + })), + Default::default(), + ), + block_hash: None, + block_number: Some(block_number), + effective_gas_price: Some(1), + transaction_index: Some(0), + }; + + BaseTransaction { inner: tx, deposit_nonce: None, deposit_receipt_version: None } + } + + fn full_reth_l2_block_with_l1_info( + number: u64, + parent_hash: B256, + ) -> RpcBlock { + RpcBlock:: { + header: alloy_rpc_types_eth::Header { + inner: alloy_consensus::Header { + number, + parent_hash, + timestamp: number, + ..Default::default() + }, + ..Default::default() + }, + transactions: alloy_rpc_types_eth::BlockTransactions::Full(vec![ + l1_info_rpc_transaction(number), + ]), + ..Default::default() + } + } + + #[tokio::test] + async fn validator_restart_does_not_crash_when_reth_safe_block_body_is_pruned() { + let (genesis_block, genesis_hash) = make_genesis_block(); + let cfg = Arc::new(RollupConfig { + genesis: ChainGenesis { + l2: BlockNumHash { number: 0, hash: genesis_hash }, + l1: BlockNumHash { number: 0, hash: B256::ZERO }, + system_config: Some(SystemConfig::default()), + ..Default::default() + }, + ..Default::default() + }); + let mut reth_latest = L2BlockInfo { + block_info: BlockInfo { + number: 44_343_433, + hash: B256::with_last_byte(0x42), + parent_hash: B256::with_last_byte(0x41), + timestamp: 44_343_433, + }, + ..Default::default() + }; + let pruned_safe = pruned_reth_l2_block(44_343_433, reth_latest.block_info.parent_hash); + let safe_checkpoint = L2BlockInfo { + block_info: BlockInfo::from( + &pruned_safe + .clone() + .into_consensus() + .map_transactions(|tx| tx.inner.inner.into_inner()), + ), + ..Default::default() + }; + reth_latest.block_info.hash = safe_checkpoint.block_info.hash; + let next_hash = B256::with_last_byte(0x43); + let full_latest = full_reth_l2_block_with_l1_info( + reth_latest.block_info.number + 1, + reth_latest.block_info.hash, + ); + + let client = Arc::new( + test_engine_client_builder() + .with_config(Arc::clone(&cfg)) + .with_l2_block(BlockId::Number(BlockNumberOrTag::Finalized), genesis_block) + .with_l2_block(BlockId::Number(BlockNumberOrTag::Safe), pruned_safe) + .with_l2_block(BlockId::Number(BlockNumberOrTag::Latest), full_latest) + .with_l1_block(BlockId::from(B256::ZERO), RpcBlock::default()) + .with_new_payload_v2_response(PayloadStatus { + status: PayloadStatusEnum::Valid, + latest_valid_hash: Some(next_hash), + }) + .with_fork_choice_updated_v3_response(valid_fcu()) + .build(), + ); + + let mut mock_derivation = MockEngineDerivationClient::new(); + mock_derivation.expect_send_signal().returning(|_| Ok(())); + mock_derivation.expect_send_new_engine_safe_head().returning(|_| Ok(())); + mock_derivation.expect_notify_sync_completed().returning(|_| Ok(())); + + let (state_tx, _) = watch::channel(EngineState::default()); + let (queue_tx, _) = watch::channel(0usize); + let engine = Engine::new(EngineState::default(), state_tx, queue_tx); + + let mut processor = EngineProcessor::new_with_checkpoint( + Arc::clone(&client), + cfg, + mock_derivation, + engine, + EngineProcessorOptions { + node_mode: NodeMode::Validator, + unsafe_head_tx: None, + conductor: None, + sequencer_stopped: false, + }, + Arc::new(TestCheckpointReader { safe: Some(safe_checkpoint), finalized: None }), + Arc::new(NoopCheckpointWriter), + ); + + processor.bootstrap_validator(Some(reth_latest)).await; + processor.handle_external_unsafe_l2_block(unsafe_payload_with_l1_info( + reth_latest.block_info.number + 1, + reth_latest.block_info.hash, + next_hash, + )); + + processor + .drain() + .await + .expect("validator restart must not crash when reth pruned historical block bodies"); + } + fn unsafe_payload_processor( node_mode: NodeMode, el_sync_finished: bool, diff --git a/crates/consensus/service/src/actors/engine/error.rs b/crates/consensus/service/src/actors/engine/error.rs index 667b2107a0..801784a994 100644 --- a/crates/consensus/service/src/actors/engine/error.rs +++ b/crates/consensus/service/src/actors/engine/error.rs @@ -4,6 +4,8 @@ use base_consensus_engine::{EngineResetError, EngineTaskErrors}; +use crate::CheckpointError; + /// An error from the [`EngineActor`]. /// /// [`EngineActor`]: super::EngineActor @@ -18,4 +20,7 @@ pub enum EngineError { /// Engine task error. #[error(transparent)] EngineTask(#[from] EngineTaskErrors), + /// Checkpoint error. + #[error(transparent)] + Checkpoint(#[from] CheckpointError), } diff --git a/crates/consensus/service/src/actors/mod.rs b/crates/consensus/service/src/actors/mod.rs index 93959d9b90..a8156815ab 100644 --- a/crates/consensus/service/src/actors/mod.rs +++ b/crates/consensus/service/src/actors/mod.rs @@ -5,6 +5,12 @@ mod traits; pub use traits::{CancellableContext, NodeActor}; +mod checkpoint; +pub use checkpoint::{ + CheckpointActor, CheckpointClient, CheckpointDB, CheckpointError, CheckpointRequest, + CheckpointWriter, NoopCheckpointWriter, +}; + mod engine; #[cfg(test)] pub use engine::MockEngineDerivationClient; diff --git a/crates/consensus/service/src/lib.rs b/crates/consensus/service/src/lib.rs index 44e6eb53c2..22f5704884 100644 --- a/crates/consensus/service/src/lib.rs +++ b/crates/consensus/service/src/lib.rs @@ -17,10 +17,11 @@ pub use service::{ mod actors; pub use actors::{ - AlloyL1BlockFetcher, BlockStream, BootstrapRole, BuildRequest, CancellableContext, Conductor, - ConductorClient, ConductorError, DelayedL1OriginSelectorProvider, DelegateDerivationActor, - DelegateL2Client, DelegateL2ClientError, DelegateL2DerivationActor, DerivationActor, - DerivationActorRequest, DerivationClientError, DerivationClientResult, + AlloyL1BlockFetcher, BlockStream, BootstrapRole, BuildRequest, CancellableContext, + CheckpointActor, CheckpointClient, CheckpointDB, CheckpointError, CheckpointRequest, + CheckpointWriter, Conductor, ConductorClient, ConductorError, DelayedL1OriginSelectorProvider, + DelegateDerivationActor, DelegateL2Client, DelegateL2ClientError, DelegateL2DerivationActor, + DerivationActor, DerivationActorRequest, DerivationClientError, DerivationClientResult, DerivationDelegateClient, DerivationDelegateClientError, DerivationEngineClient, DerivationError, DerivationState, DerivationStateMachine, DerivationStateTransitionError, DerivationStateUpdate, EngineActor, EngineActorRequest, EngineClientError, EngineClientResult, @@ -31,14 +32,14 @@ pub use actors::{ L1WatcherQueryExecutor, L1WatcherQueryProcessor, L2Finalizer, L2SourceClient, LogRetrier, NetworkActor, NetworkActorError, NetworkBuilder, NetworkBuilderError, NetworkConfig, NetworkDriver, NetworkDriverError, NetworkEngineClient, NetworkHandler, NetworkInboundData, - NodeActor, OriginSelector, PayloadBuilder, PayloadSealer, PendingStopSender, PoolActivation, - QueuedDerivationEngineClient, QueuedEngineDerivationClient, QueuedEngineRpcClient, - QueuedL1WatcherDerivationClient, QueuedNetworkEngineClient, QueuedSequencerAdminAPIClient, - QueuedSequencerEngineClient, QueuedUnsafePayloadGossipClient, RecoveryModeGuard, ResetRequest, - RpcActor, RpcActorError, RpcContext, ScheduledTicker, SealRequest, SealState, SealStepError, - SequencerActor, SequencerActorError, SequencerAdminQuery, SequencerConfig, - SequencerEngineClient, UnsafePayloadGossipClient, UnsafePayloadGossipClientError, - UnsealedPayloadHandle, + NodeActor, NoopCheckpointWriter, OriginSelector, PayloadBuilder, PayloadSealer, + PendingStopSender, PoolActivation, QueuedDerivationEngineClient, QueuedEngineDerivationClient, + QueuedEngineRpcClient, QueuedL1WatcherDerivationClient, QueuedNetworkEngineClient, + QueuedSequencerAdminAPIClient, QueuedSequencerEngineClient, QueuedUnsafePayloadGossipClient, + RecoveryModeGuard, ResetRequest, RpcActor, RpcActorError, RpcContext, ScheduledTicker, + SealRequest, SealState, SealStepError, SequencerActor, SequencerActorError, + SequencerAdminQuery, SequencerConfig, SequencerEngineClient, UnsafePayloadGossipClient, + UnsafePayloadGossipClientError, UnsealedPayloadHandle, }; mod metrics; diff --git a/crates/consensus/service/src/service/builder.rs b/crates/consensus/service/src/service/builder.rs index 848fcedff2..b6084e6d39 100644 --- a/crates/consensus/service/src/service/builder.rs +++ b/crates/consensus/service/src/service/builder.rs @@ -74,6 +74,10 @@ pub struct RollupNodeBuilder { /// When `None`, [`L1Config::default_finalized_poll_interval`] is used to select a /// chain-appropriate default derived from `config.l1_chain_id`. pub finalized_poll_interval: Option, + /// Optional path to the checkpoint database file. + /// + /// When `None`, the node stores checkpoints under the default consensus data directory. + pub checkpoint_path: Option, /// Optional path to the safe head database file. /// /// When set, enables persistent safe head tracking via redb and serves @@ -115,6 +119,7 @@ impl RollupNodeBuilder { sequencer_config: None, derivation_delegate_config: None, finalized_poll_interval: None, + checkpoint_path: None, safedb_path: None, } } @@ -157,6 +162,11 @@ impl RollupNodeBuilder { Self { safedb_path: Some(path), ..self } } + /// Sets the checkpoint database path. + pub fn with_checkpoint_path(self, path: PathBuf) -> Self { + Self { checkpoint_path: Some(path), ..self } + } + /// Assembles the [`RollupNode`] service. /// /// Returns an error if the internal L2 provider transport cannot be constructed. WebSocket @@ -190,6 +200,9 @@ impl RollupNodeBuilder { .await?; let rollup_config = Arc::new(self.config); + let checkpoint_path = self.checkpoint_path.unwrap_or_else(|| { + Self::default_checkpoint_path(self.engine_config.config.l2_chain_id.id()) + }); let p2p_config = self.p2p_config; let sequencer_config = self.sequencer_config.unwrap_or_default(); @@ -210,9 +223,19 @@ impl RollupNodeBuilder { p2p_config, sequencer_config, derivation_delegate_provider, + checkpoint_path, safedb_path: self.safedb_path, }) } + + fn default_checkpoint_path(l2_chain_id: u64) -> PathBuf { + std::env::var_os("HOME") + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from(".")) + .join(".base") + .join(l2_chain_id.to_string()) + .join("checkpoint.redb") + } } #[cfg(test)] diff --git a/crates/consensus/service/src/service/node.rs b/crates/consensus/service/src/service/node.rs index 52570ec815..951f13db52 100644 --- a/crates/consensus/service/src/service/node.rs +++ b/crates/consensus/service/src/service/node.rs @@ -13,7 +13,7 @@ use base_common_chains::ChainConfig; use base_common_genesis::RollupConfig; use base_common_network::Base; use base_consensus_derive::{Pipeline, SignalReceiver, StatefulAttributesBuilder}; -use base_consensus_engine::{Engine, EngineClient, EngineState}; +use base_consensus_engine::{Engine, EngineClient, EngineState, ForkchoiceCheckpointReader}; use base_consensus_providers::{ AlloyChainProvider, AlloyL2ChainProvider, OnlineBeaconClient, OnlineBlobProvider, OnlinePipeline, @@ -25,15 +25,15 @@ use tokio::sync::{mpsc, watch}; use tokio_util::sync::CancellationToken; use crate::{ - AlloyL1BlockFetcher, Conductor, ConductorClient, DelayedL1OriginSelectorProvider, - DelegateDerivationActor, DerivationActor, DerivationDelegateClient, DerivationError, - EngineActor, EngineActorRequest, EngineConfig, EngineProcessor, EngineProcessorOptions, - EngineRpcProcessor, L1OriginSelector, L1WatcherActor, L1WatcherQueryProcessor, NetworkActor, - NetworkBuilder, NetworkConfig, NodeActor, NodeMode, PayloadBuilder, - QueuedDerivationEngineClient, QueuedEngineDerivationClient, QueuedEngineRpcClient, - QueuedL1WatcherDerivationClient, QueuedNetworkEngineClient, QueuedSequencerAdminAPIClient, - QueuedSequencerEngineClient, RecoveryModeGuard, RpcActor, RpcContext, SequencerActor, - SequencerConfig, + AlloyL1BlockFetcher, CheckpointActor, CheckpointClient, CheckpointDB, CheckpointWriter, + Conductor, ConductorClient, DelayedL1OriginSelectorProvider, DelegateDerivationActor, + DerivationActor, DerivationDelegateClient, DerivationError, EngineActor, EngineActorRequest, + EngineConfig, EngineProcessor, EngineProcessorOptions, EngineRpcProcessor, L1OriginSelector, + L1WatcherActor, L1WatcherQueryProcessor, NetworkActor, NetworkBuilder, NetworkConfig, + NodeActor, NodeMode, PayloadBuilder, QueuedDerivationEngineClient, + QueuedEngineDerivationClient, QueuedEngineRpcClient, QueuedL1WatcherDerivationClient, + QueuedNetworkEngineClient, QueuedSequencerAdminAPIClient, QueuedSequencerEngineClient, + RecoveryModeGuard, RpcActor, RpcContext, SequencerActor, SequencerConfig, actors::{BlockStream, NetworkInboundData, QueuedUnsafePayloadGossipClient}, }; @@ -104,6 +104,11 @@ pub struct RollupNode { pub sequencer_config: SequencerConfig, /// Optional derivation delegate provider. pub derivation_delegate_provider: Option, + /// Path to the mandatory checkpoint database. + /// + /// The node records safe/finalized forkchoice checkpoints here so restart can recover + /// `L2BlockInfo` when reth has pruned old block bodies. + pub checkpoint_path: PathBuf, /// Optional path to the safe head database. /// /// When set, the node records L1→L2 safe head mappings to a persistent redb database and @@ -225,6 +230,7 @@ impl RollupNode { ) } + #[allow(clippy::too_many_arguments)] fn create_engine_actor( &self, engine_client: Arc, @@ -233,6 +239,7 @@ impl RollupNode { derivation_client: QueuedEngineDerivationClient, unsafe_head_tx: watch::Sender, conductor: Option>, + checkpoint_client: CheckpointClient, ) -> (EngineActor>, EngineRpcProcessor) { let engine_state = EngineState::default(); @@ -241,7 +248,10 @@ impl RollupNode { let engine = Engine::new(engine_state, engine_state_tx, engine_queue_length_tx); let mode = self.mode(); - let engine_processor = EngineProcessor::new( + let checkpoint_reader: Arc = + Arc::new(checkpoint_client.clone()); + let checkpoint_writer: Arc = Arc::new(checkpoint_client); + let engine_processor = EngineProcessor::new_with_checkpoint( Arc::clone(&engine_client), Arc::clone(&self.config), derivation_client, @@ -252,6 +262,8 @@ impl RollupNode { conductor, sequencer_stopped: self.sequencer_config.sequencer_stopped, }, + checkpoint_reader, + checkpoint_writer, ); let engine_rpc_processor = EngineRpcProcessor::new( @@ -377,6 +389,11 @@ impl RollupNode { let (engine_actor_request_tx, engine_actor_request_rx) = mpsc::channel(1024); let (engine_rpc_request_tx, engine_rpc_request_rx) = mpsc::channel(1024); let (unsafe_head_tx, unsafe_head_rx) = watch::channel(L2BlockInfo::default()); + let (checkpoint_request_tx, checkpoint_request_rx) = mpsc::channel(1024); + let checkpoint_db = CheckpointDB::open(&self.checkpoint_path) + .map_err(|e| format!("failed to open checkpoint database: {e}"))?; + let checkpoint_actor = CheckpointActor::new(checkpoint_db, checkpoint_request_rx); + let checkpoint_client = CheckpointClient::new(checkpoint_request_tx); // Create the conductor client early — the engine processor needs it for the // bootstrap leadership check and the sequencer actor needs it for block building. @@ -398,6 +415,7 @@ impl RollupNode { QueuedEngineDerivationClient::new(derivation_actor_request_tx.clone()), unsafe_head_tx, engine_conductor, + checkpoint_client, ); // Select the concrete derivation actor implementation based on @@ -567,6 +585,7 @@ impl RollupNode { Some((l1_watcher, ())), Some((l1_query_processor, ())), Some((derivation, ())), + Some((checkpoint_actor, cancellation.clone())), Some((engine_actor, ())), engine_rpc_actor, ] diff --git a/crates/execution/cli/src/lib.rs b/crates/execution/cli/src/lib.rs index 859d6616b7..5e3b086ac2 100644 --- a/crates/execution/cli/src/lib.rs +++ b/crates/execution/cli/src/lib.rs @@ -36,7 +36,7 @@ use reth_node_core::{ // This allows us to manually enable node metrics features, required for proper jemalloc metric // reporting use reth_node_metrics as _; -use reth_rpc_server_types::{DefaultRpcModuleValidator, RpcModuleValidator}; +use reth_rpc_server_types::{LenientRpcModuleValidator, RpcModuleValidator}; pub use standard_node::{StandardBaseRethNode, StandardNodeArgs}; /// The main base-reth cli interface. @@ -46,7 +46,7 @@ pub use standard_node::{StandardBaseRethNode, StandardNodeArgs}; #[command(author, name = version_metadata().name_client.as_ref(), version = version_metadata().short_version.as_ref(), long_version = version_metadata().long_version.as_ref(), about = "Reth", long_about = None)] pub struct Cli< Ext: clap::Args + fmt::Debug = RollupArgs, - Rpc: RpcModuleValidator = DefaultRpcModuleValidator, + Rpc: RpcModuleValidator = LenientRpcModuleValidator, > { /// The command to run #[command(subcommand)] diff --git a/crates/proof/succinct/elf/manifest.toml b/crates/proof/succinct/elf/manifest.toml index 6fc459f261..4c276debd0 100644 --- a/crates/proof/succinct/elf/manifest.toml +++ b/crates/proof/succinct/elf/manifest.toml @@ -5,16 +5,19 @@ # # just succinct build-elfs # -# That target writes the freshly built ELFs into this directory and rewrites -# the `sha256` fields below. Commit the resulting manifest change together with -# the code change that caused the ELF to change. +# Then update this manifest with: +# +# just succinct write-manifest +# +# Commit the resulting manifest change together with the code change that caused +# the ELF to change. # # The `base-proof-succinct-elfs` crate's build.rs verifies the local cache against # these hashes; builds fail fast if they drift. [[elfs]] name = "range-elf-embedded" -sha256 = "b787a342d906b270a682cbc085e9e5d4e2e0b8831097e0bac9b75f8ae5d9c1b8" +sha256 = "73baf3832f222f42b9b8b765dbf2d635a1f4cce79f71db04c8120b3b0916a05e" [[elfs]] name = "aggregation-elf" diff --git a/crates/proof/succinct/programs/Cargo.lock b/crates/proof/succinct/programs/Cargo.lock index 567a7f3c71..ccdf686d74 100644 --- a/crates/proof/succinct/programs/Cargo.lock +++ b/crates/proof/succinct/programs/Cargo.lock @@ -793,7 +793,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base-common-chains" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-eips", @@ -808,7 +808,7 @@ dependencies = [ [[package]] name = "base-common-consensus" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -828,7 +828,7 @@ dependencies = [ [[package]] name = "base-common-evm" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -845,11 +845,11 @@ dependencies = [ [[package]] name = "base-common-flz" -version = "0.9.0" +version = "0.9.1" [[package]] name = "base-common-genesis" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -865,7 +865,7 @@ dependencies = [ [[package]] name = "base-common-precompiles" -version = "0.9.0" +version = "0.9.1" dependencies = [ "base-common-chains", "revm", @@ -873,7 +873,7 @@ dependencies = [ [[package]] name = "base-common-rpc-types-engine" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -889,7 +889,7 @@ dependencies = [ [[package]] name = "base-consensus-derive" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -910,7 +910,7 @@ dependencies = [ [[package]] name = "base-consensus-upgrades" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -919,11 +919,11 @@ dependencies = [ [[package]] name = "base-metrics" -version = "0.9.0" +version = "0.9.1" [[package]] name = "base-proof" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "base-proof-driver" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-evm", @@ -976,7 +976,7 @@ dependencies = [ [[package]] name = "base-proof-executor" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -998,7 +998,7 @@ dependencies = [ [[package]] name = "base-proof-mpt" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -1009,7 +1009,7 @@ dependencies = [ [[package]] name = "base-proof-preimage" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-primitives", "async-trait", @@ -1021,7 +1021,7 @@ dependencies = [ [[package]] name = "base-proof-primitives" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-chains", "alloy-eips", @@ -1034,7 +1034,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-client-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -1069,7 +1069,7 @@ dependencies = [ [[package]] name = "base-proof-succinct-ethereum-client-utils" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-genesis", "anyhow", @@ -1099,7 +1099,7 @@ dependencies = [ [[package]] name = "base-protocol" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloc-no-stdlib", "alloy-consensus", From 9f4a14dd53b74715105ba47a4d11f7cf8c6a50d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Duchesneau?= Date: Wed, 27 May 2026 15:05:44 -0400 Subject: [PATCH 3/5] adjust Cargo.lock, add comment in src/main.rs to prevent warnings --- Cargo.lock | 6 +++--- bin/node/src/main.rs | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d73fa1d926..e24d6d27db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4085,7 +4085,7 @@ dependencies = [ [[package]] name = "base-execution-firehose" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-evm", @@ -4292,7 +4292,7 @@ dependencies = [ [[package]] name = "base-firehose-flashblocks" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -4333,7 +4333,7 @@ dependencies = [ [[package]] name = "base-firehose-tests" -version = "0.9.0" +version = "0.9.1" dependencies = [ "alloy-consensus", "alloy-eips", diff --git a/bin/node/src/main.rs b/bin/node/src/main.rs index 0432ce36df..d07f66f127 100644 --- a/bin/node/src/main.rs +++ b/bin/node/src/main.rs @@ -13,6 +13,7 @@ use crate::firehose::{FirehoseExtension, FirehoseFlashblocksExtension}; /// CLI arguments: upstream's standard set extended with Firehose-specific options. #[derive(Debug, Clone, PartialEq, Eq, clap::Args)] pub struct Args { + /// Standard upstream node arguments. #[command(flatten)] pub standard: StandardNodeArgs, From 726f8ff3d580a88cb05916fa1e38693215ae6f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Duchesneau?= Date: Wed, 27 May 2026 16:34:36 -0400 Subject: [PATCH 4/5] inform changelog --- CHANGELOG.sf.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.sf.md b/CHANGELOG.sf.md index 682a8d709f..cc8b9c5b40 100644 --- a/CHANGELOG.sf.md +++ b/CHANGELOG.sf.md @@ -1,3 +1,12 @@ +## v0.9.1-fh + +* bumped upstream to 0.9.1 +* flashblocks + +## v0.9.0-fh + +* bumped upstream to 0.9.0 + ## v0.8.0-fh ### Changes From 5847058eaae070d6d53608223fe70d833ef8c1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Duchesneau?= Date: Wed, 27 May 2026 16:37:38 -0400 Subject: [PATCH 5/5] more detailed changelog --- CHANGELOG.sf.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.sf.md b/CHANGELOG.sf.md index cc8b9c5b40..3b8f041987 100644 --- a/CHANGELOG.sf.md +++ b/CHANGELOG.sf.md @@ -1,7 +1,7 @@ ## v0.9.1-fh * bumped upstream to 0.9.1 -* flashblocks +* initial flashblocks wiring (not to be used yet) ## v0.9.0-fh @@ -9,6 +9,8 @@ ## v0.8.0-fh +* Parity with geth implementation, except flashblocks + ### Changes * Update workspace version to `v0.8.0`.