diff --git a/Cargo.lock b/Cargo.lock index aebb5828..a4cded85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.21" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ebac8ff9c2f07667e1803dc777304337e160ce5153335beb45e8ec0751808" +checksum = "4bc32535569185cbcb6ad5fa64d989a47bccb9a08e27284b1f2a3ccf16e6d010" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e318e25fb719e747a7e8db1654170fc185024f3ed5b10f86c08d448a912f6e2" +checksum = "ad704069c12f68d0c742d0cad7e0a03882b42767350584627fbf8a47b1bf1846" dependencies = [ "alloy-eips", "alloy-primitives", @@ -138,9 +138,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "364380a845193a317bcb7a5398fc86cdb66c47ebe010771dde05f6869bf9e64a" +checksum = "bc374f640a5062224d7708402728e3d6879a514ba10f377da62e7dfb14c673e6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -153,9 +153,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d39c80ffc806f27a76ed42f3351a455f3dc4f81d6ff92c8aad2cf36b7d3a34" +checksum = "15c493b2812943f7b58191063a8d13ea97c76099900869c08231e8eba3bf2f92" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -238,9 +238,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c4d7c5839d9f3a467900c625416b24328450c65702eb3d8caff8813e4d1d33" +checksum = "7e867b5fd52ed0372a95016f3a37cbff95a9d5409230fbaef2d8ea00e8618098" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -287,9 +287,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba4b1be0988c11f0095a2380aa596e35533276b8fa6c9e06961bbfe0aebcac5" +checksum = "b90be17e9760a6ba6d13cebdb049cea405ebc8bf57d90664ed708cc5bc348342" dependencies = [ "alloy-eips", "alloy-primitives", @@ -302,9 +302,9 @@ dependencies = [ [[package]] name = "alloy-hardforks" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9a33550fc21fd77a3f8b63e99969d17660eec8dcc50a95a80f7c9964f7680b" +checksum = "1e29d7eacf42f89c21d7f089916d0bdb4f36139a31698790e8837d2dbbd4b2c3" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -328,9 +328,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72cf87cda808e593381fb9f005ffa4d2475552b7a6c5ac33d087bf77d82abd0" +checksum = "dcab4c51fb1273e3b0f59078e0cdf8aa99f697925b09f0d2055c18be46b4d48c" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -343,9 +343,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12aeb37b6f2e61b93b1c3d34d01ee720207c76fe447e2a2c217e433ac75b17f5" +checksum = "196d7fd3f5d414f7bbd5886a628b7c42bd98d1b126f9a7cff69dbfd72007b39c" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -369,9 +369,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd29ace62872083e30929cd9b282d82723196d196db589f3ceda67edcc05552" +checksum = "0d3ae2777e900a7a47ad9e3b8ab58eff3d93628265e73bbdee09acf90bf68f75" dependencies = [ "alloy-consensus", "alloy-eips", @@ -400,9 +400,9 @@ dependencies = [ [[package]] name = "alloy-op-hardforks" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f96fb2fce4024ada5b2c11d4076acf778a0d3e4f011c6dfd2ffce6d0fcf84ee9" +checksum = "95ac97adaba4c26e17192d81f49186ac20c1e844e35a00e169c8d3d58bc84e6b" dependencies = [ "alloy-chains", "alloy-hardforks", @@ -424,8 +424,8 @@ dependencies = [ "derive_more", "foldhash 0.2.0", "getrandom 0.3.4", - "hashbrown 0.16.1", - "indexmap 2.12.1", + "hashbrown 0.16.0", + "indexmap 2.12.0", "itoa", "k256", "keccak-asm", @@ -442,9 +442,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b710636d7126e08003b8217e24c09f0cca0b46d62f650a841736891b1ed1fc1" +checksum = "9f9bf40c9b2a90c7677f9c39bccd9f06af457f35362439c0497a706f16557703" dependencies = [ "alloy-chains", "alloy-consensus", @@ -484,9 +484,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd4c64eb250a18101d22ae622357c6b505e158e9165d4c7974d59082a600c5e" +checksum = "acfdbe41e2ef1a7e79b5ea115baa750f9381ac9088fb600f4cedc731cf04a151" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -523,14 +523,14 @@ checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "alloy-rpc-client" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0882e72d2c1c0c79dcf4ab60a67472d3f009a949f774d4c17d0bdb669cfde05" +checksum = "e7c2630fde9ff6033a780635e1af6ef40e92d74a9cacb8af3defc1b15cfebca5" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -554,9 +554,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cf1398cb33aacb139a960fa3d8cf8b1202079f320e77e952a0b95967bf7a9f" +checksum = "ad098153a12382c22a597e865530033f5e644473742d6c733562d448125e02a2" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -567,9 +567,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-admin" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bcf50ccb65d29b8599f8f5e23dcac685f1d79459654c830cba381345760e901" +checksum = "b7604c415f725bd776d46dae44912c276cc3d8af37f37811e5675389791aa0c6" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -579,9 +579,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e176c26fdd87893b6afeb5d92099d8f7e7a1fe11d6f4fe0883d6e33ac5f31ba" +checksum = "214d9d1033c173ab8fa32edd8a4655cd784447c820b0b66cd0d5167e049567d6" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -591,9 +591,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a63fb40ed24e4c92505f488f9dd256e2afaed17faa1b7a221086ebba74f4122" +checksum = "50b8429b5b62d21bf3691eb1ae12aaae9bb496894d5a114e3cc73e27e6800ec8" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -602,9 +602,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1786681640d4c60f22b6b8376b0f3fa200360bf1c3c2cb913e6c97f51928eb1b" +checksum = "f67f8269e8b5193a5328dd3ef4d60f93524071e53a993776e290581a59aa15fa" dependencies = [ "alloy-eips", "alloy-primitives", @@ -622,9 +622,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2ca3a434a6d49910a7e8e51797eb25db42ef8a5578c52d877fcb26d0afe7bc" +checksum = "01731601ea631bd825c652a225701ab466c09457f446b8d8129368a095389c5d" dependencies = [ "alloy-primitives", "derive_more", @@ -634,9 +634,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c60bdce3be295924122732b7ecd0b2495ce4790bedc5370ca7019c08ad3f26e" +checksum = "9981491bb98e76099983f516ec7de550db0597031f5828c994961eb4bb993cce" dependencies = [ "alloy-consensus", "alloy-eips", @@ -654,9 +654,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eae0c7c40da20684548cbc8577b6b7447f7bf4ddbac363df95e3da220e41e72" +checksum = "29031a6bf46177d65efce661f7ab37829ca09dd341bc40afb5194e97600655cc" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -676,9 +676,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-mev" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a97bfc6d9b411c85bb08e1174ddd3e5d61b10d3bd13f529d6609f733cb2f6f" +checksum = "c5c5c78bdd2c72c47e66ab977af420fb4a10279707d4edbd2575693c47aa54a2" dependencies = [ "alloy-consensus", "alloy-eips", @@ -691,9 +691,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55324323aa634b01bdecb2d47462a8dce05f5505b14a6e5db361eef16eda476" +checksum = "01b842f5aac6676ff4b2e328262d03bdf49807eaec3fe3a4735c45c97388518b" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -705,9 +705,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b1aa28effb6854be356ce92ed64cea3b323acd04c3f8bfb5126e2839698043" +checksum = "7fa12c608873beeb7afa392944dce8829fa8a50c487f266863bb2dd6b743c4a2" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -717,9 +717,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0df1987ed0ff2d0159d76b52e7ddfc4e4fbddacc54d2fbee765e0d14d7c01b5" +checksum = "01e856112bfa0d9adc85bd7c13db03fad0e71d1d6fb4c2010e475b6718108236" dependencies = [ "alloy-primitives", "arbitrary", @@ -729,9 +729,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff69deedee7232d7ce5330259025b868c5e6a52fa8dffda2c861fb3a5889b24" +checksum = "66a4f629da632d5279bbc5731634f0f5c9484ad9c4cad0cd974d9669dc1f46d6" dependencies = [ "alloy-primitives", "async-trait", @@ -744,9 +744,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930e17cb1e46446a193a593a3bfff8d0ecee4e510b802575ebe300ae2e43ef75" +checksum = "76c8950810dc43660c0f22883659c4218e090a5c75dce33fa4ca787715997b7b" dependencies = [ "alloy-consensus", "alloy-network", @@ -772,7 +772,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -785,11 +785,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.12.1", + "indexmap 2.12.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "syn-solidity", "tiny-keccak", ] @@ -808,7 +808,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.111", + "syn 2.0.110", "syn-solidity", ] @@ -836,9 +836,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be98b07210d24acf5b793c99b759e9a696e4a2e67593aec0487ae3b3e1a2478c" +checksum = "fe215a2f9b51d5f1aa5c8cf22c8be8cdb354934de09c9a4e37aefb79b77552fd" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -859,9 +859,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4198a1ee82e562cab85e7f3d5921aab725d9bd154b6ad5017f82df1695877c97" +checksum = "dc1b37b1a30d23deb3a8746e882c70b384c574d355bc2bbea9ea918b0c31366e" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -874,9 +874,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8db249779ebc20dc265920c7e706ed0d31dbde8627818d1cbde60919b875bb0" +checksum = "52c81a4deeaa0d4b022095db17b286188d731e29ea141d4ec765e166732972e4" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -894,9 +894,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad2344a12398d7105e3722c9b7a7044ea837128e11d453604dec6e3731a86e2" +checksum = "4e9d6f5f304e8943afede2680e5fc7008780d4fc49387eafd53192ad95e20091" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -932,14 +932,14 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "333544408503f42d7d3792bfc0f7218b643d968a03d2c0ed383ae558fb4a76d0" +checksum = "7ccf423f6de62e8ce1d6c7a11fb7508ae3536d02e0d68aaeb05c8669337d0937" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1024,7 +1024,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1172,7 +1172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1210,7 +1210,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1299,7 +1299,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1365,9 +1365,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.34" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473" +checksum = "93c1f86859c1af3d514fa19e8323147ff10ea98684e6c7b307912509f50e67b2" dependencies = [ "compression-codecs", "compression-core", @@ -1416,7 +1416,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1427,7 +1427,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1465,7 +1465,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1558,6 +1558,8 @@ dependencies = [ "reth-rpc-convert", "reth-rpc-eth-api", "reth-testing-utils", + "revm", + "revm-database", "tokio", "tokio-tungstenite 0.28.0", "tracing", @@ -1594,11 +1596,13 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-sol-macro", "alloy-sol-types", + "arc-swap", "base-flashtypes", "base-reth-flashblocks", "base-reth-test-utils", "eyre", "futures-util", + "hex-literal", "httpmock", "jsonrpsee", "jsonrpsee-types", @@ -1624,6 +1628,8 @@ dependencies = [ "reth-testing-utils", "reth-tracing", "reth-transaction-pool", + "reth-trie-common", + "revm-database", "serde", "serde_json", "tips-core", @@ -1797,7 +1803,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1815,7 +1821,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1835,15 +1841,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitcoin-io" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin_hashes" -version = "0.14.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ "bitcoin-io", "hex-conservative", @@ -1917,7 +1923,7 @@ dependencies = [ "boa_interner", "boa_macros", "boa_string", - "indexmap 2.12.1", + "indexmap 2.12.0", "num-bigint", "rustc-hash", ] @@ -1947,9 +1953,9 @@ dependencies = [ "futures-channel", "futures-concurrency", "futures-lite", - "hashbrown 0.16.1", + "hashbrown 0.16.0", "icu_normalizer", - "indexmap 2.12.1", + "indexmap 2.12.0", "intrusive-collections", "itertools 0.14.0", "num-bigint", @@ -1982,7 +1988,7 @@ checksum = "f1179f690cbfcbe5364cceee5f1cb577265bb6f07b0be6f210aabe270adcf9da" dependencies = [ "boa_macros", "boa_string", - "hashbrown 0.16.1", + "hashbrown 0.16.0", "thin-vec", ] @@ -1994,8 +2000,8 @@ checksum = "9626505d33dc63d349662437297df1d3afd9d5fc4a2b3ad34e5e1ce879a78848" dependencies = [ "boa_gc", "boa_macros", - "hashbrown 0.16.1", - "indexmap 2.12.1", + "hashbrown 0.16.0", + "indexmap 2.12.0", "once_cell", "phf", "rustc-hash", @@ -2012,7 +2018,7 @@ dependencies = [ "cow-utils", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "synstructure", ] @@ -2050,9 +2056,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" dependencies = [ "borsh-derive", "cfg_aliases", @@ -2060,22 +2066,22 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.6.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "boyer-moore-magiclen" -version = "0.2.22" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7441b4796eb8a7107d4cd99d829810be75f5573e1081c37faa0e8094169ea0d6" +checksum = "95e6233f2d926b5b123caf9d58e3885885255567fbe7776a7fdcae2a4d7241c4" dependencies = [ "debug-helper", ] @@ -2146,7 +2152,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2377,7 +2383,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2480,9 +2486,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.33" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad" +checksum = "680dc087785c5230f8e8843e2e57ac7c1c90488b6a91b88caa265410568f441b" dependencies = [ "brotli", "compression-core", @@ -2494,9 +2500,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.31" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "3a9b614a5787ef0c8802a55766480563cb3a93b435898c422ed2a359cf811582" [[package]] name = "concat-kdf" @@ -2631,9 +2637,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -2832,7 +2838,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2866,7 +2872,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2881,7 +2887,7 @@ dependencies = [ "quote", "serde", "strsim", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2892,7 +2898,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2903,7 +2909,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2956,7 +2962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3015,7 +3021,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3026,7 +3032,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3047,7 +3053,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3057,29 +3063,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "derive_more" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.111", + "syn 2.0.110", "unicode-xid", ] @@ -3199,7 +3205,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3246,7 +3252,7 @@ checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3298,7 +3304,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3359,7 +3365,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3379,7 +3385,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3399,7 +3405,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3475,7 +3481,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3773,7 +3779,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3982,7 +3988,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.12.1", + "indexmap 2.12.0", "slab", "tokio", "tokio-util", @@ -4040,15 +4046,14 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" dependencies = [ "allocator-api2", "equivalent", "foldhash 0.2.0", "serde", - "serde_core", ] [[package]] @@ -4114,13 +4119,19 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" dependencies = [ "arrayvec", ] +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hickory-proto" version = "0.25.2" @@ -4189,11 +4200,12 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", + "fnv", "itoa", ] @@ -4367,9 +4379,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "base64 0.22.1", "bytes", @@ -4556,7 +4568,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -4597,13 +4609,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.16.0", "serde", "serde_core", ] @@ -4649,15 +4661,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.10" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" +checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" dependencies = [ "darling 0.20.11", "indoc", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -4796,9 +4808,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", @@ -4908,7 +4920,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5053,9 +5065,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.178" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libgit2-sys" @@ -5190,9 +5202,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loom" @@ -5231,7 +5243,7 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.16.0", ] [[package]] @@ -5282,7 +5294,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5348,7 +5360,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5358,7 +5370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd7399781913e5393588a8d8c6a2867bf85fb38eaf2502fdce465aad2dc6f034" dependencies = [ "base64 0.22.1", - "indexmap 2.12.1", + "indexmap 2.12.0", "metrics", "metrics-util", "quanta", @@ -5446,9 +5458,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", "log", @@ -5721,7 +5733,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5772,9 +5784,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "op-alloy-consensus" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726da827358a547be9f1e37c2a756b9e3729cb0350f43408164794b370cad8ae" +checksum = "e82f4f768ba39e52a4efe1b8f3425c04ab0d0e6f90c003fe97e5444cd963405e" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5798,9 +5810,9 @@ checksum = "a79f352fc3893dcd670172e615afef993a41798a1d3fc0db88a3e60ef2e70ecc" [[package]] name = "op-alloy-network" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63f27e65be273ec8fcb0b6af0fd850b550979465ab93423705ceb3dfddbd2ab" +checksum = "f2607d0d985f848f98fa79068d11c612f8476dba7deb7498881794bf51b3cfb5" dependencies = [ "alloy-consensus", "alloy-network", @@ -5814,9 +5826,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-jsonrpsee" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef9114426b16172254555aad34a8ea96c01895e40da92f5d12ea680a1baeaa7" +checksum = "6911db73a4bf59bf8a963dec153ada1057fa426fdc35e0b35fe82657af3501a3" dependencies = [ "alloy-primitives", "jsonrpsee", @@ -5824,9 +5836,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562dd4462562c41f9fdc4d860858c40e14a25df7f983ae82047f15f08fce4d19" +checksum = "890b51c3a619c263d52ee5a945dce173a4052d017f93bf5698613b21cbe0d237" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5843,9 +5855,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f24b8cb66e4b33e6c9e508bf46b8ecafc92eadd0b93fedd306c0accb477657" +checksum = "c92f9dd709b3a769b7604d4d2257846b6de3d3f60e5163982cc4e90c0d0b6f95" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5902,7 +5914,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6058,7 +6070,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6133,9 +6145,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.4" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" dependencies = [ "memchr", "ucd-trie", @@ -6182,7 +6194,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6211,7 +6223,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6379,7 +6391,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6474,7 +6486,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6485,7 +6497,7 @@ checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6508,7 +6520,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6812,7 +6824,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6850,7 +6862,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.16.0", "memchr", ] @@ -7177,7 +7189,7 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -10333,7 +10345,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.111", + "syn 2.0.110", "unicode-ident", ] @@ -10481,9 +10493,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "web-time", "zeroize", @@ -10796,7 +10808,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -10805,7 +10817,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.12.0", "itoa", "memchr", "ryu", @@ -10846,15 +10858,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.1", + "indexmap 2.12.0", "schemars 0.9.0", "schemars 1.1.0", "serde_core", @@ -10865,14 +10877,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -10974,9 +10986,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.7" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -11177,7 +11189,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11189,7 +11201,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11211,9 +11223,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.111" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", @@ -11229,7 +11241,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11249,7 +11261,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11359,7 +11371,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11370,7 +11382,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11504,12 +11516,11 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tips-core" version = "0.1.0" -source = "git+https://github.com/base/tips?rev=b1dfde6a5c8f603f19f9309ca6dc015bad260b44#b1dfde6a5c8f603f19f9309ca6dc015bad260b44" +source = "git+https://github.com/base/tips?rev=c08eaa4fe10c26de8911609b41ddab4918698325#c08eaa4fe10c26de8911609b41ddab4918698325" dependencies = [ "alloy-consensus", "alloy-primitives", "alloy-provider", - "alloy-rpc-types", "alloy-serde", "op-alloy-consensus", "op-alloy-flz", @@ -11544,7 +11555,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11659,7 +11670,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.12.0", "serde", "serde_spanned", "toml_datetime 0.6.11", @@ -11673,7 +11684,7 @@ version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.12.0", "toml_datetime 0.7.3", "toml_parser", "winnow", @@ -11740,7 +11751,7 @@ dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.12.1", + "indexmap 2.12.0", "pin-project-lite", "slab", "sync_wrapper", @@ -11753,9 +11764,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.7" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "async-compression", "base64 0.22.1", @@ -11796,9 +11807,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -11808,12 +11819,12 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror 2.0.17", + "thiserror 1.0.69", "time", "tracing-subscriber 0.3.22", ] @@ -11826,14 +11837,14 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -11851,9 +11862,9 @@ dependencies = [ [[package]] name = "tracing-journald" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3a81ed245bfb62592b1e2bc153e77656d94ee6a0497683a65a12ccaf2438d0" +checksum = "fc0b4143302cf1022dac868d521e36e8b27691f72c84b3311750d5188ebba657" dependencies = [ "libc", "tracing-core", @@ -11964,7 +11975,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12174,13 +12185,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.19.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.4", "js-sys", - "serde_core", + "serde", "wasm-bindgen", ] @@ -12251,7 +12262,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12299,9 +12310,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", @@ -12312,9 +12323,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -12325,9 +12336,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12335,22 +12346,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] @@ -12384,9 +12395,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -12596,7 +12607,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12607,7 +12618,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12618,7 +12629,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12629,7 +12640,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -13026,9 +13037,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -13130,28 +13141,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -13171,7 +13182,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "synstructure", ] @@ -13192,7 +13203,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -13226,7 +13237,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6a860dca..17a30a48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,8 @@ base-reth-test-utils = { path = "crates/test-utils" } base-reth-flashblocks = { path = "crates/flashblocks" } # base/tips -tips-core = { git = "https://github.com/base/tips", rev = "b1dfde6a5c8f603f19f9309ca6dc015bad260b44" } +# Note: default-features = false avoids version conflicts with reth's alloy/op-alloy dependencies +tips-core = { git = "https://github.com/base/tips", rev = "c08eaa4fe10c26de8911609b41ddab4918698325", default-features = false } # reth reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } @@ -91,10 +92,12 @@ reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", features = [ "op", ] } +reth-trie-common = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } # revm revm = { version = "31.0.2", default-features = false } revm-bytecode = { version = "7.1.1", default-features = false } +revm-database = { version = "9.0.6", default-features = false } # alloy alloy-trie = "0.9.1" diff --git a/crates/flashblocks/Cargo.toml b/crates/flashblocks/Cargo.toml index b649312b..c8b66356 100644 --- a/crates/flashblocks/Cargo.toml +++ b/crates/flashblocks/Cargo.toml @@ -26,6 +26,9 @@ reth-optimism-evm.workspace = true reth-optimism-chainspec.workspace = true reth-optimism-primitives.workspace = true +# revm +revm-database.workspace = true + # alloy alloy-eips.workspace = true alloy-provider.workspace = true @@ -57,6 +60,7 @@ metrics-derive.workspace = true [dev-dependencies] rand.workspace = true +revm.workspace = true reth-db.workspace = true once_cell.workspace = true reth-provider.workspace = true diff --git a/crates/flashblocks/src/metrics.rs b/crates/flashblocks/src/metrics.rs index 3cdaf96f..3c2824ec 100644 --- a/crates/flashblocks/src/metrics.rs +++ b/crates/flashblocks/src/metrics.rs @@ -56,4 +56,12 @@ pub struct Metrics { /// Total number of WebSocket reconnection attempts. #[metric(describe = "Total number of WebSocket reconnection attempts")] pub reconnect_attempts: Counter, + + /// Time taken to clone bundle state. + #[metric(describe = "Time taken to clone bundle state")] + pub bundle_state_clone_duration: Histogram, + + /// Size of bundle state being cloned (number of accounts). + #[metric(describe = "Size of bundle state being cloned (number of accounts)")] + pub bundle_state_clone_size: Histogram, } diff --git a/crates/flashblocks/src/pending_blocks.rs b/crates/flashblocks/src/pending_blocks.rs index eaf31ed5..e5b84ad9 100644 --- a/crates/flashblocks/src/pending_blocks.rs +++ b/crates/flashblocks/src/pending_blocks.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{sync::Arc, time::Instant}; use alloy_consensus::{Header, Sealed}; use alloy_eips::BlockNumberOrTag; @@ -14,11 +14,14 @@ use base_flashtypes::Flashblock; use eyre::eyre; use op_alloy_network::Optimism; use op_alloy_rpc_types::{OpTransactionReceipt, Transaction}; -use reth::revm::{db::Cache, state::EvmState}; +use reth::revm::{ + db::{BundleState, Cache}, + state::EvmState, +}; use reth_rpc_convert::RpcTransaction; use reth_rpc_eth_api::{RpcBlock, RpcReceipt}; -use crate::PendingBlocksAPI; +use crate::{Metrics, PendingBlocksAPI}; /// Builder for [`PendingBlocks`]. #[derive(Debug)] @@ -36,6 +39,7 @@ pub struct PendingBlocksBuilder { state_overrides: Option, db_cache: Cache, + bundle_state: BundleState, } impl PendingBlocksBuilder { @@ -52,6 +56,7 @@ impl PendingBlocksBuilder { transaction_senders: HashMap::new(), state_overrides: None, db_cache: Cache::default(), + bundle_state: BundleState::default(), } } @@ -122,6 +127,12 @@ impl PendingBlocksBuilder { self } + #[inline] + pub(crate) fn with_bundle_state(&mut self, bundle_state: BundleState) -> &Self { + self.bundle_state = bundle_state; + self + } + pub(crate) fn build(self) -> eyre::Result { if self.headers.is_empty() { return Err(eyre!("missing headers")); @@ -143,6 +154,7 @@ impl PendingBlocksBuilder { transaction_senders: self.transaction_senders, state_overrides: self.state_overrides, db_cache: self.db_cache, + bundle_state: self.bundle_state, }) } } @@ -163,6 +175,7 @@ pub struct PendingBlocks { state_overrides: Option, db_cache: Cache, + bundle_state: BundleState, } impl PendingBlocks { @@ -211,6 +224,22 @@ impl PendingBlocks { self.db_cache.clone() } + /// Returns a clone of the bundle state. + /// + /// NOTE: This clones the entire BundleState, which contains a HashMap of all touched + /// accounts and their storage slots. The cost scales with the number of accounts and + /// storage slots modified in the flashblock. Monitor `bundle_state_clone_duration` and + /// `bundle_state_clone_size` metrics to track if this becomes a bottleneck. + pub fn get_bundle_state(&self) -> BundleState { + let metrics = Metrics::default(); + let size = self.bundle_state.state.len(); + let start = Instant::now(); + let cloned = self.bundle_state.clone(); + metrics.bundle_state_clone_duration.record(start.elapsed()); + metrics.bundle_state_clone_size.record(size as f64); + cloned + } + /// Returns all transactions for a specific block number. pub fn get_transactions_for_block(&self, block_number: BlockNumber) -> Vec { self.transactions diff --git a/crates/flashblocks/src/processor.rs b/crates/flashblocks/src/processor.rs index 2db3bf0b..1701c573 100644 --- a/crates/flashblocks/src/processor.rs +++ b/crates/flashblocks/src/processor.rs @@ -36,6 +36,7 @@ use reth_optimism_primitives::{DepositReceipt, OpBlock, OpPrimitives}; use reth_optimism_rpc::OpReceiptBuilder; use reth_primitives::RecoveredBlock; use reth_rpc_convert::transaction::ConvertReceiptInput; +use revm_database::states::bundle_state::BundleRetention; use tokio::sync::{Mutex, broadcast::Sender, mpsc::UnboundedReceiver}; use tracing::{debug, error, info, warn}; @@ -293,12 +294,26 @@ where let state_provider = self.client.state_by_block_number_or_tag(BlockNumberOrTag::Number(canonical_block))?; let state_provider_db = StateProviderDatabase::new(state_provider); - let state = State::builder().with_database(state_provider_db).with_bundle_update().build(); let mut pending_blocks_builder = PendingBlocksBuilder::new(); + // Cache reads across flashblocks, accumulating caches from previous + // pending blocks if available + let cache_db = match &prev_pending_blocks { + Some(pending_blocks) => { + CacheDB { cache: pending_blocks.get_db_cache(), db: state_provider_db } + } + None => CacheDB::new(state_provider_db), + }; + + // Track state changes across flashblocks, accumulating bundle state + // from previous pending blocks if available let mut db = match &prev_pending_blocks { - Some(pending_blocks) => CacheDB { cache: pending_blocks.get_db_cache(), db: state }, - None => CacheDB::new(state), + Some(pending_blocks) => State::builder() + .with_database(cache_db) + .with_bundle_update() + .with_bundle_prestate(pending_blocks.get_bundle_state()) + .build(), + None => State::builder().with_database(cache_db).with_bundle_update().build(), }; let mut state_overrides = @@ -537,7 +552,9 @@ where last_block_header = block.header.clone(); } - pending_blocks_builder.with_db_cache(db.cache); + db.merge_transitions(BundleRetention::Reverts); + pending_blocks_builder.with_bundle_state(db.take_bundle()); + pending_blocks_builder.with_db_cache(db.database.cache); pending_blocks_builder.with_state_overrides(state_overrides); Ok(Some(Arc::new(pending_blocks_builder.build()?))) } diff --git a/crates/flashblocks/tests/layering.rs b/crates/flashblocks/tests/layering.rs new file mode 100644 index 00000000..049198d3 --- /dev/null +++ b/crates/flashblocks/tests/layering.rs @@ -0,0 +1,410 @@ +// ============================================================================= +// Database Layering Tests +// +// These tests verify that the three-layer state architecture is correct: +// 1. StateProviderDatabase (canonical block base state) +// 2. CacheDB (applies flashblock pending read cache) +// 3. State wrapper (applies bundle_prestate for pending writes + tracks new changes) +// +// The correct layering is State>. Writes go +// through State first (properly tracked for bundle/state root calculation), +// and reads fall through to CacheDB (flashblock read cache) then to +// StateProviderDatabase (canonical state). +// +// The WRONG layering would be CacheDB>. CacheDB intercepts all +// writes into its internal cache and doesn't propagate them to State, so +// State's bundle tracking captures nothing. See the test +// layering_cachedb_wrapping_state_loses_writes for a demonstration. +// ============================================================================= + +use std::sync::Arc; + +use alloy_consensus::crypto::secp256k1::public_key_to_address; +use alloy_genesis::GenesisAccount; +use alloy_primitives::{Address, B256, U256}; +use eyre::Context; +use rand::{SeedableRng, rngs::StdRng}; +use reth::{ + api::NodeTypesWithDBAdapter, + revm::db::{AccountState, BundleState, Cache, CacheDB, DbAccount, State}, +}; +use reth_db::{ + ClientVersion, DatabaseEnv, init_db, + mdbx::{DatabaseArguments, KILOBYTE, MEGABYTE, MaxReadTransactionDuration}, + test_utils::{ERROR_DB_CREATION, TempDatabase, create_test_static_files_dir, tempdir_path}, +}; +use reth_optimism_chainspec::{BASE_MAINNET, OpChainSpec, OpChainSpecBuilder}; +use reth_optimism_node::OpNode; +use reth_primitives_traits::SealedHeader; +use reth_provider::{ + HeaderProvider, ProviderFactory, StateProviderFactory, + providers::{BlockchainProvider, StaticFileProvider}, +}; +use reth_testing_utils::generators::generate_keys; +use revm::{Database, primitives::KECCAK_EMPTY}; + +type NodeTypes = NodeTypesWithDBAdapter>>; + +#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)] +enum User { + Alice, + Bob, +} + +#[derive(Debug, Clone)] +struct TestHarness { + provider: BlockchainProvider, + header: SealedHeader, + #[allow(dead_code)] + chain_spec: Arc, + user_to_address: std::collections::HashMap, + #[allow(dead_code)] + user_to_private_key: std::collections::HashMap, +} + +impl TestHarness { + fn address(&self, u: User) -> Address { + self.user_to_address[&u] + } +} + +fn create_chain_spec( + seed: u64, +) -> ( + Arc, + std::collections::HashMap, + std::collections::HashMap, +) { + let keys = generate_keys(&mut StdRng::seed_from_u64(seed), 2); + + let mut addresses = std::collections::HashMap::new(); + let mut private_keys = std::collections::HashMap::new(); + + let alice_key = keys[0]; + let alice_address = public_key_to_address(alice_key.public_key()); + let alice_secret = B256::from(alice_key.secret_bytes()); + addresses.insert(User::Alice, alice_address); + private_keys.insert(User::Alice, alice_secret); + + let bob_key = keys[1]; + let bob_address = public_key_to_address(bob_key.public_key()); + let bob_secret = B256::from(bob_key.secret_bytes()); + addresses.insert(User::Bob, bob_address); + private_keys.insert(User::Bob, bob_secret); + + let genesis = BASE_MAINNET + .genesis + .clone() + .extend_accounts(vec![ + (alice_address, GenesisAccount::default().with_balance(U256::from(1_000_000_000_u64))), + (bob_address, GenesisAccount::default().with_balance(U256::from(1_000_000_000_u64))), + ]) + .with_gas_limit(100_000_000); + + let spec = + Arc::new(OpChainSpecBuilder::base_mainnet().genesis(genesis).isthmus_activated().build()); + + (spec, addresses, private_keys) +} + +fn create_provider_factory( + chain_spec: Arc, +) -> ProviderFactory>>> { + let (static_dir, _) = create_test_static_files_dir(); + let db = create_test_db(); + ProviderFactory::new( + db, + chain_spec, + StaticFileProvider::read_write(static_dir.keep()).expect("static file provider"), + ) +} + +fn create_test_db() -> Arc> { + let path = tempdir_path(); + let emsg = format!("{ERROR_DB_CREATION}: {path:?}"); + + let db = init_db( + &path, + DatabaseArguments::new(ClientVersion::default()) + .with_max_read_transaction_duration(Some(MaxReadTransactionDuration::Unbounded)) + .with_geometry_max_size(Some(4 * MEGABYTE)) + .with_growth_step(Some(4 * KILOBYTE)), + ) + .expect(&emsg); + + Arc::new(TempDatabase::new(db, path)) +} + +fn setup_harness() -> eyre::Result { + let (chain_spec, user_to_address, user_to_private_key) = create_chain_spec(1337); + let factory = create_provider_factory(chain_spec.clone()); + + reth_db_common::init::init_genesis(&factory).context("initializing genesis state")?; + + let provider = BlockchainProvider::new(factory.clone()).context("creating provider")?; + let header = provider + .sealed_header(0) + .context("fetching genesis header")? + .expect("genesis header exists"); + + Ok(TestHarness { provider, header, chain_spec, user_to_address, user_to_private_key }) +} + +/// Demonstrates that State alone cannot see pending state. +/// +/// Without CacheDB or bundle_prestate, State can only see canonical block state. +#[test] +fn layering_old_state_only_cannot_see_pending_state() -> eyre::Result<()> { + let harness = setup_harness()?; + let alice_address = harness.address(User::Alice); + + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + // OLD implementation: just State wrapping StateProviderDatabase directly + // No CacheDB, no bundle_prestate - cannot see any pending flashblock state + let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); + let mut db = State::builder().with_database(state_db).with_bundle_update().build(); + + // Read through State - can only see canonical state (nonce 0) + let account = db.basic(alice_address)?.expect("account should exist"); + + // Old implementation sees canonical nonce (0), not any pending state + assert_eq!(account.nonce, 0, "Old State-only layering can only see canonical state"); + + Ok(()) +} + +/// Demonstrates that CacheDB> is the WRONG layering order. +/// +/// CacheDB is a read-through cache. When the EVM writes state changes, those +/// writes go into CacheDB's internal cache and are NOT propagated to the +/// underlying State. As a result, State's bundle tracking captures nothing, +/// and take_bundle() returns an empty bundle - breaking state root calculation. +/// +/// The correct layering is State> where State wraps CacheDB, +/// so all writes go through State first and are properly tracked. +#[test] +fn layering_cachedb_wrapping_state_loses_writes() -> eyre::Result<()> { + use revm::DatabaseCommit; + + let harness = setup_harness()?; + let alice_address = harness.address(User::Alice); + let bob_address = harness.address(User::Bob); + + // ========================================================================= + // WRONG layering: CacheDB> + // ========================================================================= + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); + let state = State::builder().with_database(state_db).with_bundle_update().build(); + let mut wrong_db = CacheDB::new(state); + + // Simulate a write: modify Alice's account through the CacheDB + let mut alice_account = wrong_db.basic(alice_address)?.expect("alice should exist"); + alice_account.nonce = 999; + alice_account.balance = U256::from(12345u64); + + // Commit the change through CacheDB + let mut state_changes = revm::state::EvmState::default(); + state_changes.insert( + alice_address, + revm::state::Account { + info: alice_account.clone(), + storage: Default::default(), + status: revm::state::AccountStatus::Touched, + transaction_id: 0, + }, + ); + wrong_db.commit(state_changes); + + // The write went into CacheDB's cache - verify we can read it back + let alice_from_cache = wrong_db.basic(alice_address)?.expect("alice should exist"); + assert_eq!(alice_from_cache.nonce, 999, "CacheDB should have the written nonce"); + + // BUT: The underlying State captured NOTHING! + // CacheDB doesn't propagate writes to its underlying database. + wrong_db.db.merge_transitions(revm_database::states::bundle_state::BundleRetention::Reverts); + let wrong_bundle = wrong_db.db.take_bundle(); + + assert!( + wrong_bundle.state.is_empty(), + "WRONG layering: State's bundle should be EMPTY because CacheDB intercepted all writes. \ + Got {} accounts in bundle.", + wrong_bundle.state.len() + ); + + // ========================================================================= + // CORRECT layering: State> + // ========================================================================= + let state_provider2 = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let state_db2 = reth::revm::database::StateProviderDatabase::new(state_provider2); + let cache_db = CacheDB::new(state_db2); + let mut correct_db = State::builder().with_database(cache_db).with_bundle_update().build(); + + // Simulate the same write through State + let mut bob_account = correct_db.basic(bob_address)?.expect("bob should exist"); + bob_account.nonce = 888; + bob_account.balance = U256::from(54321u64); + + // Commit through State + let mut state_changes2 = revm::state::EvmState::default(); + state_changes2.insert( + bob_address, + revm::state::Account { + info: bob_account.clone(), + storage: Default::default(), + status: revm::state::AccountStatus::Touched, + transaction_id: 0, + }, + ); + correct_db.commit(state_changes2); + + // State properly captures the write + correct_db.merge_transitions(revm_database::states::bundle_state::BundleRetention::Reverts); + let correct_bundle = correct_db.take_bundle(); + + assert!( + !correct_bundle.state.is_empty(), + "CORRECT layering: State's bundle should contain the written account" + ); + assert!( + correct_bundle.state.contains_key(&bob_address), + "Bundle should contain Bob's account changes" + ); + + Ok(()) +} + +/// Verifies that CacheDB layer is required for pending balance visibility. +/// +/// This test demonstrates that without the CacheDB layer, pending balance +/// changes from flashblocks would not be visible to bundle execution. +#[test] +fn layering_cachedb_makes_pending_balance_visible() -> eyre::Result<()> { + let harness = setup_harness()?; + + // Get the canonical balance for Alice from the state provider + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let alice_address = harness.address(User::Alice); + let canonical_balance = state_provider.account_balance(&alice_address)?.unwrap_or(U256::ZERO); + + // Create a cache with a modified balance (simulating a pending flashblock) + let pending_balance = canonical_balance + U256::from(999_999_u64); + let mut cache = Cache::default(); + cache.accounts.insert( + alice_address, + DbAccount { + info: revm::state::AccountInfo { + balance: pending_balance, + nonce: 0, + code_hash: KECCAK_EMPTY, + code: None, + }, + account_state: AccountState::Touched, + storage: Default::default(), + }, + ); + + // Wrap with CacheDB to apply the pending cache + let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); + let mut cache_db = CacheDB { cache, db: state_db }; + + // Read the balance through CacheDB - should see pending value + let account = cache_db.basic(alice_address)?.expect("account should exist"); + assert_eq!( + account.balance, pending_balance, + "CacheDB should return pending balance from cache" + ); + + // Verify the canonical state still has the original balance + let state_provider2 = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + let canonical_balance2 = state_provider2.account_balance(&alice_address)?.unwrap_or(U256::ZERO); + assert_eq!(canonical_balance, canonical_balance2, "Canonical state should be unchanged"); + + Ok(()) +} + +/// Verifies that bundle_prestate is required for pending state changes visibility. +/// +/// This test demonstrates that without with_bundle_prestate(), the State wrapper +/// would start with empty state and not see pending flashblock state changes. +#[test] +fn layering_bundle_prestate_makes_pending_nonce_visible() -> eyre::Result<()> { + let harness = setup_harness()?; + let alice_address = harness.address(User::Alice); + + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + // Create a BundleState with a modified nonce (simulating pending flashblock writes) + let pending_nonce = 42u64; + let bundle_state = BundleState::new( + [( + alice_address, + Some(revm::state::AccountInfo { + balance: U256::from(1_000_000_000u64), + nonce: 0, // original + code_hash: KECCAK_EMPTY, + code: None, + }), + Some(revm::state::AccountInfo { + balance: U256::from(1_000_000_000u64), + nonce: pending_nonce, // pending + code_hash: KECCAK_EMPTY, + code: None, + }), + Default::default(), + )], + Vec::>, Vec<(U256, U256)>)>>::new(), + Vec::<(B256, revm::bytecode::Bytecode)>::new(), + ); + + // Create the three-layer stack WITH bundle_prestate + let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); + let cache_db = CacheDB::new(state_db); + let mut db_with_prestate = State::builder() + .with_database(cache_db) + .with_bundle_update() + .with_bundle_prestate(bundle_state.clone()) + .build(); + + // Read through the State - should see pending nonce from bundle_prestate + let account = db_with_prestate.basic(alice_address)?.expect("account should exist"); + assert_eq!(account.nonce, pending_nonce, "State with bundle_prestate should see pending nonce"); + + // Now create WITHOUT bundle_prestate to show the difference + let state_provider2 = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + let state_db2 = reth::revm::database::StateProviderDatabase::new(state_provider2); + let cache_db2 = CacheDB::new(state_db2); + let mut db_without_prestate = + State::builder().with_database(cache_db2).with_bundle_update().build(); + + // Read through State without prestate - should see canonical nonce (0) + let account2 = db_without_prestate.basic(alice_address)?.expect("account should exist"); + assert_eq!(account2.nonce, 0, "State without bundle_prestate should see canonical nonce"); + + Ok(()) +} diff --git a/crates/flashblocks/tests/state.rs b/crates/flashblocks/tests/state.rs index 75ef5d82..d4063c9d 100644 --- a/crates/flashblocks/tests/state.rs +++ b/crates/flashblocks/tests/state.rs @@ -837,6 +837,131 @@ async fn test_duplicate_flashblock_ignored() { assert_eq!(block, block_two); } +/// Verifies that eth_call targeting pending block sees flashblock state changes. +/// +/// This test catches database layering bugs where pending state from flashblocks +/// isn't visible to RPC callers. After a flashblock transfers ETH to Bob, an +/// eth_call simulating a transfer FROM Bob should succeed because Bob now has +/// more funds from the flashblock. +#[tokio::test] +async fn test_eth_call_sees_flashblock_state_changes() { + use alloy_eips::BlockNumberOrTag; + use alloy_provider::Provider; + use alloy_rpc_types_eth::TransactionInput; + use op_alloy_rpc_types::OpTransactionRequest; + + let test = TestHarness::new().await; + let provider = test.node.provider(); + + let bob_address = test.address(User::Bob); + let charlie_address = test.address(User::Charlie); + + // Get Bob's canonical balance to calculate a transfer amount that exceeds it + let canonical_balance = provider.get_balance(bob_address).await.unwrap(); + + // Send base flashblock + test.send_flashblock(FlashblockBuilder::new_base(&test).build()).await; + + // Flashblock 1: Alice sends a large amount to Bob + let transfer_to_bob = 1_000_000_000_000_000_000u128; // 1 ETH + let tx = + test.build_transaction_to_send_eth_with_nonce(User::Alice, User::Bob, transfer_to_bob, 0); + test.send_flashblock(FlashblockBuilder::new(&test, 1).with_transactions(vec![tx]).build()) + .await; + + // Verify via state overrides that Bob received the funds + let overrides = test + .flashblocks + .get_pending_blocks() + .get_state_overrides() + .expect("state overrides should exist after flashblock execution"); + let bob_override = overrides.get(&bob_address).expect("Bob should have a state override"); + let bob_pending_balance = bob_override.balance.expect("Bob's balance override should be set"); + assert_eq!( + bob_pending_balance, + canonical_balance + U256::from(transfer_to_bob), + "State override should show Bob's increased balance" + ); + + // Now the key test: eth_call from Bob should see this pending balance. + // Try to transfer more than Bob's canonical balance (but less than pending). + // This would fail if eth_call can't see the pending state. + let transfer_amount = canonical_balance + U256::from(100_000u64); + let call_request = OpTransactionRequest::default() + .from(bob_address) + .to(charlie_address) + .value(transfer_amount) + .gas_limit(21_000) + .input(TransactionInput::default()); + + let result = provider.call(call_request).block(BlockNumberOrTag::Pending.into()).await; + assert!( + result.is_ok(), + "eth_call from Bob should succeed because pending state shows increased balance. \ + If this fails, eth_call may not be seeing flashblock state changes. Error: {:?}", + result.err() + ); +} + +/// Verifies that transactions in flashblock N+1 can see state changes from flashblock N. +/// +/// This test catches database layering bugs where writes from earlier flashblocks +/// aren't visible to later flashblock execution. The key is that flashblock 2's +/// transaction uses nonce=1, which only succeeds if the execution layer sees +/// flashblock 1's transaction (which used nonce=0). +#[tokio::test] +async fn test_sequential_nonces_across_flashblocks() { + let test = TestHarness::new().await; + + // Send base flashblock + test.send_flashblock(FlashblockBuilder::new_base(&test).build()).await; + + // Flashblock 1: Alice sends to Bob with nonce 0 + let tx_nonce_0 = test.build_transaction_to_send_eth_with_nonce(User::Alice, User::Bob, 1000, 0); + test.send_flashblock( + FlashblockBuilder::new(&test, 1).with_transactions(vec![tx_nonce_0]).build(), + ) + .await; + + // Verify flashblock 1 was processed - Alice's pending nonce should now be 1 + let alice_state = test.account_state(User::Alice); + assert_eq!(alice_state.nonce, 1, "After flashblock 1, Alice's pending nonce should be 1"); + + // Flashblock 2: Alice sends to Charlie with nonce 1 + // This will FAIL if the execution layer can't see flashblock 1's state change + let tx_nonce_1 = + test.build_transaction_to_send_eth_with_nonce(User::Alice, User::Charlie, 2000, 1); + test.send_flashblock( + FlashblockBuilder::new(&test, 2).with_transactions(vec![tx_nonce_1]).build(), + ) + .await; + + // Verify flashblock 2 was processed - Alice's pending nonce should now be 2 + let alice_state_after = test.account_state(User::Alice); + assert_eq!( + alice_state_after.nonce, 2, + "After flashblock 2, Alice's pending nonce should be 2. \ + If this fails, the database layering may be preventing flashblock 2 \ + from seeing flashblock 1's state changes." + ); + + // Also verify Bob and Charlie received their funds + let overrides = test + .flashblocks + .get_pending_blocks() + .get_state_overrides() + .expect("state overrides should exist"); + + assert!( + overrides.get(&test.address(User::Bob)).is_some(), + "Bob should have received funds from flashblock 1" + ); + assert!( + overrides.get(&test.address(User::Charlie)).is_some(), + "Charlie should have received funds from flashblock 2" + ); +} + #[tokio::test] async fn test_progress_canonical_blocks_without_flashblocks() { let mut test = TestHarness::new().await; diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index 390fdf1a..8a4aa655 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -27,6 +27,7 @@ reth-optimism-chainspec.workspace = true reth-transaction-pool = { workspace = true, features = ["test-utils"] } reth-rpc.workspace = true reth-rpc-eth-api.workspace = true +reth-trie-common.workspace = true # alloy alloy-primitives.workspace = true @@ -43,6 +44,9 @@ op-alloy-network.workspace = true tokio.workspace = true tokio-stream.workspace = true +# revm +revm-database.workspace = true + # rpc jsonrpsee.workspace = true jsonrpsee-types.workspace = true @@ -53,6 +57,7 @@ eyre.workspace = true serde.workspace = true metrics.workspace = true metrics-derive.workspace = true +arc-swap.workspace = true [dev-dependencies] base-flashtypes.workspace = true @@ -80,6 +85,7 @@ alloy-provider.workspace = true alloy-sol-macro = { workspace = true, features = ["json"] } alloy-sol-types.workspace = true alloy-contract.workspace = true +hex-literal = "0.4" [package.metadata.cargo-udeps.ignore] normal = ["reth-optimism-cli"] diff --git a/crates/rpc/src/base/flashblock_trie_cache.rs b/crates/rpc/src/base/flashblock_trie_cache.rs new file mode 100644 index 00000000..13134f86 --- /dev/null +++ b/crates/rpc/src/base/flashblock_trie_cache.rs @@ -0,0 +1,95 @@ +use std::sync::Arc; + +use alloy_primitives::B256; +use arc_swap::ArcSwap; +use eyre::Result as EyreResult; +use reth_provider::StateProvider; +use reth_trie_common::{HashedPostState, updates::TrieUpdates}; + +use crate::FlashblocksState; + +/// Trie nodes and hashed state from computing a flashblock state root. +/// +/// When metering bundles, we want each state root calculation to measure only +/// the bundle's incremental I/O, not I/O from previous flashblocks. By caching +/// the flashblock trie once and reusing it for all bundle simulations, we ensure +/// each bundle's state root time reflects only its own I/O cost. +#[derive(Debug, Clone)] +pub struct FlashblockTrieData { + /// The trie updates from computing the state root. + pub trie_updates: TrieUpdates, + /// The hashed post state used for state root computation. + pub hashed_state: HashedPostState, +} + +/// Internal cache entry for a single flashblock. +#[derive(Debug, Clone)] +struct CachedFlashblockTrie { + block_hash: B256, + flashblock_index: u64, + trie_data: FlashblockTrieData, +} + +/// Thread-safe single-entry cache for the latest flashblock's trie nodes. +/// +/// This cache stores the intermediate trie nodes computed when calculating +/// the latest flashblock's state root. Subsequent bundle metering operations +/// on the same flashblock can reuse these cached nodes instead of recalculating +/// them, significantly improving performance. +/// +/// **Important**: This cache holds only ONE flashblock's trie at a time. +/// When a new flashblock is cached, it replaces any previously cached flashblock. +#[derive(Debug, Clone)] +pub struct FlashblockTrieCache { + cache: Arc>>, +} + +impl FlashblockTrieCache { + /// Creates a new empty flashblock trie cache. + pub fn new() -> Self { + Self { cache: Arc::new(ArcSwap::from_pointee(None)) } + } + + /// Ensures the trie for the given flashblock is cached and returns it. + /// + /// If the cache already contains an entry for the provided `block_hash` and + /// `flashblock_index`, the cached data is returned immediately. Otherwise the trie is + /// recomputed, cached (replacing any previous entry), and returned. + pub fn ensure_cached( + &self, + block_hash: B256, + flashblock_index: u64, + flashblocks_state: &FlashblocksState, + canonical_state_provider: &dyn StateProvider, + ) -> EyreResult { + let cached_entry = self.cache.load(); + if let Some(cached) = cached_entry.as_ref() + && cached.block_hash == block_hash + && cached.flashblock_index == flashblock_index + { + return Ok(cached.trie_data.clone()); + } + + let hashed_state = + canonical_state_provider.hashed_post_state(&flashblocks_state.bundle_state); + let (_state_root, trie_updates) = + canonical_state_provider.state_root_with_updates(hashed_state.clone())?; + + let trie_data = FlashblockTrieData { trie_updates, hashed_state }; + + // Store the new entry, replacing any previous flashblock's cached trie + self.cache.store(Arc::new(Some(CachedFlashblockTrie { + block_hash, + flashblock_index, + trie_data: trie_data.clone(), + }))); + + Ok(trie_data) + } +} + +impl Default for FlashblockTrieCache { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index 577b8ee3..33c68047 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -3,43 +3,102 @@ use std::{sync::Arc, time::Instant}; use alloy_consensus::{BlockHeader, Transaction as _, transaction::SignerRecoverable}; use alloy_primitives::{B256, U256}; use eyre::{Result as EyreResult, eyre}; -use reth::revm::db::State; +use reth::revm::db::{BundleState, Cache, CacheDB, State}; use reth_evm::{ConfigureEvm, execute::BlockBuilder}; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; use reth_primitives_traits::SealedHeader; +use reth_trie_common::TrieInput; +use revm_database::states::bundle_state::BundleRetention; use tips_core::types::{BundleExtensions, BundleTxs, ParsedBundle}; -use crate::TransactionResult; +use crate::{FlashblockTrieData, TransactionResult}; + +/// State from pending flashblocks that is used as a base for metering +#[derive(Debug, Clone)] +pub struct FlashblocksState { + /// The cache of account and storage data + pub cache: Cache, + /// The accumulated bundle of state changes + pub bundle_state: BundleState, +} const BLOCK_TIME: u64 = 2; // 2 seconds per block +/// Output from metering a bundle of transactions +#[derive(Debug)] +pub struct MeterBundleOutput { + /// Transaction results with individual metrics + pub results: Vec, + /// Total gas used by all transactions + pub total_gas_used: u64, + /// Total gas fees paid by all transactions + pub total_gas_fees: U256, + /// Bundle hash + pub bundle_hash: B256, + /// Total time in microseconds (includes transaction execution and state root calculation) + pub total_time_us: u128, + /// State root calculation time in microseconds + pub state_root_time_us: u128, +} + /// Simulates and meters a bundle of transactions /// -/// Takes a state provider, chain spec, decoded transactions, block header, and bundle metadata, -/// and executes transactions in sequence to measure gas usage and execution time. +/// Takes a state provider, chain spec, parsed bundle, block header, and optional flashblocks state, +/// then executes transactions in sequence to measure gas usage and execution time. /// -/// Returns a tuple of: -/// - Vector of transaction results -/// - Total gas used -/// - Total gas fees paid -/// - Bundle hash -/// - Total execution time in microseconds +/// Returns [`MeterBundleOutput`] containing transaction results and aggregated metrics. pub fn meter_bundle( state_provider: SP, chain_spec: Arc, bundle: ParsedBundle, header: &SealedHeader, -) -> EyreResult<(Vec, u64, U256, B256, u128)> + flashblocks_state: Option, + cached_flashblock_trie: Option, +) -> EyreResult where SP: reth_provider::StateProvider, { // Get bundle hash let bundle_hash = bundle.bundle_hash(); + // Get flashblock trie data before starting timers. This ensures we only measure + // the bundle's incremental I/O cost, not I/O from previous flashblocks. + let flashblock_trie_data = cached_flashblock_trie + .map(Ok::<_, eyre::Report>) + .or_else(|| { + flashblocks_state.as_ref().map(|fb_state| { + let fb_hashed_state = state_provider.hashed_post_state(&fb_state.bundle_state); + let (_fb_state_root, fb_trie_updates) = + state_provider.state_root_with_updates(fb_hashed_state.clone())?; + Ok(crate::FlashblockTrieData { + trie_updates: fb_trie_updates, + hashed_state: fb_hashed_state, + }) + }) + }) + .transpose()?; + // Create state database let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); - let mut db = State::builder().with_database(state_db).with_bundle_update().build(); + + // Apply flashblocks read cache if available + let cache_db = if let Some(ref flashblocks) = flashblocks_state { + CacheDB { cache: flashblocks.cache.clone(), db: state_db } + } else { + CacheDB::new(state_db) + }; + + // Track bundle state changes. If metering using flashblocks state, include its bundle prestate. + let mut db = if let Some(flashblocks) = flashblocks_state.as_ref() { + State::builder() + .with_database(cache_db) + .with_bundle_update() + .with_bundle_prestate(flashblocks.bundle_state.clone()) + .build() + } else { + State::builder().with_database(cache_db).with_bundle_update().build() + }; // Set up next block attributes // Use bundle.min_timestamp if provided, otherwise use header timestamp + BLOCK_TIME @@ -58,7 +117,7 @@ where let mut total_gas_used = 0u64; let mut total_gas_fees = U256::ZERO; - let execution_start = Instant::now(); + let total_start = Instant::now(); { let evm_config = OpEvmConfig::optimism(chain_spec); let mut builder = evm_config.builder_for_next_block(&mut db, header, attributes)?; @@ -85,7 +144,7 @@ where results.push(TransactionResult { coinbase_diff: gas_fees, - eth_sent_to_coinbase: U256::from(0), + eth_sent_to_coinbase: U256::ZERO, from_address: from, gas_fees, gas_price: U256::from(gas_price), @@ -97,7 +156,36 @@ where }); } } - let total_execution_time = execution_start.elapsed().as_micros(); - Ok((results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time)) + // Calculate state root and measure its calculation time. The bundle already includes + // flashblocks state if it was provided via with_bundle_prestate. + db.merge_transitions(BundleRetention::Reverts); + let bundle_update = db.take_bundle(); + let state_provider = db.database.db.as_ref(); + + let state_root_start = Instant::now(); + let hashed_state = state_provider.hashed_post_state(&bundle_update); + + if let Some(fb_trie_data) = flashblock_trie_data { + // Prepend cached flashblock trie so state root calculation only performs I/O + // for this bundle's changes, not for previous flashblocks. + let mut trie_input = TrieInput::from_state(hashed_state); + trie_input.prepend_cached(fb_trie_data.trie_updates, fb_trie_data.hashed_state); + let _ = state_provider.state_root_from_nodes_with_updates(trie_input)?; + } else { + // No flashblocks, just calculate bundle state root + let _ = state_provider.state_root_with_updates(hashed_state)?; + } + + let state_root_time_us = state_root_start.elapsed().as_micros(); + let total_time_us = total_start.elapsed().as_micros(); + + Ok(MeterBundleOutput { + results, + total_gas_used, + total_gas_fees, + bundle_hash, + total_time_us, + state_root_time_us, + }) } diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index 9a99aa32..efd7b8e6 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -1,36 +1,44 @@ -use alloy_consensus::Header; -use alloy_eips::BlockNumberOrTag; +use std::sync::Arc; + +use alloy_consensus::{Header, Sealed}; use alloy_primitives::U256; +use base_reth_flashblocks::{FlashblocksAPI, PendingBlocksAPI}; use jsonrpsee::core::{RpcResult, async_trait}; use reth::providers::BlockReaderIdExt; use reth_optimism_chainspec::OpChainSpec; +use reth_primitives_traits::SealedHeader; use reth_provider::{ChainSpecProvider, StateProviderFactory}; use tips_core::types::{Bundle, MeterBundleResponse, ParsedBundle}; use tracing::{error, info}; -use crate::{MeteringApiServer, meter_bundle}; +use crate::{FlashblockTrieCache, MeteringApiServer, meter_bundle}; -/// Implementation of the metering RPC API +/// Implementation of the metering RPC API. #[derive(Debug)] -pub struct MeteringApiImpl { +pub struct MeteringApiImpl { provider: Provider, + flashblocks_state: Arc, + /// Cache for the latest flashblock's trie, ensuring each bundle's state root + /// calculation only measures the bundle's incremental I/O. + trie_cache: FlashblockTrieCache, } -impl MeteringApiImpl +impl MeteringApiImpl where Provider: StateProviderFactory + ChainSpecProvider + BlockReaderIdExt
+ Clone, + FB: FlashblocksAPI, { /// Creates a new instance of MeteringApi - pub const fn new(provider: Provider) -> Self { - Self { provider } + pub fn new(provider: Provider, flashblocks_state: Arc) -> Self { + Self { provider, flashblocks_state, trie_cache: FlashblockTrieCache::new() } } } #[async_trait] -impl MeteringApiServer for MeteringApiImpl +impl MeteringApiServer for MeteringApiImpl where Provider: StateProviderFactory + ChainSpecProvider @@ -39,6 +47,7 @@ where + Send + Sync + 'static, + FB: FlashblocksAPI + Send + Sync + 'static, { async fn meter_bundle(&self, bundle: Bundle) -> RpcResult { info!( @@ -47,24 +56,56 @@ where "Starting bundle metering" ); - // Get the latest header - let header = self - .provider - .sealed_header_by_number_or_tag(BlockNumberOrTag::Latest) - .map_err(|e| { - jsonrpsee::types::ErrorObjectOwned::owned( - jsonrpsee::types::ErrorCode::InternalError.code(), - format!("Failed to get latest header: {}", e), - None::<()>, - ) - })? - .ok_or_else(|| { - jsonrpsee::types::ErrorObjectOwned::owned( - jsonrpsee::types::ErrorCode::InternalError.code(), - "Latest block not found".to_string(), - None::<()>, - ) - })?; + // Get pending flashblocks state + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + + // Get header and flashblock index from pending blocks + // If no pending blocks exist, fall back to latest canonical block + let (header, flashblock_index, canonical_block_number) = + if let Some(pb) = pending_blocks.as_ref() { + let latest_header: Sealed
= pb.latest_header(); + let flashblock_index = pb.latest_flashblock_index(); + let canonical_block_number = pb.canonical_block_number(); + + info!( + latest_block = latest_header.number, + canonical_block = %canonical_block_number, + flashblock_index = flashblock_index, + "Using latest flashblock state for metering" + ); + + // Convert Sealed
to SealedHeader + let sealed_header = + SealedHeader::new(latest_header.inner().clone(), latest_header.hash()); + (sealed_header, flashblock_index, canonical_block_number) + } else { + // No pending blocks, use latest canonical block + let canonical_block_number = pending_blocks.get_canonical_block_number(); + let header = self + .provider + .sealed_header_by_number_or_tag(canonical_block_number) + .map_err(|e| { + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + format!("Failed to get canonical block header: {}", e), + None::<()>, + ) + })? + .ok_or_else(|| { + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + "Canonical block not found".to_string(), + None::<()>, + ) + })?; + + info!( + canonical_block = header.number, + "No flashblocks available, using canonical block state for metering" + ); + + (header, 0, canonical_block_number) + }; let parsed_bundle = ParsedBundle::try_from(bundle).map_err(|e| { jsonrpsee::types::ErrorObjectOwned::owned( @@ -74,54 +115,93 @@ where ) })?; - // Get state provider for the block - let state_provider = self.provider.state_by_block_hash(header.hash()).map_err(|e| { - error!(error = %e, "Failed to get state provider"); + // Get state provider for the canonical block + let state_provider = + self.provider.state_by_block_number_or_tag(canonical_block_number).map_err(|e| { + error!(error = %e, "Failed to get state provider"); + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + format!("Failed to get state provider: {}", e), + None::<()>, + ) + })?; + + // If we have pending flashblocks, get the state to apply pending changes + let flashblocks_state = pending_blocks.as_ref().map(|pb| crate::FlashblocksState { + cache: pb.get_db_cache(), + bundle_state: pb.get_bundle_state(), + }); + + // Get the flashblock index if we have pending flashblocks + let state_flashblock_index = pending_blocks.as_ref().map(|pb| pb.latest_flashblock_index()); + + // Ensure the flashblock trie is cached for reuse across bundle simulations + let cached_trie = if let Some(ref fb_state) = flashblocks_state { + let fb_index = state_flashblock_index.unwrap(); + Some( + self.trie_cache + .ensure_cached(header.hash(), fb_index, fb_state, &*state_provider) + .map_err(|e| { + error!(error = %e, "Failed to cache flashblock trie"); + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + format!("Failed to cache flashblock trie: {}", e), + None::<()>, + ) + })?, + ) + } else { + None + }; + + // Meter bundle using utility function + let result = meter_bundle( + state_provider, + self.provider.chain_spec(), + parsed_bundle, + &header, + flashblocks_state, + cached_trie, + ) + .map_err(|e| { + error!(error = %e, "Bundle metering failed"); jsonrpsee::types::ErrorObjectOwned::owned( jsonrpsee::types::ErrorCode::InternalError.code(), - format!("Failed to get state provider: {}", e), + format!("Bundle metering failed: {}", e), None::<()>, ) })?; - // Meter bundle using utility function - let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) = - meter_bundle(state_provider, self.provider.chain_spec(), parsed_bundle, &header) - .map_err(|e| { - error!(error = %e, "Bundle metering failed"); - jsonrpsee::types::ErrorObjectOwned::owned( - jsonrpsee::types::ErrorCode::InternalError.code(), - format!("Bundle metering failed: {}", e), - None::<()>, - ) - })?; - // Calculate average gas price - let bundle_gas_price = if total_gas_used > 0 { - total_gas_fees / U256::from(total_gas_used) + let bundle_gas_price = if result.total_gas_used > 0 { + result.total_gas_fees / U256::from(result.total_gas_used) } else { U256::from(0) }; info!( - bundle_hash = %bundle_hash, - num_transactions = results.len(), - total_gas_used = total_gas_used, - total_execution_time_us = total_execution_time, + bundle_hash = %result.bundle_hash, + num_transactions = result.results.len(), + total_gas_used = result.total_gas_used, + total_time_us = result.total_time_us, + state_block_number = header.number, + flashblock_index = flashblock_index, "Bundle metering completed successfully" ); Ok(MeterBundleResponse { bundle_gas_price, - bundle_hash, - coinbase_diff: total_gas_fees, + bundle_hash: result.bundle_hash, + coinbase_diff: result.total_gas_fees, eth_sent_to_coinbase: U256::from(0), - gas_fees: total_gas_fees, - results, + gas_fees: result.total_gas_fees, + results: result.results, state_block_number: header.number, - state_flashblock_index: None, - total_gas_used, - total_execution_time_us: total_execution_time, + state_flashblock_index, + total_gas_used: result.total_gas_used, + // TODO: reintroduce state_root_time_us once tips-core exposes it again. + // TODO: rename total_execution_time_us to total_time_us since it includes state root time + total_execution_time_us: result.total_time_us, }) } } diff --git a/crates/rpc/src/base/mod.rs b/crates/rpc/src/base/mod.rs index b4097c40..f7bee2a8 100644 --- a/crates/rpc/src/base/mod.rs +++ b/crates/rpc/src/base/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod flashblock_trie_cache; pub(crate) mod meter; pub(crate) mod meter_rpc; pub(crate) mod pubsub; diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index 5a5db0a0..4f0facc3 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -8,7 +8,8 @@ pub use tips_core::types::{Bundle, MeterBundleResponse, TransactionResult}; mod base; pub use base::{ - meter::meter_bundle, + flashblock_trie_cache::{FlashblockTrieCache, FlashblockTrieData}, + meter::{FlashblocksState, MeterBundleOutput, meter_bundle}, meter_rpc::MeteringApiImpl, pubsub::{EthPubSub, EthPubSubApiServer}, traits::{MeteringApiServer, TransactionStatusApiServer}, diff --git a/crates/rpc/tests/meter.rs b/crates/rpc/tests/meter.rs index 23235233..ce421868 100644 --- a/crates/rpc/tests/meter.rs +++ b/crates/rpc/tests/meter.rs @@ -11,7 +11,11 @@ use base_reth_test_utils::create_provider_factory; use eyre::Context; use op_alloy_consensus::OpTxEnvelope; use rand::{SeedableRng, rngs::StdRng}; -use reth::{api::NodeTypesWithDBAdapter, chainspec::EthChainSpec}; +use reth::{ + api::NodeTypesWithDBAdapter, + chainspec::EthChainSpec, + revm::db::{BundleState, Cache}, +}; use reth_db::{DatabaseEnv, test_utils::TempDatabase}; use reth_optimism_chainspec::{BASE_MAINNET, OpChainSpec, OpChainSpecBuilder}; use reth_optimism_node::OpNode; @@ -20,6 +24,7 @@ use reth_primitives_traits::SealedHeader; use reth_provider::{HeaderProvider, StateProviderFactory, providers::BlockchainProvider}; use reth_testing_utils::generators::generate_keys; use reth_transaction_pool::test_utils::TransactionBuilder; +use revm::primitives::KECCAK_EMPTY; use tips_core::types::{Bundle, ParsedBundle}; type NodeTypes = NodeTypesWithDBAdapter>>; @@ -136,15 +141,22 @@ fn meter_bundle_empty_transactions() -> eyre::Result<()> { let parsed_bundle = create_parsed_bundle(Vec::new())?; - let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) = - meter_bundle(state_provider, harness.chain_spec.clone(), parsed_bundle, &harness.header)?; - - assert!(results.is_empty()); - assert_eq!(total_gas_used, 0); - assert_eq!(total_gas_fees, U256::ZERO); + let output = meter_bundle( + state_provider, + harness.chain_spec.clone(), + parsed_bundle, + &harness.header, + None, + None, + )?; + + assert!(output.results.is_empty()); + assert_eq!(output.total_gas_used, 0); + assert_eq!(output.total_gas_fees, U256::ZERO); // Even empty bundles have some EVM setup overhead - assert!(total_execution_time > 0); - assert_eq!(bundle_hash, keccak256([])); + assert!(output.total_time_us > 0); + assert!(output.state_root_time_us > 0); + assert_eq!(output.bundle_hash, keccak256([])); Ok(()) } @@ -178,12 +190,19 @@ fn meter_bundle_single_transaction() -> eyre::Result<()> { let parsed_bundle = create_parsed_bundle(vec![envelope.clone()])?; - let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) = - meter_bundle(state_provider, harness.chain_spec.clone(), parsed_bundle, &harness.header)?; + let output = meter_bundle( + state_provider, + harness.chain_spec.clone(), + parsed_bundle, + &harness.header, + None, + None, + )?; - assert_eq!(results.len(), 1); - let result = &results[0]; - assert!(total_execution_time > 0); + assert_eq!(output.results.len(), 1); + let result = &output.results[0]; + assert!(output.total_time_us > 0); + assert!(output.state_root_time_us > 0); assert_eq!(result.from_address, harness.address(User::Alice)); assert_eq!(result.to_address, Some(to)); @@ -192,12 +211,12 @@ fn meter_bundle_single_transaction() -> eyre::Result<()> { assert_eq!(result.gas_used, 21_000); assert_eq!(result.coinbase_diff, (U256::from(21_000) * U256::from(10)),); - assert_eq!(total_gas_used, 21_000); - assert_eq!(total_gas_fees, U256::from(21_000) * U256::from(10)); + assert_eq!(output.total_gas_used, 21_000); + assert_eq!(output.total_gas_fees, U256::from(21_000) * U256::from(10)); let mut concatenated = Vec::with_capacity(32); concatenated.extend_from_slice(tx_hash.as_slice()); - assert_eq!(bundle_hash, keccak256(concatenated)); + assert_eq!(output.bundle_hash, keccak256(concatenated)); assert!(result.execution_time_us > 0, "execution_time_us should be greater than zero"); @@ -255,14 +274,21 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { let parsed_bundle = create_parsed_bundle(vec![envelope_1.clone(), envelope_2.clone()])?; - let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) = - meter_bundle(state_provider, harness.chain_spec.clone(), parsed_bundle, &harness.header)?; + let output = meter_bundle( + state_provider, + harness.chain_spec.clone(), + parsed_bundle, + &harness.header, + None, + None, + )?; - assert_eq!(results.len(), 2); - assert!(total_execution_time > 0); + assert_eq!(output.results.len(), 2); + assert!(output.total_time_us > 0); + assert!(output.state_root_time_us > 0); // Check first transaction - let result_1 = &results[0]; + let result_1 = &output.results[0]; assert_eq!(result_1.from_address, harness.address(User::Alice)); assert_eq!(result_1.to_address, Some(to_1)); assert_eq!(result_1.tx_hash, tx_hash_1); @@ -271,7 +297,7 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { assert_eq!(result_1.coinbase_diff, (U256::from(21_000) * U256::from(10)),); // Check second transaction - let result_2 = &results[1]; + let result_2 = &output.results[1]; assert_eq!(result_2.from_address, harness.address(User::Bob)); assert_eq!(result_2.to_address, Some(to_2)); assert_eq!(result_2.tx_hash, tx_hash_2); @@ -280,19 +306,166 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { assert_eq!(result_2.coinbase_diff, U256::from(21_000) * U256::from(15),); // Check aggregated values - assert_eq!(total_gas_used, 42_000); + assert_eq!(output.total_gas_used, 42_000); let expected_total_fees = U256::from(21_000) * U256::from(10) + U256::from(21_000) * U256::from(15); - assert_eq!(total_gas_fees, expected_total_fees); + assert_eq!(output.total_gas_fees, expected_total_fees); // Check bundle hash includes both transactions let mut concatenated = Vec::with_capacity(64); concatenated.extend_from_slice(tx_hash_1.as_slice()); concatenated.extend_from_slice(tx_hash_2.as_slice()); - assert_eq!(bundle_hash, keccak256(concatenated)); + assert_eq!(output.bundle_hash, keccak256(concatenated)); assert!(result_1.execution_time_us > 0, "execution_time_us should be greater than zero"); assert!(result_2.execution_time_us > 0, "execution_time_us should be greater than zero"); Ok(()) } + +#[test] +fn meter_bundle_state_root_time_invariant() -> eyre::Result<()> { + let harness = setup_harness()?; + + let to = Address::random(); + let signed_tx = TransactionBuilder::default() + .signer(harness.signer(User::Alice)) + .chain_id(harness.chain_spec.chain_id()) + .nonce(0) + .to(to) + .value(1_000) + .gas_limit(21_000) + .max_fee_per_gas(10) + .max_priority_fee_per_gas(1) + .into_eip1559(); + + let tx = + OpTransactionSigned::Eip1559(signed_tx.as_eip1559().expect("eip1559 transaction").clone()); + + let envelope = envelope_from_signed(&tx)?; + + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let parsed_bundle = create_parsed_bundle(vec![envelope.clone()])?; + + let output = meter_bundle( + state_provider, + harness.chain_spec.clone(), + parsed_bundle, + &harness.header, + None, + None, + )?; + + // Verify invariant: total time must include state root time + assert!( + output.total_time_us >= output.state_root_time_us, + "total_time_us ({}) should be >= state_root_time_us ({})", + output.total_time_us, + output.state_root_time_us + ); + + // State root time should be non-zero + assert!(output.state_root_time_us > 0, "state_root_time_us should be greater than zero"); + + Ok(()) +} + +/// Integration test: verifies meter_bundle uses flashblocks state correctly. +/// +/// A transaction using nonce=1 should fail without flashblocks state (since +/// canonical nonce is 0), but succeed when flashblocks state indicates nonce=1. +#[test] +fn meter_bundle_requires_correct_layering_for_pending_nonce() -> eyre::Result<()> { + let harness = setup_harness()?; + let alice_address = harness.address(User::Alice); + + // Create a transaction that requires nonce=1 (assuming canonical nonce is 0) + let to = Address::random(); + let signed_tx = TransactionBuilder::default() + .signer(harness.signer(User::Alice)) + .chain_id(harness.chain_spec.chain_id()) + .nonce(1) // Requires pending state to have nonce=1 + .to(to) + .value(100) + .gas_limit(21_000) + .max_fee_per_gas(10) + .max_priority_fee_per_gas(1) + .into_eip1559(); + + let tx = + OpTransactionSigned::Eip1559(signed_tx.as_eip1559().expect("eip1559 transaction").clone()); + let envelope = envelope_from_signed(&tx)?; + let parsed_bundle = create_parsed_bundle(vec![envelope])?; + + // Without flashblocks state, transaction should fail (nonce mismatch) + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let result_without_flashblocks = meter_bundle( + state_provider, + harness.chain_spec.clone(), + parsed_bundle.clone(), + &harness.header, + None, // No flashblocks state + None, + ); + + assert!( + result_without_flashblocks.is_err(), + "Transaction with nonce=1 should fail without pending state (canonical nonce is 0)" + ); + + // Now create flashblocks state with nonce=1 for Alice + // Use BundleState::new() to properly calculate state_size + let bundle_state = BundleState::new( + [( + alice_address, + Some(revm::state::AccountInfo { + balance: U256::from(1_000_000_000u64), + nonce: 0, // original + code_hash: KECCAK_EMPTY, + code: None, + }), + Some(revm::state::AccountInfo { + balance: U256::from(1_000_000_000u64), + nonce: 1, // pending (after first flashblock tx) + code_hash: KECCAK_EMPTY, + code: None, + }), + Default::default(), // no storage changes + )], + Vec::>, Vec<(U256, U256)>)>>::new(), + Vec::<(B256, revm::bytecode::Bytecode)>::new(), + ); + + let flashblocks_state = FlashblocksState { cache: Cache::default(), bundle_state }; + + // With correct flashblocks state, transaction should succeed + let state_provider2 = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let result_with_flashblocks = meter_bundle( + state_provider2, + harness.chain_spec.clone(), + parsed_bundle, + &harness.header, + Some(flashblocks_state), + None, + ); + + assert!( + result_with_flashblocks.is_ok(), + "Transaction with nonce=1 should succeed with pending state showing nonce=1: {:?}", + result_with_flashblocks.err() + ); + + Ok(()) +} diff --git a/crates/runner/src/extensions/rpc.rs b/crates/runner/src/extensions/rpc.rs index 164a3af5..06be28e2 100644 --- a/crates/runner/src/extensions/rpc.rs +++ b/crates/runner/src/extensions/rpc.rs @@ -49,12 +49,6 @@ impl BaseNodeExtension for BaseRpcExtension { let sequencer_rpc = self.sequencer_rpc.clone(); builder.extend_rpc_modules(move |ctx| { - if metering_enabled { - info!(message = "Starting Metering RPC"); - let metering_api = MeteringApiImpl::new(ctx.provider().clone()); - ctx.modules.merge_configured(metering_api.into_rpc())?; - } - let proxy_api = TransactionStatusApiImpl::new(sequencer_rpc.clone(), ctx.pool().clone()) .expect("Failed to create transaction status proxy"); @@ -77,6 +71,13 @@ impl BaseNodeExtension for BaseRpcExtension { let mut flashblocks_client = FlashblocksSubscriber::new(fb.clone(), ws_url); flashblocks_client.start(); + // Metering API requires flashblocks state to access pending blocks for bundle simulation + if metering_enabled { + info!(message = "Starting Metering RPC"); + let metering_api = MeteringApiImpl::new(ctx.provider().clone(), fb.clone()); + ctx.modules.merge_configured(metering_api.into_rpc())?; + } + let api_ext = EthApiExt::new( ctx.registry.eth_api().clone(), ctx.registry.eth_handlers().filter.clone(), @@ -91,6 +92,9 @@ impl BaseNodeExtension for BaseRpcExtension { ctx.modules.replace_configured(eth_pubsub.into_rpc())?; } else { info!(message = "flashblocks integration is disabled"); + if metering_enabled { + info!(message = "Metering RPC requires flashblocks to be enabled - skipping"); + } } Ok(())