From f153d8f4d440d4cd8364c44e1ef639f84f852061 Mon Sep 17 00:00:00 2001 From: rakita Date: Sat, 16 Sep 2023 13:00:22 +0200 Subject: [PATCH] feat(revm): Integrate State (#3512) Co-authored-by: Roman Krasiuk Co-authored-by: Alexey Shekhirin Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com> Co-authored-by: Bjerg --- Cargo.lock | 589 +++-- Cargo.toml | 19 +- bin/reth/src/chain/import.rs | 1 + bin/reth/src/debug_cmd/execution.rs | 6 +- bin/reth/src/debug_cmd/in_memory_merkle.rs | 15 +- bin/reth/src/debug_cmd/merkle.rs | 6 +- bin/reth/src/init.rs | 81 +- bin/reth/src/node/mod.rs | 5 +- bin/reth/src/stage/dump/merkle.rs | 6 +- bin/reth/src/stage/run.rs | 1 + crates/blockchain-tree/src/blockchain_tree.rs | 62 +- crates/blockchain-tree/src/chain.rs | 62 +- crates/blockchain-tree/src/lib.rs | 5 +- crates/blockchain-tree/src/post_state_data.rs | 22 +- crates/blockchain-tree/src/shareable.rs | 11 +- crates/config/src/config.rs | 9 +- crates/consensus/auto-seal/src/lib.rs | 84 +- crates/consensus/auto-seal/src/task.rs | 15 +- crates/consensus/beacon/src/engine/mod.rs | 2 +- crates/consensus/beacon/src/engine/sync.rs | 6 +- .../consensus/beacon/src/engine/test_utils.rs | 76 +- crates/consensus/common/src/calc.rs | 12 +- .../interfaces/src/blockchain_tree/error.rs | 1 + crates/interfaces/src/executor.rs | 10 +- crates/payload/basic/Cargo.toml | 1 + crates/payload/basic/src/lib.rs | 104 +- crates/payload/builder/src/database.rs | 20 +- crates/payload/builder/src/payload.rs | 10 +- crates/primitives/src/account.rs | 22 +- crates/primitives/src/bits.rs | 2 + crates/primitives/src/bloom.rs | 2 + crates/primitives/src/proofs.rs | 2 +- crates/primitives/src/prune/part.rs | 2 +- crates/primitives/src/storage.rs | 7 + crates/primitives/src/transaction/mod.rs | 2 +- crates/primitives/src/withdrawal.rs | 9 +- crates/revm/Cargo.toml | 6 +- .../revm/revm-inspectors/src/access_list.rs | 1 - .../revm-inspectors/src/stack/maybe_owned.rs | 30 +- crates/revm/revm-inspectors/src/stack/mod.rs | 21 +- .../src/tracing/builder/parity.rs | 8 +- .../revm-inspectors/src/tracing/fourbyte.rs | 1 - .../revm-inspectors/src/tracing/js/mod.rs | 12 +- .../revm/revm-inspectors/src/tracing/mod.rs | 23 +- .../revm-inspectors/src/tracing/opcount.rs | 1 - crates/revm/revm-primitives/src/compat.rs | 12 +- crates/revm/revm-primitives/src/env.rs | 3 +- crates/revm/src/database.rs | 44 +- crates/revm/src/executor.rs | 1340 ----------- crates/revm/src/factory.rs | 24 +- crates/revm/src/lib.rs | 7 +- crates/revm/src/processor.rs | 531 +++++ crates/revm/src/state_change.rs | 85 + crates/rpc/rpc/src/debug.rs | 20 +- crates/rpc/rpc/src/eth/api/call.rs | 10 +- crates/rpc/rpc/src/eth/api/pending_block.rs | 47 +- crates/rpc/rpc/src/eth/api/transactions.rs | 12 +- crates/rpc/rpc/src/eth/cache/mod.rs | 8 +- crates/rpc/rpc/src/eth/error.rs | 8 +- crates/rpc/rpc/src/trace.rs | 18 +- crates/stages/Cargo.toml | 3 + crates/stages/src/pipeline/mod.rs | 4 +- crates/stages/src/stages/execution.rs | 94 +- crates/stages/src/stages/mod.rs | 7 +- crates/stages/src/stages/tx_lookup.rs | 2 - crates/storage/provider/Cargo.toml | 5 +- .../bundle_state_with_receipts.rs | 1198 ++++++++++ .../storage/provider/src/bundle_state/mod.rs | 11 + .../src/bundle_state/state_changes.rs | 88 + .../src/bundle_state/state_reverts.rs | 167 ++ crates/storage/provider/src/chain.rs | 96 +- crates/storage/provider/src/lib.rs | 19 +- .../provider/src/post_state/account.rs | 89 - crates/storage/provider/src/post_state/mod.rs | 2076 ----------------- .../provider/src/post_state/storage.rs | 156 -- ...e_provider.rs => bundle_state_provider.rs} | 54 +- .../src/providers/database/provider.rs | 213 +- crates/storage/provider/src/providers/mod.rs | 16 +- .../src/providers/state/historical.rs | 6 +- .../provider/src/providers/state/latest.rs | 8 +- .../provider/src/providers/state/macros.rs | 2 +- .../storage/provider/src/test_utils/blocks.rs | 117 +- .../provider/src/test_utils/executor.rs | 56 +- .../storage/provider/src/test_utils/mock.rs | 9 +- .../storage/provider/src/test_utils/noop.rs | 12 +- crates/storage/provider/src/traits/block.rs | 15 +- crates/storage/provider/src/traits/chain.rs | 4 +- .../storage/provider/src/traits/executor.rs | 72 +- crates/storage/provider/src/traits/mod.rs | 4 +- crates/storage/provider/src/traits/state.rs | 19 +- crates/storage/provider/src/transaction.rs | 197 -- crates/transaction-pool/src/maintain.rs | 27 +- crates/transaction-pool/src/pool/state.rs | 4 +- 93 files changed, 3436 insertions(+), 4975 deletions(-) delete mode 100644 crates/revm/src/executor.rs create mode 100644 crates/revm/src/processor.rs create mode 100644 crates/revm/src/state_change.rs create mode 100644 crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs create mode 100644 crates/storage/provider/src/bundle_state/mod.rs create mode 100644 crates/storage/provider/src/bundle_state/state_changes.rs create mode 100644 crates/storage/provider/src/bundle_state/state_reverts.rs delete mode 100644 crates/storage/provider/src/post_state/account.rs delete mode 100644 crates/storage/provider/src/post_state/mod.rs delete mode 100644 crates/storage/provider/src/post_state/storage.rs rename crates/storage/provider/src/providers/{post_state_provider.rs => bundle_state_provider.rs} (59%) delete mode 100644 crates/storage/provider/src/transaction.rs diff --git a/Cargo.lock b/Cargo.lock index 3b01df05b..352b0ebc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" [[package]] name = "anstyle-parse" @@ -242,8 +242,8 @@ dependencies = [ "include_dir", "itertools 0.10.5", "proc-macro-error", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -300,7 +300,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote 1.0.33", + "quote", "syn 1.0.109", ] @@ -310,7 +310,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.33", + "quote", "syn 1.0.109", ] @@ -322,7 +322,7 @@ checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ "num-bigint", "num-traits", - "quote 1.0.33", + "quote", "syn 1.0.109", ] @@ -334,8 +334,8 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -432,9 +432,9 @@ version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -485,8 +485,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -537,9 +537,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" [[package]] name = "base64ct" @@ -592,8 +592,8 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "regex", "rustc-hash", "shlex", @@ -614,12 +614,12 @@ dependencies = [ "log", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.32", + "syn 2.0.29", "which", ] @@ -635,12 +635,12 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.32", + "syn 2.0.29", ] [[package]] @@ -669,6 +669,10 @@ name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +dependencies = [ + "arbitrary", + "serde", +] [[package]] name = "bitvec" @@ -822,9 +826,9 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3de43b7806061fccfba716fef51eea462d636de36803b62d10f902608ffef4" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", "synstructure 0.13.0", ] @@ -857,9 +861,9 @@ checksum = "a24f6aa1ecc56e797506437b1f9a172e4a5f207894e74196c682cb656d2c2d60" [[package]] name = "boyer-moore-magiclen" -version = "0.2.18" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "116d76fee857b03ecdd95d5f9555e46aa0cd34e5bb348a520e9445d151182a7e" +checksum = "6c77eb6b3a37f71fcd40e49b56c028ea8795c0e550afd8021e3e6a2369653035" dependencies = [ "debug-helper", ] @@ -920,9 +924,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" [[package]] name = "byteorder" @@ -1038,9 +1042,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.30" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" +checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1135,9 +1139,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -1158,10 +1162,10 @@ version = "0.1.0-alpha.8" dependencies = [ "convert_case 0.6.0", "parity-scale-codec", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "serde", - "syn 2.0.32", + "syn 2.0.29", ] [[package]] @@ -1202,7 +1206,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ - "base64 0.21.4", + "base64 0.21.3", "bech32", "bs58", "digest 0.10.7", @@ -1506,9 +1510,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -1546,9 +1550,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588" +checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2" dependencies = [ "cfg-if", "cpufeatures", @@ -1567,9 +1571,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -1600,8 +1604,8 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "strsim 0.9.3", "syn 1.0.109", ] @@ -1614,10 +1618,10 @@ checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "strsim 0.10.0", - "syn 2.0.32", + "syn 2.0.29", ] [[package]] @@ -1627,7 +1631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core 0.10.2", - "quote 1.0.33", + "quote", "syn 1.0.109", ] @@ -1638,8 +1642,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", - "quote 1.0.33", - "syn 2.0.32", + "quote", + "syn 2.0.29", ] [[package]] @@ -1711,8 +1715,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -1722,9 +1726,9 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -1735,8 +1739,8 @@ checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" dependencies = [ "darling 0.10.2", "derive_builder_core", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -1747,8 +1751,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" dependencies = [ "darling 0.10.2", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -1759,8 +1763,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "rustc_version 0.4.0", "syn 1.0.109", ] @@ -1896,9 +1900,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -1971,13 +1975,13 @@ dependencies = [ [[package]] name = "educe" -version = "0.4.23" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +checksum = "079044df30bb07de7d846d41a184c4b00e66ebdac93ee459253474f3a47e50ae" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -2051,7 +2055,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0be7b2ac146c1f99fe245c02d16af0696450d8e06c135db75e10eeb9e642c20d" dependencies = [ - "base64 0.21.4", + "base64 0.21.3", "bytes", "ed25519-dalek", "hex", @@ -2073,8 +2077,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "570d109b813e904becc80d8d5da38376818a143348413f7149f1340fe04754d4" dependencies = [ "heck", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -2085,9 +2089,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck", - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -2098,9 +2102,9 @@ checksum = "e4f76552f53cefc9a7f64987c3701b99d982f7690606fd67de1d09712fbf52f1" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -2109,9 +2113,9 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -2253,13 +2257,13 @@ dependencies = [ "ethers-core", "eyre", "prettyplease", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "regex", "serde", "serde_json", - "syn 2.0.32", - "toml 0.7.8", + "syn 2.0.29", + "toml 0.7.6", "walkdir", ] @@ -2273,10 +2277,10 @@ dependencies = [ "const-hex", "ethers-contract-abigen", "ethers-core", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "serde_json", - "syn 2.0.32", + "syn 2.0.29", ] [[package]] @@ -2302,11 +2306,11 @@ dependencies = [ "serde", "serde_json", "strum 0.25.0", - "syn 2.0.32", + "syn 2.0.29", "tempfile", "thiserror", "tiny-keccak", - "unicode-xid 0.2.4", + "unicode-xid", ] [[package]] @@ -2359,7 +2363,7 @@ checksum = "6838fa110e57d572336178b7c79e94ff88ef976306852d8cb87d9e5b1fc7c0b5" dependencies = [ "async-trait", "auto_impl", - "base64 0.21.4", + "base64 0.21.3", "bytes", "const-hex", "enr", @@ -2505,9 +2509,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.1" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" [[package]] name = "findshlibs" @@ -2643,9 +2647,9 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -2874,7 +2878,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ "ahash 0.8.3", - "serde", ] [[package]] @@ -2885,6 +2888,7 @@ checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" dependencies = [ "ahash 0.8.3", "allocator-api2", + "serde", ] [[package]] @@ -3036,9 +3040,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "human_bytes" -version = "0.4.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91f255a4535024abf7640cb288260811fc14794f62b063652ed349f9a6c2348e" +checksum = "27e2b089f28ad15597b48d8c0a8fe94eeb1c1cb26ca99b6f66ac9582ae10c5e6" [[package]] name = "humantime" @@ -3216,8 +3220,8 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b728b9421e93eff1d9f8681101b78fa745e0748c95c655c83f337044a7e10" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -3304,8 +3308,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -3324,8 +3328,8 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", ] [[package]] @@ -3439,7 +3443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.13", + "rustix 0.38.11", "windows-sys 0.48.0", ] @@ -3610,8 +3614,8 @@ checksum = "985d4a3753a08aaf120429924567795b2764c5c691489316a7fd076178e708b4" dependencies = [ "heck", "proc-macro-crate", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -3682,7 +3686,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.4", + "base64 0.21.3", "pem", "ring", "serde", @@ -3793,9 +3797,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "litemap" @@ -3923,7 +3927,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a4964177ddfdab1e3a2b37aec7cf320e14169abb0ed73999f558136409178d5" dependencies = [ - "base64 0.21.4", + "base64 0.21.3", "hyper", "indexmap 1.9.3", "ipnet", @@ -3941,9 +3945,9 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -4045,8 +4049,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -4066,8 +4070,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -4252,9 +4256,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -4264,9 +4268,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -4280,9 +4284,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" dependencies = [ "memchr", ] @@ -4329,8 +4333,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -4373,9 +4377,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.8" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88eaac72ead1b9bd4ce747d577dbd2ad31fb0a56a9a20c611bf27bd1b97fbed" +checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" dependencies = [ "arrayvec", "bitvec", @@ -4388,13 +4392,13 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.8" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33bdcd446e9400b6ad9fc85b4aea68846c258b07c3efb994679ae82707b133f0" +checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -4555,9 +4559,9 @@ checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ "phf_generator", "phf_shared", - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -4584,9 +4588,9 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -4765,12 +4769,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ - "proc-macro2 1.0.66", - "syn 2.0.32", + "proc-macro2", + "syn 2.0.29", ] [[package]] @@ -4804,8 +4808,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", "version_check", ] @@ -4816,20 +4820,11 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "version_check", ] -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - [[package]] name = "proc-macro2" version = "1.0.66" @@ -4872,25 +4867,14 @@ dependencies = [ "unarray", ] -[[package]] -name = "proptest-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b46295382dc76166cb7cf2bb4a97952464e4b7ed5a43e6cd34e1fec3349ddc" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - [[package]] name = "proptest-derive" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -4946,22 +4930,13 @@ dependencies = [ "memchr", ] -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2", ] [[package]] @@ -5180,7 +5155,7 @@ version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "base64 0.21.4", + "base64 0.21.3", "bytes", "encoding_rs", "futures-core", @@ -5285,7 +5260,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "toml 0.7.8", + "toml 0.7.6", "tracing", "tui", "vergen", @@ -5315,6 +5290,7 @@ dependencies = [ "futures-core", "futures-util", "metrics", + "reth-interfaces", "reth-metrics", "reth-payload-builder", "reth-primitives", @@ -5386,7 +5362,7 @@ dependencies = [ "codecs-derive", "modular-bitfield", "proptest", - "proptest-derive 0.4.0", + "proptest-derive", "revm-primitives", "serde", "test-fuzz", @@ -5407,7 +5383,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "toml 0.7.8", + "toml 0.7.6", ] [[package]] @@ -5444,7 +5420,7 @@ dependencies = [ "postcard", "pprof", "proptest", - "proptest-derive 0.4.0", + "proptest-derive", "rand 0.8.5", "reth-codecs", "reth-db", @@ -5582,7 +5558,7 @@ dependencies = [ "metrics", "pin-project", "proptest", - "proptest-derive 0.4.0", + "proptest-derive", "rand 0.8.5", "reth-codecs", "reth-discv4", @@ -5695,11 +5671,11 @@ version = "0.1.0-alpha.8" dependencies = [ "metrics", "once_cell", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "regex", "serial_test", - "syn 2.0.32", + "syn 2.0.29", "trybuild", ] @@ -5836,7 +5812,7 @@ dependencies = [ "plain_hasher", "pprof", "proptest", - "proptest-derive 0.4.0", + "proptest-derive", "rand 0.8.5", "rayon", "reth-codecs", @@ -5857,7 +5833,7 @@ dependencies = [ "tiny-keccak", "tokio", "tokio-stream", - "toml 0.7.8", + "toml 0.7.6", "tracing", "triehash", "url", @@ -5870,16 +5846,17 @@ version = "0.1.0-alpha.8" dependencies = [ "assert_matches", "auto_impl", - "derive_more", "itertools 0.11.0", "parking_lot 0.12.1", "pin-project", + "rayon", "reth-db", "reth-interfaces", "reth-primitives", "reth-revm-primitives", "reth-rlp", "reth-trie", + "revm", "tempfile", "tokio", "tokio-stream", @@ -5908,14 +5885,12 @@ dependencies = [ name = "reth-revm" version = "0.1.0-alpha.8" dependencies = [ - "once_cell", "reth-consensus-common", "reth-interfaces", "reth-primitives", "reth-provider", "reth-revm-inspectors", "reth-revm-primitives", - "reth-rlp", "revm", "tracing", ] @@ -5967,9 +5942,9 @@ dependencies = [ name = "reth-rlp-derive" version = "0.1.0-alpha.8" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -6153,6 +6128,7 @@ dependencies = [ "reth-revm", "reth-rlp", "reth-trie", + "revm", "serde", "serde_json", "thiserror", @@ -6239,7 +6215,7 @@ dependencies = [ [[package]] name = "revm" version = "3.3.0" -source = "git+https://github.com/bluealloy/revm?branch=release/v25#6084e0fa2d457931cd8c9d29934bca0812b5b8d6" +source = "git+https://github.com/bluealloy/revm#9f00e3796e165efb2c76feb2c335d1d7468a9b3d" dependencies = [ "auto_impl", "revm-interpreter", @@ -6249,7 +6225,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "1.1.2" -source = "git+https://github.com/bluealloy/revm?branch=release/v25#6084e0fa2d457931cd8c9d29934bca0812b5b8d6" +source = "git+https://github.com/bluealloy/revm#9f00e3796e165efb2c76feb2c335d1d7468a9b3d" dependencies = [ "derive_more", "enumn", @@ -6260,7 +6236,7 @@ dependencies = [ [[package]] name = "revm-precompile" version = "2.0.3" -source = "git+https://github.com/bluealloy/revm?branch=release/v25#6084e0fa2d457931cd8c9d29934bca0812b5b8d6" +source = "git+https://github.com/bluealloy/revm#9f00e3796e165efb2c76feb2c335d1d7468a9b3d" dependencies = [ "k256", "num", @@ -6276,21 +6252,22 @@ dependencies = [ [[package]] name = "revm-primitives" version = "1.1.2" -source = "git+https://github.com/bluealloy/revm?branch=release/v25#6084e0fa2d457931cd8c9d29934bca0812b5b8d6" +source = "git+https://github.com/bluealloy/revm#9f00e3796e165efb2c76feb2c335d1d7468a9b3d" dependencies = [ "arbitrary", "auto_impl", + "bitflags 2.4.0", "bitvec", "bytes", "derive_more", "enumn", "fixed-hash", - "hashbrown 0.13.2", + "hashbrown 0.14.0", "hex", "hex-literal", "primitive-types", "proptest", - "proptest-derive 0.3.0", + "proptest-derive", "rlp", "ruint", "serde", @@ -6366,8 +6343,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -6470,14 +6447,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.13" +version = "0.38.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" dependencies = [ "bitflags 2.4.0", "errno 0.3.3", "libc", - "linux-raw-sys 0.4.7", + "linux-raw-sys 0.4.5", "windows-sys 0.48.0", ] @@ -6511,7 +6488,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.4", + "base64 0.21.3", ] [[package]] @@ -6591,8 +6568,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -6766,16 +6743,16 @@ version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] name = "serde_json" -version = "1.0.106" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", @@ -6809,7 +6786,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" dependencies = [ - "base64 0.21.4", + "base64 0.21.3", "chrono", "hex", "indexmap 1.9.3", @@ -6827,9 +6804,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" dependencies = [ "darling 0.20.3", - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -6852,9 +6829,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -6933,9 +6910,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook" @@ -7179,8 +7156,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "rustversion", "syn 1.0.109", ] @@ -7192,10 +7169,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" dependencies = [ "heck", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "rustversion", - "syn 2.0.32", + "syn 2.0.29", ] [[package]] @@ -7238,9 +7215,9 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.4.0" +version = "12.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e0e9bc48b3852f36a84f8d0da275d50cb3c2b88b59b9ec35fdd8b7fa239e37d" +checksum = "167a4ffd7c35c143fd1030aa3c2caf76ba42220bd5a6b5f4781896434723b8c3" dependencies = [ "debugid", "memmap2", @@ -7250,45 +7227,34 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.4.0" +version = "12.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "691e53bdc0702aba3a5abc2cffff89346fcbd4050748883c7e2f714b33a69045" +checksum = "e378c50e80686c1c5c205674e1f86a2858bec3d2a7dfdd690331a8a19330f293" dependencies = [ "cpp_demangle", "rustc-demangle", "symbolic-common", ] -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", -] - [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.32" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "unicode-ident", ] @@ -7298,10 +7264,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", - "unicode-xid 0.2.4", + "unicode-xid", ] [[package]] @@ -7310,10 +7276,10 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", - "unicode-xid 0.2.4", + "proc-macro2", + "quote", + "syn 2.0.29", + "unicode-xid", ] [[package]] @@ -7331,7 +7297,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix 0.38.13", + "rustix 0.38.11", "windows-sys 0.48.0", ] @@ -7369,8 +7335,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0528a7ad0bc85f826aa831434a37833aea622a5ae155f5b5dd431b25244213" dependencies = [ "cargo_metadata 0.15.4", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "serde", "strum_macros 0.25.2", ] @@ -7385,10 +7351,10 @@ dependencies = [ "if_chain", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "subprocess", - "syn 2.0.32", + "syn 2.0.29", "test-fuzz-internal", "toolchain_find", ] @@ -7428,9 +7394,9 @@ version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -7551,9 +7517,9 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -7617,9 +7583,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", "serde_spanned", @@ -7638,9 +7604,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap 2.0.0", "serde", @@ -7690,7 +7656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ "async-compression", - "base64 0.21.4", + "base64 0.21.3", "bitflags 2.4.0", "bytes", "futures-core", @@ -7755,9 +7721,9 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -7931,9 +7897,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "trybuild" -version = "1.0.84" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5c89fd17b7536f2cf66c97cff6e811e89e728ca0ed13caeed610c779360d8b4" +checksum = "6df60d81823ed9c520ee897489573da4b1d79ffbe006b8134f46de1a1aa03555" dependencies = [ "basic-toml", "glob", @@ -8048,12 +8014,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "unicode-xid" version = "0.2.4" @@ -8164,9 +8124,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.4.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", "winapi-util", @@ -8212,9 +8172,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", "wasm-bindgen-shared", ] @@ -8236,7 +8196,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ - "quote 1.0.33", + "quote", "wasm-bindgen-macro-support", ] @@ -8246,9 +8206,9 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8277,14 +8237,13 @@ checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "which" -version = "4.4.2" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", - "home", + "libc", "once_cell", - "rustix 0.38.13", ] [[package]] @@ -8551,9 +8510,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.18" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab77e97b50aee93da431f2cee7cd0f43b4d1da3c408042f2d7d164187774f0a" +checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1" [[package]] name = "xmltree" @@ -8588,8 +8547,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af46c169923ed7516eef0aa32b56d2651b229f57458ebe46b49ddd6efef5b7a2" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", "synstructure 0.12.6", ] @@ -8609,8 +8568,8 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4eae7c1f7d4b8eafce526bc0771449ddc2f250881ae31c50d22c032b5a1c499" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", "synstructure 0.12.6", ] @@ -8630,9 +8589,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", - "syn 2.0.32", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -8652,8 +8611,8 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "486558732d5dde10d0f8cb2936507c1bb21bc539d924c949baf5f36a58e51bac" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 1.0.109", "synstructure 0.12.6", ] diff --git a/Cargo.toml b/Cargo.toml index 343a5dbaf..2bd859afc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,10 +97,9 @@ reth-network = { path = "./crates/net/network" } reth-network-api = { path = "./crates/net/network-api" } reth-rpc-types-compat = { path = "./crates/rpc/rpc-types-compat" } -revm = { git = "https://github.com/bluealloy/revm", branch = "release/v25" } -revm-interpreter = { git = "https://github.com/bluealloy/revm", branch = "release/v25" } -revm-precompile = { git = "https://github.com/bluealloy/revm", branch = "release/v25" } -revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "release/v25" } +# revm +revm = { git = "https://github.com/bluealloy/revm" } +revm-primitives = { git = "https://github.com/bluealloy/revm" } ## eth ethers-core = { version = "2.0", default-features = false } @@ -117,7 +116,7 @@ boa_gc = "0.17" ## misc aquamarine = "0.3" -bytes = "1.4" +bytes = "1.5" bitflags = "2.3" tracing = "0.1.0" tracing-appender = "0.2" @@ -129,7 +128,7 @@ strum = "0.25" rayon = "1.7" itertools = "0.11" parking_lot = "0.12" -metrics = "0.21.1" # Needed for `metrics-macro` to resolve the crate using `::metrics` notation +metrics = "0.21.1" # Needed for `metrics-macro` to resolve the crate using `::metrics` notation hex-literal = "0.4" ### proc-macros @@ -172,10 +171,4 @@ assert_matches = "1.5.0" proptest = "1.0" proptest-derive = "0.4" -serial_test = "2" - -[patch.crates-io] -revm = { git = "https://github.com/bluealloy/revm", branch = "release/v25" } -revm-interpreter = { git = "https://github.com/bluealloy/revm", branch = "release/v25" } -revm-precompile = { git = "https://github.com/bluealloy/revm", branch = "release/v25" } -revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "release/v25" } +serial_test = "2" \ No newline at end of file diff --git a/bin/reth/src/chain/import.rs b/bin/reth/src/chain/import.rs index 429e5c796..19d6ec1d5 100644 --- a/bin/reth/src/chain/import.rs +++ b/bin/reth/src/chain/import.rs @@ -184,6 +184,7 @@ impl ImportCommand { ExecutionStageThresholds { max_blocks: config.stages.execution.max_blocks, max_changes: config.stages.execution.max_changes, + max_cumulative_gas: config.stages.execution.max_cumulative_gas, }, config .stages diff --git a/bin/reth/src/debug_cmd/execution.rs b/bin/reth/src/debug_cmd/execution.rs index db6f2146a..afd838569 100644 --- a/bin/reth/src/debug_cmd/execution.rs +++ b/bin/reth/src/debug_cmd/execution.rs @@ -135,7 +135,11 @@ impl Command { }) .set(ExecutionStage::new( factory, - ExecutionStageThresholds { max_blocks: None, max_changes: None }, + ExecutionStageThresholds { + max_blocks: None, + max_changes: None, + max_cumulative_gas: None, + }, stage_conf .merkle .clean_threshold diff --git a/bin/reth/src/debug_cmd/in_memory_merkle.rs b/bin/reth/src/debug_cmd/in_memory_merkle.rs index 837c31c75..bddece765 100644 --- a/bin/reth/src/debug_cmd/in_memory_merkle.rs +++ b/bin/reth/src/debug_cmd/in_memory_merkle.rs @@ -14,8 +14,9 @@ use reth_network::NetworkHandle; use reth_network_api::NetworkInfo; use reth_primitives::{fs, stage::StageId, BlockHashOrNumber, ChainSpec}; use reth_provider::{ - AccountExtReader, BlockExecutor, BlockWriter, ExecutorFactory, HashingWriter, HeaderProvider, - LatestStateProviderRef, ProviderFactory, StageCheckpointReader, StorageReader, + AccountExtReader, BlockWriter, ExecutorFactory, HashingWriter, HeaderProvider, + LatestStateProviderRef, OriginalValuesKnown, ProviderFactory, StageCheckpointReader, + StorageReader, }; use reth_tasks::TaskExecutor; use reth_trie::{hashed_cursor::HashedPostStateCursorFactory, updates::TrieKey, StateRoot}; @@ -164,17 +165,19 @@ impl Command { .await?; let executor_factory = reth_revm::Factory::new(self.chain.clone()); - let mut executor = executor_factory.with_sp(LatestStateProviderRef::new(provider.tx_ref())); + let mut executor = + executor_factory.with_state(LatestStateProviderRef::new(provider.tx_ref())); let merkle_block_td = provider.header_td_by_number(merkle_block_number)?.unwrap_or_default(); - let block_state = executor.execute_and_verify_receipt( + executor.execute_and_verify_receipt( &block.clone().unseal(), merkle_block_td + block.difficulty, None, )?; + let block_state = executor.take_output_state(); - // Unpacked `PostState::state_root_slow` function + // Unpacked `BundleState::state_root_slow` function let hashed_post_state = block_state.hash_state_slow().sorted(); let (account_prefix_set, storage_prefix_set) = hashed_post_state.construct_prefix_sets(); let tx = provider.tx_ref(); @@ -194,7 +197,7 @@ impl Command { // Insert block, state and hashes provider_rw.insert_block(block.clone(), None, None)?; - block_state.write_to_db(provider_rw.tx_ref(), block.number)?; + block_state.write_to_db(provider_rw.tx_ref(), OriginalValuesKnown::No)?; let storage_lists = provider_rw.changed_storages_with_range(block.number..=block.number)?; let storages = provider_rw.plainstate_storages(storage_lists)?; provider_rw.insert_storage_for_hashing(storages)?; diff --git a/bin/reth/src/debug_cmd/merkle.rs b/bin/reth/src/debug_cmd/merkle.rs index add25f6cd..37e41e8fc 100644 --- a/bin/reth/src/debug_cmd/merkle.rs +++ b/bin/reth/src/debug_cmd/merkle.rs @@ -205,7 +205,11 @@ impl Command { let factory = reth_revm::Factory::new(self.chain.clone()); let mut execution_stage = ExecutionStage::new( factory, - ExecutionStageThresholds { max_blocks: Some(1), max_changes: None }, + ExecutionStageThresholds { + max_blocks: Some(1), + max_changes: None, + max_cumulative_gas: None, + }, MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD, PruneModes::all(), ); diff --git a/bin/reth/src/init.rs b/bin/reth/src/init.rs index 0f46139a6..3f6d44b4e 100644 --- a/bin/reth/src/init.rs +++ b/bin/reth/src/init.rs @@ -6,8 +6,15 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, }; use reth_primitives::{stage::StageId, Account, Bytecode, ChainSpec, StorageEntry, H256, U256}; -use reth_provider::{DatabaseProviderRW, HashingWriter, HistoryWriter, PostState, ProviderFactory}; -use std::{collections::BTreeMap, sync::Arc}; +use reth_provider::{ + bundle_state::{BundleStateInit, RevertsInit}, + BundleStateWithReceipts, DatabaseProviderRW, HashingWriter, HistoryWriter, OriginalValuesKnown, + ProviderFactory, +}; +use std::{ + collections::{BTreeMap, HashMap}, + sync::Arc, +}; use tracing::debug; /// Database initialization error type. @@ -85,31 +92,63 @@ pub fn insert_genesis_state( tx: &>::TXMut, genesis: &reth_primitives::Genesis, ) -> Result<(), InitDatabaseError> { - let mut state = PostState::default(); + let mut state_init: BundleStateInit = HashMap::new(); + let mut reverts_init = HashMap::new(); + let mut contracts: HashMap = HashMap::new(); for (address, account) in &genesis.alloc { - let mut bytecode_hash = None; - if let Some(code) = &account.code { + let bytecode_hash = if let Some(code) = &account.code { let bytecode = Bytecode::new_raw(code.0.clone()); - // FIXME: Can bytecode_hash be Some(Bytes::new()) here? - bytecode_hash = Some(bytecode.hash); - state.add_bytecode(bytecode.hash, bytecode); - } - state.create_account( - 0, + let hash = bytecode.hash_slow(); + contracts.insert(hash, bytecode); + Some(hash) + } else { + None + }; + + // get state + let storage = account + .storage + .as_ref() + .map(|m| { + m.iter() + .map(|(key, value)| { + let value = U256::from_be_bytes(value.0); + (*key, (U256::ZERO, value)) + }) + .collect::>() + }) + .unwrap_or_default(); + + reverts_init.insert( *address, - Account { nonce: account.nonce.unwrap_or(0), balance: account.balance, bytecode_hash }, + (Some(None), storage.keys().map(|k| StorageEntry::new(*k, U256::ZERO)).collect()), + ); + + state_init.insert( + *address, + ( + None, + Some(Account { + nonce: account.nonce.unwrap_or_default(), + balance: account.balance, + bytecode_hash, + }), + storage, + ), ); - if let Some(storage) = &account.storage { - let mut storage_changes = reth_provider::post_state::StorageChangeset::new(); - for (&key, &value) in storage { - storage_changes - .insert(U256::from_be_bytes(key.0), (U256::ZERO, U256::from_be_bytes(value.0))); - } - state.change_storage(0, *address, storage_changes); - } } - state.write_to_db(tx, 0)?; + let all_reverts_init: RevertsInit = HashMap::from([(0, reverts_init)]); + + let bundle = BundleStateWithReceipts::new_init( + state_init, + all_reverts_init, + contracts.into_iter().collect(), + vec![], + 0, + ); + + bundle.write_to_db(tx, OriginalValuesKnown::Yes)?; Ok(()) } diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index 6f66d5677..8905dba4d 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -691,7 +691,7 @@ impl NodeCommand { /// NOTE: The download is attempted with infinite retries. async fn lookup_or_fetch_tip( &self, - db: &DB, + db: DB, client: Client, tip: H256, ) -> Result @@ -707,7 +707,7 @@ impl NodeCommand { /// NOTE: The download is attempted with infinite retries. async fn fetch_tip( &self, - db: &DB, + db: DB, client: Client, tip: BlockHashOrNumber, ) -> Result @@ -846,6 +846,7 @@ impl NodeCommand { ExecutionStageThresholds { max_blocks: stage_config.execution.max_blocks, max_changes: stage_config.execution.max_changes, + max_cumulative_gas: stage_config.execution.max_cumulative_gas, }, stage_config .merkle diff --git a/bin/reth/src/stage/dump/merkle.rs b/bin/reth/src/stage/dump/merkle.rs index 69ca457fd..55eef819f 100644 --- a/bin/reth/src/stage/dump/merkle.rs +++ b/bin/reth/src/stage/dump/merkle.rs @@ -69,7 +69,11 @@ async fn unwind_and_copy( // Bring Plainstate to TO (hashing stage execution requires it) let mut exec_stage = ExecutionStage::new( reth_revm::Factory::new(db_tool.chain.clone()), - ExecutionStageThresholds { max_blocks: Some(u64::MAX), max_changes: None }, + ExecutionStageThresholds { + max_blocks: Some(u64::MAX), + max_changes: None, + max_cumulative_gas: None, + }, MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD, PruneModes::all(), ); diff --git a/bin/reth/src/stage/run.rs b/bin/reth/src/stage/run.rs index 079db6783..d06ee8e7b 100644 --- a/bin/reth/src/stage/run.rs +++ b/bin/reth/src/stage/run.rs @@ -201,6 +201,7 @@ impl Command { ExecutionStageThresholds { max_blocks: Some(batch_size), max_changes: None, + max_cumulative_gas: None, }, config.stages.merkle.clean_threshold, config.prune.map(|prune| prune.parts).unwrap_or_default(), diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index ac17cd2f4..7302baab4 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -3,7 +3,8 @@ use crate::{ canonical_chain::CanonicalChain, chain::{BlockChainId, BlockKind}, metrics::TreeMetrics, - AppendableChain, BlockBuffer, BlockIndices, BlockchainTreeConfig, PostStateData, TreeExternals, + AppendableChain, BlockBuffer, BlockIndices, BlockchainTreeConfig, BundleStateData, + TreeExternals, }; use reth_db::{cursor::DbCursorRO, database::Database, tables, transaction::DbTx}; use reth_interfaces::{ @@ -21,10 +22,9 @@ use reth_primitives::{ }; use reth_provider::{ chain::{ChainSplit, SplitAt}, - post_state::PostState, - BlockExecutionWriter, BlockNumReader, BlockWriter, CanonStateNotification, - CanonStateNotificationSender, CanonStateNotifications, Chain, DatabaseProvider, - DisplayBlocksChain, ExecutorFactory, HeaderProvider, + BlockExecutionWriter, BlockNumReader, BlockWriter, BundleStateWithReceipts, + CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications, Chain, + DatabaseProvider, DisplayBlocksChain, ExecutorFactory, HeaderProvider, }; use reth_stages::{MetricEvent, MetricEventsSender}; use std::{ @@ -233,7 +233,7 @@ impl BlockchainTree /// Returns the block's receipts with matching hash from any side-chain. /// /// Caution: This will not return blocks from the canonical chain. - pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option<&[Receipt]> { + pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option> { let id = self.block_indices.get_blocks_chain_id(&block_hash)?; let chain = self.chains.get(&id)?; chain.receipts_by_block_hash(block_hash) @@ -254,11 +254,11 @@ impl BlockchainTree /// This includes: /// * `BlockHash` of canonical block that chain connects to. Needed for creating database /// provider for the rest of the state. - /// * `PostState` changes that happened at the asked `block_hash` + /// * `BundleState` changes that happened at the asked `block_hash` /// * `BTreeMap` list of past pending and canonical hashes, That are /// needed for evm `BLOCKHASH` opcode. /// Return none if block is not known. - pub fn post_state_data(&self, block_hash: BlockHash) -> Option { + pub fn post_state_data(&self, block_hash: BlockHash) -> Option { trace!(target: "blockchain_tree", ?block_hash, "Searching for post state data"); // if it is part of the chain if let Some(chain_id) = self.block_indices.get_blocks_chain_id(&block_hash) { @@ -281,15 +281,15 @@ impl BlockchainTree // get canonical fork. let canonical_fork = self.canonical_fork(chain_id)?; - return Some(PostStateData { state, parent_block_hashed, canonical_fork }) + return Some(BundleStateData { state, parent_block_hashed, canonical_fork }) } // check if there is canonical block if let Some(canonical_number) = self.canonical_chain().canonical_number(block_hash) { trace!(target: "blockchain_tree", ?block_hash, "Constructing post state data based on canonical chain"); - return Some(PostStateData { + return Some(BundleStateData { canonical_fork: ForkBlock { number: canonical_number, hash: block_hash }, - state: PostState::new(), + state: BundleStateWithReceipts::default(), parent_block_hashed: self.canonical_chain().inner().clone(), }) } @@ -844,12 +844,16 @@ impl BlockchainTree let chain = chain.into_inner(); match chain.split(split_at) { ChainSplit::Split { canonical, pending } => { + trace!(target: "blockchain_tree", ?canonical, ?pending, "Split chain"); // rest of split chain is inserted back with same chain_id. self.block_indices.insert_chain(chain_id, &pending); self.chains.insert(chain_id, AppendableChain::new(pending)); canonical } - ChainSplit::NoSplitCanonical(canonical) => canonical, + ChainSplit::NoSplitCanonical(canonical) => { + trace!(target: "blockchain_tree", "No split on canonical chain"); + canonical + } ChainSplit::NoSplitPending(_) => { unreachable!("Should not happen as block indices guarantee structure of blocks") } @@ -931,6 +935,8 @@ impl BlockchainTree }; let chain = self.chains.remove(&chain_id).expect("To be present"); + trace!(target: "blockchain_tree", ?chain, "Found chain to make canonical"); + // we are splitting chain at the block hash that we want to make canonical let canonical = self.split_chain(chain_id, chain, SplitAt::Hash(*block_hash)); @@ -942,6 +948,7 @@ impl BlockchainTree while let Some(chain_id) = self.block_indices.get_blocks_chain_id(&block_fork.hash) { let chain = self.chains.remove(&chain_id).expect("To fork to be present"); block_fork = chain.fork_block(); + // canonical chain is lower part of the chain. let canonical = self.split_chain(chain_id, chain, SplitAt::Number(block_fork_number)); block_fork_number = canonical.fork_block_number(); chains_to_promote.push(canonical); @@ -950,10 +957,17 @@ impl BlockchainTree let old_tip = self.block_indices.canonical_tip(); // Merge all chain into one chain. let mut new_canon_chain = chains_to_promote.pop().expect("There is at least one block"); + trace!(target: "blockchain_tree", ?new_canon_chain, "Merging chains"); + let mut chain_appended = false; for chain in chains_to_promote.into_iter().rev() { + chain_appended = true; + trace!(target: "blockchain_tree", ?chain, "Appending chain"); new_canon_chain.append_chain(chain).expect("We have just build the chain."); } + if chain_appended { + trace!(target: "blockchain_tree", ?new_canon_chain, "Canonical appended chain"); + } // update canonical index self.block_indices.canonicalize_blocks(new_canon_chain.blocks()); @@ -963,6 +977,7 @@ impl BlockchainTree target: "blockchain_tree", "Committing new canonical chain: {}", DisplayBlocksChain(new_canon_chain.blocks()) ); + // if joins to the tip; if new_canon_chain.fork_block_hash() == old_tip.hash { chain_notification = @@ -1000,7 +1015,6 @@ impl BlockchainTree } Ok(val) => val, }; - // commit new canonical chain. self.commit_canonical(new_canon_chain.clone())?; @@ -1051,7 +1065,7 @@ impl BlockchainTree let (blocks, state) = chain.into_inner(); provider - .append_blocks_with_post_state( + .append_blocks_with_bundle_state( blocks.into_blocks().collect(), state, self.prune_modes.as_ref(), @@ -1106,7 +1120,7 @@ impl BlockchainTree if blocks_and_execution.is_empty() { Ok(None) } else { - Ok(Some(Chain::new(blocks_and_execution))) + Ok(Some(blocks_and_execution)) } } @@ -1148,14 +1162,13 @@ mod tests { proofs::EMPTY_ROOT, stage::StageCheckpoint, ChainSpecBuilder, H256, MAINNET, }; use reth_provider::{ - post_state::PostState, test_utils::{blocks::BlockChainTestData, TestExecutorFactory}, - BlockWriter, ProviderFactory, + BlockWriter, BundleStateWithReceipts, ProviderFactory, }; use std::{collections::HashSet, sync::Arc}; fn setup_externals( - exec_res: Vec, + exec_res: Vec, ) -> TreeExternals, Arc, TestExecutorFactory> { let db = create_test_rw_db(); let consensus = Arc::new(TestConsensus::default()); @@ -1289,10 +1302,10 @@ mod tests { BlockchainTree::new(externals, sender, config, None).expect("failed to create tree"); // genesis block 10 is already canonical - assert!(tree.make_canonical(&H256::zero()).is_ok()); + tree.make_canonical(&H256::zero()).unwrap(); // make sure is_block_hash_canonical returns true for genesis block - assert!(tree.is_block_hash_canonical(&H256::zero()).unwrap()); + tree.is_block_hash_canonical(&H256::zero()).unwrap(); // make genesis block 10 as finalized tree.finalize_block(10); @@ -1365,12 +1378,12 @@ mod tests { ); // make block1 canonical - assert!(tree.make_canonical(&block1.hash()).is_ok()); + tree.make_canonical(&block1.hash()).unwrap(); // check notification assert_matches!(canon_notif.try_recv(), Ok(CanonStateNotification::Commit{ new}) if *new.blocks() == BTreeMap::from([(block1.number,block1.clone())])); // make block2 canonicals - assert!(tree.make_canonical(&block2.hash()).is_ok()); + tree.make_canonical(&block2.hash()).unwrap(); // check notification. assert_matches!(canon_notif.try_recv(), Ok(CanonStateNotification::Commit{ new}) if *new.blocks() == BTreeMap::from([(block2.number,block2.clone())])); @@ -1502,8 +1515,7 @@ mod tests { assert!(tree.is_block_hash_canonical(&block1a.hash).unwrap()); // make b2 canonical - assert!(tree.make_canonical(&block2.hash()).is_ok()); - + tree.make_canonical(&block2.hash()).unwrap(); // Trie state: // b2 b2a (side chain) // | / @@ -1572,7 +1584,7 @@ mod tests { .assert(&tree); // commit b2a - assert!(tree.make_canonical(&block2.hash).is_ok()); + tree.make_canonical(&block2.hash).unwrap(); // Trie state: // b2 b2a (side chain) diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 8a3b1b615..258976fde 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -3,7 +3,7 @@ //! A [`Chain`] contains the state of accounts for the chain after execution of its constituent //! blocks, as well as a list of the blocks the chain is composed of. use super::externals::TreeExternals; -use crate::{post_state::PostState, PostStateDataRef}; +use crate::BundleStateDataRef; use reth_db::database::Database; use reth_interfaces::{ blockchain_tree::error::{BlockchainTreeError, InsertBlockError}, @@ -14,8 +14,8 @@ use reth_primitives::{ BlockHash, BlockNumber, ForkBlock, SealedBlockWithSenders, SealedHeader, U256, }; use reth_provider::{ - providers::PostStateProvider, BlockExecutor, Chain, ExecutorFactory, PostStateDataProvider, - StateRootProvider, + providers::BundleStateProvider, BundleStateDataProvider, BundleStateWithReceipts, Chain, + ExecutorFactory, StateRootProvider, }; use std::{ collections::BTreeMap, @@ -72,17 +72,17 @@ impl AppendableChain { C: Consensus, EF: ExecutorFactory, { - let state = PostState::default(); + let state = BundleStateWithReceipts::default(); let empty = BTreeMap::new(); - let state_provider = PostStateDataRef { + let state_provider = BundleStateDataRef { state: &state, sidechain_block_hashes: &empty, canonical_block_hashes, canonical_fork, }; - let changeset = Self::validate_and_execute_canonical_head_descendant( + let bundle_state = Self::validate_and_execute_canonical_head_descendant( block.clone(), parent_header, state_provider, @@ -90,7 +90,7 @@ impl AppendableChain { ) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; - Ok(Self { chain: Chain::new(vec![(block, changeset)]) }) + Ok(Self { chain: Chain::new(vec![block], bundle_state) }) } /// Create a new chain that forks off of the canonical chain. @@ -106,17 +106,17 @@ impl AppendableChain { C: Consensus, EF: ExecutorFactory, { - let state = PostState::default(); + let state = BundleStateWithReceipts::default(); let empty = BTreeMap::new(); - let state_provider = PostStateDataRef { + let state_provider = BundleStateDataRef { state: &state, sidechain_block_hashes: &empty, canonical_block_hashes, canonical_fork, }; - let changeset = Self::validate_and_execute_sidechain( + let bundle_state = Self::validate_and_execute_sidechain( block.clone(), parent_header, state_provider, @@ -124,7 +124,7 @@ impl AppendableChain { ) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; - Ok(Self { chain: Chain::new(vec![(block, changeset)]) }) + Ok(Self { chain: Chain::new(vec![block], bundle_state) }) } /// Create a new chain that forks off of an existing sidechain. @@ -157,7 +157,7 @@ impl AppendableChain { state.revert_to(parent.number); // Revert changesets to get the state of the parent that we need to apply the change. - let post_state_data = PostStateDataRef { + let post_state_data = BundleStateDataRef { state: &state, sidechain_block_hashes: &side_chain_block_hashes, canonical_block_hashes, @@ -177,15 +177,15 @@ impl AppendableChain { /// Validate and execute the given block that _extends the canonical chain_, validating its /// state root after execution. - fn validate_and_execute( + fn validate_and_execute( block: SealedBlockWithSenders, parent_block: &SealedHeader, - post_state_data_provider: PSDP, + post_state_data_provider: BSDP, externals: &TreeExternals, block_kind: BlockKind, - ) -> Result + ) -> Result where - PSDP: PostStateDataProvider, + BSDP: BundleStateDataProvider, DB: Database, C: Consensus, EF: ExecutorFactory, @@ -201,15 +201,16 @@ impl AppendableChain { let canonical_fork = post_state_data_provider.canonical_fork(); let state_provider = db.history_by_block_number(canonical_fork.number)?; - let provider = PostStateProvider::new(state_provider, post_state_data_provider); + let provider = BundleStateProvider::new(state_provider, post_state_data_provider); - let mut executor = externals.executor_factory.with_sp(&provider); - let post_state = executor.execute_and_verify_receipt(&block, U256::MAX, Some(senders))?; + let mut executor = externals.executor_factory.with_state(&provider); + executor.execute_and_verify_receipt(&block, U256::MAX, Some(senders))?; + let bundle_state = executor.take_output_state(); // check state root if the block extends the canonical chain. if block_kind.extends_canonical_head() { // check state root - let state_root = provider.state_root(post_state.clone())?; + let state_root = provider.state_root(bundle_state.clone())?; if block.state_root != state_root { return Err(ConsensusError::BodyStateRootDiff { got: state_root, @@ -219,19 +220,19 @@ impl AppendableChain { } } - Ok(post_state) + Ok(bundle_state) } /// Validate and execute the given block that _extends the canonical chain_, validating its /// state root after execution. - fn validate_and_execute_canonical_head_descendant( + fn validate_and_execute_canonical_head_descendant( block: SealedBlockWithSenders, parent_block: &SealedHeader, - post_state_data_provider: PSDP, + post_state_data_provider: BSDP, externals: &TreeExternals, - ) -> Result + ) -> Result where - PSDP: PostStateDataProvider, + BSDP: BundleStateDataProvider, DB: Database, C: Consensus, EF: ExecutorFactory, @@ -246,14 +247,14 @@ impl AppendableChain { } /// Validate and execute the given sidechain block, skipping state root validation. - fn validate_and_execute_sidechain( + fn validate_and_execute_sidechain( block: SealedBlockWithSenders, parent_block: &SealedHeader, - post_state_data_provider: PSDP, + post_state_data_provider: BSDP, externals: &TreeExternals, - ) -> Result + ) -> Result where - PSDP: PostStateDataProvider, + BSDP: BundleStateDataProvider, DB: Database, C: Consensus, EF: ExecutorFactory, @@ -295,7 +296,7 @@ impl AppendableChain { { let (_, parent_block) = self.blocks.last_key_value().expect("Chain has at least one block"); - let post_state_data = PostStateDataRef { + let post_state_data = BundleStateDataRef { state: &self.state, sidechain_block_hashes: &side_chain_block_hashes, canonical_block_hashes, @@ -310,6 +311,7 @@ impl AppendableChain { block_kind, ) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; + // extend the state. self.state.extend(block_state); self.blocks.insert(block.number, block); Ok(()) diff --git a/crates/blockchain-tree/src/lib.rs b/crates/blockchain-tree/src/lib.rs index 15aa1da16..9f48a41d8 100644 --- a/crates/blockchain-tree/src/lib.rs +++ b/crates/blockchain-tree/src/lib.rs @@ -22,9 +22,6 @@ //! //! - `test-utils`: Export utilities for testing -/// Execution result types. -pub use reth_provider::post_state; - pub mod blockchain_tree; pub use blockchain_tree::{BlockHashes, BlockchainTree}; @@ -44,7 +41,7 @@ pub mod shareable; pub use shareable::ShareableBlockchainTree; pub mod post_state_data; -pub use post_state_data::{PostStateData, PostStateDataRef}; +pub use post_state_data::{BundleStateData, BundleStateDataRef}; /// Buffer of not executed blocks. pub mod block_buffer; diff --git a/crates/blockchain-tree/src/post_state_data.rs b/crates/blockchain-tree/src/post_state_data.rs index 324400281..b03f25e15 100644 --- a/crates/blockchain-tree/src/post_state_data.rs +++ b/crates/blockchain-tree/src/post_state_data.rs @@ -1,14 +1,14 @@ //! Substate for blockchain trees use reth_primitives::{BlockHash, BlockNumber, ForkBlock}; -use reth_provider::{post_state::PostState, PostStateDataProvider}; +use reth_provider::{BundleStateDataProvider, BundleStateWithReceipts}; use std::collections::BTreeMap; -/// Structure that bundles references of data needs to implement [`PostStateDataProvider`] +/// Structure that bundles references of data needs to implement [`BundleStateDataProvider`] #[derive(Clone, Debug)] -pub struct PostStateDataRef<'a> { +pub struct BundleStateDataRef<'a> { /// The wrapped state after execution of one or more transactions and/or blocks. - pub state: &'a PostState, + pub state: &'a BundleStateWithReceipts, /// The blocks in the sidechain. pub sidechain_block_hashes: &'a BTreeMap, /// The blocks in the canonical chain. @@ -17,8 +17,8 @@ pub struct PostStateDataRef<'a> { pub canonical_fork: ForkBlock, } -impl<'a> PostStateDataProvider for PostStateDataRef<'a> { - fn state(&self) -> &PostState { +impl<'a> BundleStateDataProvider for BundleStateDataRef<'a> { + fn state(&self) -> &BundleStateWithReceipts { self.state } @@ -36,11 +36,11 @@ impl<'a> PostStateDataProvider for PostStateDataRef<'a> { } } -/// Structure that contains data needs to implement [`PostStateDataProvider`] +/// Structure that contains data needs to implement [`BundleStateDataProvider`] #[derive(Clone, Debug)] -pub struct PostStateData { +pub struct BundleStateData { /// Post state with changes - pub state: PostState, + pub state: BundleStateWithReceipts, /// Parent block hashes needs for evm BLOCKHASH opcode. /// NOTE: it does not mean that all hashes are there but all until finalized are there. /// Other hashes can be obtained from provider @@ -49,8 +49,8 @@ pub struct PostStateData { pub canonical_fork: ForkBlock, } -impl PostStateDataProvider for PostStateData { - fn state(&self) -> &PostState { +impl BundleStateDataProvider for BundleStateData { + fn state(&self) -> &BundleStateWithReceipts { &self.state } diff --git a/crates/blockchain-tree/src/shareable.rs b/crates/blockchain-tree/src/shareable.rs index ab191385e..6b9c6380b 100644 --- a/crates/blockchain-tree/src/shareable.rs +++ b/crates/blockchain-tree/src/shareable.rs @@ -15,8 +15,8 @@ use reth_primitives::{ SealedHeader, }; use reth_provider::{ - BlockchainTreePendingStateProvider, CanonStateSubscriptions, ExecutorFactory, - PostStateDataProvider, + BlockchainTreePendingStateProvider, BundleStateDataProvider, CanonStateSubscriptions, + ExecutorFactory, }; use std::{ collections::{BTreeMap, HashSet}, @@ -181,13 +181,14 @@ impl BlockchainTreeViewer fn pending_block_and_receipts(&self) -> Option<(SealedBlock, Vec)> { let tree = self.tree.read(); let pending_block = tree.pending_block()?.clone(); - let receipts = tree.receipts_by_block_hash(pending_block.hash)?.to_vec(); + let receipts = + tree.receipts_by_block_hash(pending_block.hash)?.into_iter().cloned().collect(); Some((pending_block, receipts)) } fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option> { let tree = self.tree.read(); - Some(tree.receipts_by_block_hash(block_hash)?.to_vec()) + Some(tree.receipts_by_block_hash(block_hash)?.into_iter().cloned().collect()) } } @@ -197,7 +198,7 @@ impl BlockchainTreePendingState fn find_pending_state_provider( &self, block_hash: BlockHash, - ) -> Option> { + ) -> Option> { trace!(target: "blockchain_tree", ?block_hash, "Finding pending state provider"); let provider = self.tree.read().post_state_data(block_hash)?; Some(Box::new(provider)) diff --git a/crates/config/src/config.rs b/crates/config/src/config.rs index d5e0f9bb2..5e2dbe39a 100644 --- a/crates/config/src/config.rs +++ b/crates/config/src/config.rs @@ -210,11 +210,18 @@ pub struct ExecutionConfig { pub max_blocks: Option, /// The maximum amount of state changes to keep in memory before the execution stage commits. pub max_changes: Option, + /// The maximum gas to process before the execution stage commits. + pub max_cumulative_gas: Option, } impl Default for ExecutionConfig { fn default() -> Self { - Self { max_blocks: Some(500_000), max_changes: Some(5_000_000) } + Self { + max_blocks: Some(500_000), + max_changes: Some(5_000_000), + // 50k full blocks of 30M gas + max_cumulative_gas: Some(30_000_000 * 50_000), + } } } diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index f68d1271a..ea6b87f5e 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -31,8 +31,14 @@ use reth_primitives::{ Header, ReceiptWithBloom, SealedBlock, SealedHeader, TransactionSigned, EMPTY_OMMER_ROOT, H256, U256, }; -use reth_provider::{BlockReaderIdExt, CanonStateNotificationSender, PostState, StateProvider}; -use reth_revm::executor::Executor; +use reth_provider::{ + BlockExecutor, BlockReaderIdExt, BundleStateWithReceipts, CanonStateNotificationSender, + StateProviderFactory, +}; +use reth_revm::{ + database::StateProviderDatabase, db::states::bundle_state::BundleRetention, + processor::EVMProcessor, State, +}; use reth_transaction_pool::TransactionPool; use std::{ collections::HashMap, @@ -289,41 +295,51 @@ impl StorageInner { header } - /// Executes the block with the given block and senders, on the provided [Executor]. + /// Executes the block with the given block and senders, on the provided [EVMProcessor]. /// /// This returns the poststate from execution and post-block changes, as well as the gas used. - pub(crate) fn execute( + pub(crate) fn execute( &mut self, block: &Block, - executor: &mut Executor, + executor: &mut EVMProcessor<'_>, senders: Vec
, - ) -> Result<(PostState, u64), BlockExecutionError> { + ) -> Result<(BundleStateWithReceipts, u64), BlockExecutionError> { trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions"); - let (post_state, gas_used) = + let (receipts, gas_used) = executor.execute_transactions(block, U256::ZERO, Some(senders))?; - // apply post block changes - let post_state = executor.apply_post_block_changes(block, U256::ZERO, post_state)?; + // Save receipts. + executor.save_receipts(receipts)?; - Ok((post_state, gas_used)) + // add post execution state change + // Withdrawals, rewards etc. + executor.apply_post_execution_state_change(block, U256::ZERO)?; + + // merge transitions + executor.db_mut().merge_transitions(BundleRetention::Reverts); + + // apply post block changes + Ok((executor.take_output_state(), gas_used)) } - /// Fills in the post-execution header fields based on the given PostState and gas used. + /// Fills in the post-execution header fields based on the given BundleState and gas used. /// In doing this, the state root is calculated and the final header is returned. - pub(crate) fn complete_header( + pub(crate) fn complete_header( &self, mut header: Header, - post_state: &PostState, - executor: &mut Executor, + bundle_state: &BundleStateWithReceipts, + client: &S, gas_used: u64, - ) -> Header { - let receipts = post_state.receipts(header.number); + ) -> Result { + let receipts = bundle_state.receipts_by_block(header.number); header.receipts_root = if receipts.is_empty() { EMPTY_RECEIPTS } else { - let receipts_with_bloom = - receipts.iter().map(|r| r.clone().into()).collect::>(); + let receipts_with_bloom = receipts + .iter() + .map(|r| (*r).clone().expect("receipts have not been pruned").into()) + .collect::>(); header.logs_bloom = receipts_with_bloom.iter().fold(Bloom::zero(), |bloom, r| bloom | r.bloom); proofs::calculate_receipt_root(&receipts_with_bloom) @@ -332,21 +348,25 @@ impl StorageInner { header.gas_used = gas_used; // calculate the state root - let state_root = executor.db().db.0.state_root(post_state.clone()).unwrap(); + let state_root = client + .latest() + .map_err(|_| BlockExecutionError::ProviderError)? + .state_root(bundle_state.clone()) + .unwrap(); header.state_root = state_root; - header + Ok(header) } - /// Builds and executes a new block with the given transactions, on the provided [Executor]. + /// Builds and executes a new block with the given transactions, on the provided [EVMProcessor]. /// /// This returns the header of the executed block, as well as the poststate from execution. - pub(crate) fn build_and_execute( + pub(crate) fn build_and_execute( &mut self, transactions: Vec, - executor: &mut Executor, + client: &impl StateProviderFactory, chain_spec: Arc, - ) -> Result<(SealedHeader, PostState), BlockExecutionError> { - let header = self.build_header_template(&transactions, chain_spec); + ) -> Result<(SealedHeader, BundleStateWithReceipts), BlockExecutionError> { + let header = self.build_header_template(&transactions, chain_spec.clone()); let block = Block { header, body: transactions, ommers: vec![], withdrawals: None }; @@ -356,15 +376,21 @@ impl StorageInner { trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions"); // now execute the block - let (post_state, gas_used) = self.execute(&block, executor, senders)?; + let db = State::builder() + .with_database_boxed(Box::new(StateProviderDatabase::new(client.latest().unwrap()))) + .with_bundle_update() + .build(); + let mut executor = EVMProcessor::new_with_state(chain_spec, db); + + let (bundle_state, gas_used) = self.execute(&block, &mut executor, senders)?; let Block { header, body, .. } = block; let body = BlockBody { transactions: body, ommers: vec![], withdrawals: None }; - trace!(target: "consensus::auto", ?post_state, ?header, ?body, "executed block, calculating state root and completing header"); + trace!(target: "consensus::auto", ?bundle_state, ?header, ?body, "executed block, calculating state root and completing header"); // fill in the rest of the fields - let header = self.complete_header(header, &post_state, executor, gas_used); + let header = self.complete_header(header, &bundle_state, client, gas_used)?; trace!(target: "consensus::auto", root=?header.state_root, ?body, "calculated root"); @@ -374,6 +400,6 @@ impl StorageInner { // set new header with hash that should have been updated by insert_new_block let new_header = header.seal(self.best_hash); - Ok((new_header, post_state)) + Ok((new_header, bundle_state)) } } diff --git a/crates/consensus/auto-seal/src/task.rs b/crates/consensus/auto-seal/src/task.rs index 4abdd19b1..2ace06489 100644 --- a/crates/consensus/auto-seal/src/task.rs +++ b/crates/consensus/auto-seal/src/task.rs @@ -4,10 +4,6 @@ use reth_beacon_consensus::{BeaconEngineMessage, ForkchoiceStatus}; use reth_interfaces::consensus::ForkchoiceState; use reth_primitives::{Block, ChainSpec, IntoRecoveredTransaction, SealedBlockWithSenders}; use reth_provider::{CanonChainTracker, CanonStateNotificationSender, Chain, StateProviderFactory}; -use reth_revm::{ - database::{State, SubState}, - executor::Executor, -}; use reth_stages::PipelineEvent; use reth_transaction_pool::{TransactionPool, ValidPoolTransaction}; use std::{ @@ -127,13 +123,8 @@ where }) .unzip(); - // execute the new block - let substate = SubState::new(State::new(client.latest().unwrap())); - let mut executor = Executor::new(Arc::clone(&chain_spec), substate); - - match storage.build_and_execute(transactions.clone(), &mut executor, chain_spec) - { - Ok((new_header, post_state)) => { + match storage.build_and_execute(transactions.clone(), &client, chain_spec) { + Ok((new_header, bundle_state)) => { // clear all transactions from pool pool.remove_transactions( transactions.iter().map(|tx| tx.hash()).collect(), @@ -202,7 +193,7 @@ where debug!(target: "consensus::auto", header=?sealed_block_with_senders.hash(), "sending block notification"); let chain = - Arc::new(Chain::new(vec![(sealed_block_with_senders, post_state)])); + Arc::new(Chain::new(vec![sealed_block_with_senders], bundle_state)); // send block notification let _ = canon_state_notification diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index 03d164d7d..71a3577f2 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -2057,7 +2057,7 @@ mod tests { } fn insert_blocks<'a, DB: Database>( - db: &DB, + db: DB, chain: Arc, mut blocks: impl Iterator, ) { diff --git a/crates/consensus/beacon/src/engine/sync.rs b/crates/consensus/beacon/src/engine/sync.rs index 24b4d04a3..2042dd142 100644 --- a/crates/consensus/beacon/src/engine/sync.rs +++ b/crates/consensus/beacon/src/engine/sync.rs @@ -402,7 +402,7 @@ mod tests { constants::ETHEREUM_BLOCK_GAS_LIMIT, stage::StageCheckpoint, BlockBody, ChainSpec, ChainSpecBuilder, Header, SealedHeader, MAINNET, }; - use reth_provider::{test_utils::TestExecutorFactory, PostState}; + use reth_provider::{test_utils::TestExecutorFactory, BundleStateWithReceipts}; use reth_stages::{test_utils::TestStages, ExecOutput, StageError}; use reth_tasks::TokioTaskExecutor; use std::{collections::VecDeque, future::poll_fn, sync::Arc}; @@ -410,7 +410,7 @@ mod tests { struct TestPipelineBuilder { pipeline_exec_outputs: VecDeque>, - executor_results: Vec, + executor_results: Vec, max_block: Option, } @@ -435,7 +435,7 @@ mod tests { /// Set the executor results to use for the test consensus engine. #[allow(dead_code)] - fn with_executor_results(mut self, executor_results: Vec) -> Self { + fn with_executor_results(mut self, executor_results: Vec) -> Self { self.executor_results = executor_results; self } diff --git a/crates/consensus/beacon/src/engine/test_utils.rs b/crates/consensus/beacon/src/engine/test_utils.rs index d3959f449..cf2a47c3e 100644 --- a/crates/consensus/beacon/src/engine/test_utils.rs +++ b/crates/consensus/beacon/src/engine/test_utils.rs @@ -4,8 +4,7 @@ use crate::{ MIN_BLOCKS_FOR_PIPELINE_RUN, }; use reth_blockchain_tree::{ - config::BlockchainTreeConfig, externals::TreeExternals, post_state::PostState, BlockchainTree, - ShareableBlockchainTree, + config::BlockchainTreeConfig, externals::TreeExternals, BlockchainTree, ShareableBlockchainTree, }; use reth_db::{test_utils::create_test_rw_db, DatabaseEnv}; use reth_downloaders::{ @@ -22,8 +21,8 @@ use reth_interfaces::{ use reth_payload_builder::test_utils::spawn_test_payload_service; use reth_primitives::{BlockNumber, ChainSpec, PruneBatchSizes, PruneModes, H256, U256}; use reth_provider::{ - providers::BlockchainProvider, test_utils::TestExecutorFactory, BlockExecutor, ExecutorFactory, - ProviderFactory, StateProvider, + providers::BlockchainProvider, test_utils::TestExecutorFactory, BlockExecutor, + BundleStateWithReceipts, ExecutorFactory, ProviderFactory, PrunableBlockExecutor, }; use reth_prune::Pruner; use reth_revm::Factory; @@ -143,7 +142,7 @@ impl Default for TestPipelineConfig { /// Represents either test executor results, or real executor configuration. enum TestExecutorConfig { /// Test executor results. - Test(Vec), + Test(Vec), /// Real executor configuration. Real, } @@ -172,18 +171,17 @@ pub enum EitherBlockExecutor { Right(B), } -impl BlockExecutor for EitherBlockExecutor +impl BlockExecutor for EitherBlockExecutor where - A: BlockExecutor, - B: BlockExecutor, - SP: StateProvider, + A: BlockExecutor, + B: BlockExecutor, { fn execute( &mut self, block: &reth_primitives::Block, total_difficulty: U256, senders: Option>, - ) -> Result { + ) -> Result<(), BlockExecutionError> { match self { EitherBlockExecutor::Left(a) => a.execute(block, total_difficulty, senders), EitherBlockExecutor::Right(b) => b.execute(block, total_difficulty, senders), @@ -195,7 +193,7 @@ where block: &reth_primitives::Block, total_difficulty: U256, senders: Option>, - ) -> Result { + ) -> Result<(), BlockExecutionError> { match self { EitherBlockExecutor::Left(a) => { a.execute_and_verify_receipt(block, total_difficulty, senders) @@ -205,6 +203,47 @@ where } } } + + fn take_output_state(&mut self) -> BundleStateWithReceipts { + match self { + EitherBlockExecutor::Left(a) => a.take_output_state(), + EitherBlockExecutor::Right(b) => b.take_output_state(), + } + } + + fn stats(&self) -> reth_provider::BlockExecutorStats { + match self { + EitherBlockExecutor::Left(a) => a.stats(), + EitherBlockExecutor::Right(b) => b.stats(), + } + } + + fn size_hint(&self) -> Option { + match self { + EitherBlockExecutor::Left(a) => a.size_hint(), + EitherBlockExecutor::Right(b) => b.size_hint(), + } + } +} + +impl PrunableBlockExecutor for EitherBlockExecutor +where + B: PrunableBlockExecutor, + A: PrunableBlockExecutor, +{ + fn set_prune_modes(&mut self, prune_modes: PruneModes) { + match self { + EitherBlockExecutor::Left(a) => a.set_prune_modes(prune_modes), + EitherBlockExecutor::Right(b) => b.set_prune_modes(prune_modes), + } + } + + fn set_tip(&mut self, tip: BlockNumber) { + match self { + EitherBlockExecutor::Left(a) => a.set_tip(tip), + EitherBlockExecutor::Right(b) => b.set_tip(tip), + } + } } impl ExecutorFactory for EitherExecutorFactory @@ -212,8 +251,6 @@ where A: ExecutorFactory, B: ExecutorFactory, { - type Executor = EitherBlockExecutor, B::Executor>; - fn chain_spec(&self) -> &ChainSpec { match self { EitherExecutorFactory::Left(a) => a.chain_spec(), @@ -221,10 +258,13 @@ where } } - fn with_sp(&self, sp: SP) -> Self::Executor { + fn with_state<'a, SP: reth_provider::StateProvider + 'a>( + &'a self, + sp: SP, + ) -> Box { match self { - EitherExecutorFactory::Left(a) => EitherBlockExecutor::Left(a.with_sp(sp)), - EitherExecutorFactory::Right(b) => EitherBlockExecutor::Right(b.with_sp(sp)), + EitherExecutorFactory::Left(a) => a.with_state::<'a, SP>(sp), + EitherExecutorFactory::Right(b) => b.with_state::<'a, SP>(sp), } } } @@ -263,7 +303,7 @@ impl TestConsensusEngineBuilder { } /// Set the executor results to use for the test consensus engine. - pub fn with_executor_results(mut self, executor_results: Vec) -> Self { + pub fn with_executor_results(mut self, executor_results: Vec) -> Self { self.executor_config = TestExecutorConfig::Test(executor_results); self } @@ -343,7 +383,7 @@ where /// Set the executor results to use for the test consensus engine. #[allow(dead_code)] - pub fn with_executor_results(mut self, executor_results: Vec) -> Self { + pub fn with_executor_results(mut self, executor_results: Vec) -> Self { self.base_config.executor_config = TestExecutorConfig::Test(executor_results); self } diff --git a/crates/consensus/common/src/calc.rs b/crates/consensus/common/src/calc.rs index 304a081e5..10e140989 100644 --- a/crates/consensus/common/src/calc.rs +++ b/crates/consensus/common/src/calc.rs @@ -61,7 +61,7 @@ pub fn base_block_reward( /// // The base block reward is 5 ETH, and the ommer inclusion reward is 1/32th of 5 ETH. /// assert_eq!( /// reward.unwrap(), -/// U256::from(ETH_TO_WEI * 5 + ((ETH_TO_WEI * 5) >> 5)) +/// ETH_TO_WEI * 5 + ((ETH_TO_WEI * 5) >> 5) /// ); /// ``` /// @@ -70,8 +70,8 @@ pub fn base_block_reward( /// - Definition: [Yellow Paper][yp] (page 15, 11.3) /// /// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf -pub fn block_reward(base_block_reward: u128, ommers: usize) -> U256 { - U256::from(base_block_reward + (base_block_reward >> 5) * ommers as u128) +pub fn block_reward(base_block_reward: u128, ommers: usize) -> u128 { + base_block_reward + (base_block_reward >> 5) * ommers as u128 } /// Calculate the reward for an ommer. @@ -98,8 +98,8 @@ pub fn ommer_reward( base_block_reward: u128, block_number: BlockNumber, ommer_block_number: BlockNumber, -) -> U256 { - U256::from(((8 + ommer_block_number - block_number) as u128 * base_block_reward) >> 3) +) -> u128 { + ((8 + ommer_block_number - block_number) as u128 * base_block_reward) >> 3 } #[cfg(test)] @@ -139,7 +139,7 @@ mod tests { ]; for (num_ommers, expected_reward) in cases { - assert_eq!(block_reward(base_reward, num_ommers), U256::from(expected_reward)); + assert_eq!(block_reward(base_reward, num_ommers), expected_reward); } } } diff --git a/crates/interfaces/src/blockchain_tree/error.rs b/crates/interfaces/src/blockchain_tree/error.rs index edcdb569b..83bdf0023 100644 --- a/crates/interfaces/src/blockchain_tree/error.rs +++ b/crates/interfaces/src/blockchain_tree/error.rs @@ -189,6 +189,7 @@ impl InsertBlockErrorKind { } // these are internal errors, not caused by an invalid block BlockExecutionError::ProviderError | + BlockExecutionError::Pruning(_) | BlockExecutionError::CanonicalRevert { .. } | BlockExecutionError::CanonicalCommit { .. } | BlockExecutionError::BlockHashNotFoundInChain { .. } | diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index f57d26fb5..cbddf4017 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -1,4 +1,4 @@ -use reth_primitives::{BlockHash, BlockNumHash, Bloom, H256}; +use reth_primitives::{BlockHash, BlockNumHash, Bloom, PrunePartError, H256}; use thiserror::Error; /// Transaction validation errors @@ -9,6 +9,8 @@ pub enum BlockValidationError { EVM { hash: H256, message: String }, #[error("Failed to recover sender for transaction")] SenderRecoveryError, + #[error("Incrementing balance in post execution failed")] + IncrementBalanceFailed, #[error("Receipt root {got:?} is different than expected {expected:?}.")] ReceiptRootDiff { got: H256, expected: H256 }, #[error("Header bloom filter {got:?} is different than expected {expected:?}.")] @@ -18,8 +20,8 @@ pub enum BlockValidationError { transaction_gas_limit: u64, block_available_gas: u64, }, - #[error("Block gas used {got} is different from expected gas used {expected}.")] - BlockGasUsed { got: u64, expected: u64 }, + #[error("Block gas used {got} is different from expected gas used {expected}.\nGas spent by each transaction: {gas_spent_by_tx:?}\n")] + BlockGasUsed { got: u64, expected: u64, gas_spent_by_tx: Vec<(u64, u64)> }, #[error("Block {hash:?} is pre merge")] BlockPreMerge { hash: H256 }, #[error("Missing total difficulty")] @@ -32,6 +34,8 @@ pub enum BlockValidationError { pub enum BlockExecutionError { #[error(transparent)] Validation(#[from] BlockValidationError), + #[error(transparent)] + Pruning(#[from] PrunePartError), // === misc provider error === #[error("Provider error")] diff --git a/crates/payload/basic/Cargo.toml b/crates/payload/basic/Cargo.toml index 5af296de3..c6d505b6d 100644 --- a/crates/payload/basic/Cargo.toml +++ b/crates/payload/basic/Cargo.toml @@ -17,6 +17,7 @@ reth-rlp.workspace = true reth-provider.workspace = true reth-payload-builder.workspace = true reth-tasks.workspace = true +reth-interfaces.workspace = true ## ethereum revm.workspace = true diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 4b125238a..c209e9858 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -16,6 +16,7 @@ use crate::metrics::PayloadBuilderMetrics; use futures_core::ready; use futures_util::FutureExt; +use reth_interfaces::Error; use reth_payload_builder::{ database::CachedReads, error::PayloadBuilderError, BuiltPayload, KeepPayloadJobAlive, PayloadBuilderAttributes, PayloadJob, PayloadJobGenerator, @@ -30,21 +31,18 @@ use reth_primitives::{ proofs, Block, BlockNumberOrTag, ChainSpec, Header, IntoRecoveredTransaction, Receipt, SealedBlock, Withdrawal, EMPTY_OMMER_ROOT, H256, U256, }; -use reth_provider::{BlockReaderIdExt, BlockSource, PostState, StateProviderFactory}; +use reth_provider::{BlockReaderIdExt, BlockSource, BundleStateWithReceipts, StateProviderFactory}; use reth_revm::{ - database::{State, SubState}, - env::tx_env_with_recovered, - executor::{ - commit_state_changes, increment_account_balance, post_block_withdrawals_balance_increments, - }, - into_reth_log, + database::StateProviderDatabase, env::tx_env_with_recovered, into_reth_log, + state_change::post_block_withdrawals_balance_increments, }; use reth_rlp::Encodable; use reth_tasks::TaskSpawner; use reth_transaction_pool::TransactionPool; use revm::{ - db::{CacheDB, DatabaseRef}, + db::states::bundle_state::BundleRetention, primitives::{BlockEnv, CfgEnv, EVMError, Env, InvalidTransaction, ResultAndState}, + Database, DatabaseCommit, State, }; use std::{ future::Future, @@ -640,6 +638,10 @@ where { let BuildArguments { client, pool, mut cached_reads, config, cancel, best_payload } = args; + let state_provider = client.state_by_block_hash(config.parent_block.hash)?; + let state = StateProviderDatabase::new(&state_provider); + let mut db = + State::builder().with_database_ref(cached_reads.as_db(&state)).with_bundle_update().build(); let PayloadConfig { initialized_block_env, initialized_cfg, @@ -650,11 +652,6 @@ where } = config; debug!(parent_hash=?parent_block.hash, parent_number=parent_block.number, "building new payload"); - - let state = State::new(client.state_by_block_hash(parent_block.hash)?); - let mut db = CacheDB::new(cached_reads.as_db(&state)); - let mut post_state = PostState::default(); - let mut cumulative_gas_used = 0; let mut sum_blob_gas_used = 0; let block_gas_limit: u64 = initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX); @@ -667,6 +664,7 @@ where let block_number = initialized_block_env.number.to::(); + let mut receipts = Vec::new(); while let Some(pool_tx) = best_txs.next() { // ensure we still have capacity for this transaction if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { @@ -741,23 +739,19 @@ where }; let gas_used = result.gas_used(); - // commit changes - commit_state_changes(&mut db, &mut post_state, block_number, state, true); + db.commit(state); // add gas used by the transaction to cumulative gas used, before creating the receipt cumulative_gas_used += gas_used; // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt( - block_number, - Receipt { - tx_type: tx.tx_type(), - success: result.is_success(), - cumulative_gas_used, - logs: result.logs().into_iter().map(into_reth_log).collect(), - }, - ); + receipts.push(Some(Receipt { + tx_type: tx.tx_type(), + success: result.is_success(), + cumulative_gas_used, + logs: result.logs().into_iter().map(into_reth_log).collect(), + })); // update add to total fees let miner_fee = @@ -774,20 +768,18 @@ where return Ok(BuildOutcome::Aborted { fees: total_fees, cached_reads }) } - let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals( - &mut db, - &mut post_state, - &chain_spec, - block_number, - attributes.timestamp, - attributes.withdrawals, - )?; + let WithdrawalsOutcome { withdrawals_root, withdrawals } = + commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals)?; - let receipts_root = post_state.receipts_root(block_number); - let logs_bloom = post_state.logs_bloom(block_number); + // merge all transitions into bundle state. + db.merge_transitions(BundleRetention::PlainState); + + let bundle = BundleStateWithReceipts::new(db.take_bundle(), vec![receipts], block_number); + let receipts_root = bundle.receipts_root_slow(block_number).expect("Number is in range"); + let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range"); // calculate the state root - let state_root = state.state().state_root(post_state)?; + let state_root = state_provider.state_root(bundle)?; // create the block header let transactions_root = proofs::calculate_transaction_root(&executed_txs); @@ -844,6 +836,7 @@ where let block = Block { header, body: executed_txs, ommers: vec![], withdrawals }; let sealed_block = block.seal_slow(); + let mut payload = BuiltPayload::new(attributes.id, sealed_block, total_fees); if !blob_sidecars.is_empty() { @@ -874,24 +867,24 @@ where debug!(parent_hash=?parent_block.hash, parent_number=parent_block.number, "building empty payload"); let state = client.state_by_block_hash(parent_block.hash)?; - let mut db = SubState::new(State::new(state)); - let mut post_state = PostState::default(); + let mut db = State::builder() + .with_database_boxed(Box::new(StateProviderDatabase::new(&state))) + .with_bundle_update() + .build(); let base_fee = initialized_block_env.basefee.to::(); let block_number = initialized_block_env.number.to::(); let block_gas_limit: u64 = initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX); - let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals( - &mut db, - &mut post_state, - &chain_spec, - block_number, - attributes.timestamp, - attributes.withdrawals, - )?; + let WithdrawalsOutcome { withdrawals_root, withdrawals } = + commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals)?; + + // merge transition, this will apply the withdrawal balance changes. + db.merge_transitions(BundleRetention::PlainState); // calculate the state root - let state_root = db.db.0.state_root(post_state)?; + let bundle_state = BundleStateWithReceipts::new(db.take_bundle(), vec![], block_number); + let state_root = state.state_root(bundle_state)?; let header = Header { parent_hash: parent_block.hash, @@ -924,6 +917,7 @@ where /// Represents the outcome of committing withdrawals to the runtime database and post state. /// Pre-shanghai these are `None` values. +#[derive(Default)] struct WithdrawalsOutcome { withdrawals: Option>, withdrawals_root: Option, @@ -940,23 +934,17 @@ impl WithdrawalsOutcome { } } -/// Executes the withdrawals and commits them to the _runtime_ Database and PostState. +/// Executes the withdrawals and commits them to the _runtime_ Database and BundleState. /// /// Returns the withdrawals root. /// /// Returns `None` values pre shanghai -#[allow(clippy::too_many_arguments)] -fn commit_withdrawals( - db: &mut CacheDB, - post_state: &mut PostState, +fn commit_withdrawals>( + db: &mut State, chain_spec: &ChainSpec, - block_number: u64, timestamp: u64, withdrawals: Vec, -) -> Result::Error> -where - DB: DatabaseRef, -{ +) -> Result { if !chain_spec.is_shanghai_activated_at_timestamp(timestamp) { return Ok(WithdrawalsOutcome::pre_shanghai()) } @@ -968,9 +956,7 @@ where let balance_increments = post_block_withdrawals_balance_increments(chain_spec, timestamp, &withdrawals); - for (address, increment) in balance_increments { - increment_account_balance(db, post_state, block_number, address, increment)?; - } + db.increment_balances(balance_increments)?; let withdrawals_root = proofs::calculate_withdrawals_root(&withdrawals); diff --git a/crates/payload/builder/src/database.rs b/crates/payload/builder/src/database.rs index c69f7bafa..0de967c57 100644 --- a/crates/payload/builder/src/database.rs +++ b/crates/payload/builder/src/database.rs @@ -12,24 +12,22 @@ use std::{ /// A container type that caches reads from an underlying [DatabaseRef]. /// -/// This is intended to be used in conjunction with [CacheDB](reth_revm_primitives::db::CacheDB) +/// This is intended to be used in conjunction with [State](reth_revm_primitives::db::State) /// during payload building which repeatedly accesses the same data. /// /// # Example /// /// ``` -/// use revm_primitives::db::DatabaseRef; /// use reth_payload_builder::database::CachedReads; -/// use reth_revm_primitives::db::CacheDB; +/// use reth_revm_primitives::db::State; +/// use revm_primitives::db::DatabaseRef; /// /// fn build_payload(db: DB) { -/// let mut cached_reads = CachedReads::default(); -/// let db_ref = cached_reads.as_db(db); -/// -/// // this is `Database` and can be used to build a payload, it never writes to `CachedReads` or the underlying database, but all reads from the underlying database are cached in `CachedReads`. -/// // Subsequent payload build attempts can use cached reads and avoid hitting the underlying database. -/// let db = CacheDB::new(db_ref); -/// +/// let mut cached_reads = CachedReads::default(); +/// let db_ref = cached_reads.as_db(db); +/// // this is `Database` and can be used to build a payload, it never writes to `CachedReads` or the underlying database, but all reads from the underlying database are cached in `CachedReads`. +/// // Subsequent payload build attempts can use cached reads and avoid hitting the underlying database. +/// let db = State::builder().with_database_ref(db_ref).build(); /// } /// ``` #[derive(Debug, Clone, Default)] @@ -121,7 +119,7 @@ impl<'a, DB: DatabaseRef> Database for CachedReadsDbMut<'a, DB> { /// A [DatabaseRef] that caches reads inside [CachedReads]. /// /// This is intended to be used as the [DatabaseRef] for -/// [CacheDB](reth_revm_primitives::db::CacheDB) for repeated payload build jobs. +/// [State](reth_revm_primitives::db::State) for repeated payload build jobs. #[derive(Debug)] pub struct CachedReadsDBRef<'a, DB> { inner: RefCell>, diff --git a/crates/payload/builder/src/payload.rs b/crates/payload/builder/src/payload.rs index 65e7b0ad2..9170bc534 100644 --- a/crates/payload/builder/src/payload.rs +++ b/crates/payload/builder/src/payload.rs @@ -159,12 +159,10 @@ impl PayloadBuilderAttributes { /// NOTE: This is only intended for beacon consensus (after merge). pub fn cfg_and_block_env(&self, chain_spec: &ChainSpec, parent: &Header) -> (CfgEnv, BlockEnv) { // configure evm env based on parent block - let cfg = CfgEnv { - chain_id: U256::from(chain_spec.chain().id()), - // ensure we're not missing any timestamp based hardforks - spec_id: revm_spec_by_timestamp_after_merge(chain_spec, self.timestamp), - ..Default::default() - }; + let mut cfg = CfgEnv::default(); + cfg.chain_id = chain_spec.chain().id(); + // ensure we're not missing any timestamp based hardforks + cfg.spec_id = revm_spec_by_timestamp_after_merge(chain_spec, self.timestamp); let block_env = BlockEnv { number: U256::from(parent.number + 1), diff --git a/crates/primitives/src/account.rs b/crates/primitives/src/account.rs index 708ebd186..876c8e30c 100644 --- a/crates/primitives/src/account.rs +++ b/crates/primitives/src/account.rs @@ -48,9 +48,6 @@ impl Account { /// Bytecode for an account. /// /// A wrapper around [`revm::primitives::Bytecode`][RevmBytecode] with encoding/decoding support. -/// -/// Note: Upon decoding bytecode from the database, you *should* set the code hash using -/// [`Self::with_code_hash`]. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct Bytecode(pub RevmBytecode); @@ -61,18 +58,6 @@ impl Bytecode { pub fn new_raw(bytes: Bytes) -> Self { Self(RevmBytecode::new_raw(bytes)) } - - /// Create new bytecode from raw bytes and its hash. - pub fn new_raw_with_hash(bytes: Bytes, code_hash: H256) -> Self { - let revm_bytecode = unsafe { RevmBytecode::new_raw_with_hash(bytes, code_hash) }; - Self(revm_bytecode) - } - - /// Set the hash of the inner bytecode. - pub fn with_code_hash(mut self, code_hash: H256) -> Self { - self.0.hash = code_hash; - self - } } impl Deref for Bytecode { @@ -121,15 +106,10 @@ impl Compact for Bytecode { let decoded = match variant { 0 => Bytecode(RevmBytecode::new_raw(bytes)), 1 => Bytecode(unsafe { - RevmBytecode::new_checked( - bytes, - buf.read_u64::().unwrap() as usize, - None, - ) + RevmBytecode::new_checked(bytes, buf.read_u64::().unwrap() as usize) }), 2 => Bytecode(RevmBytecode { bytecode: bytes, - hash: KECCAK_EMPTY, state: BytecodeState::Analysed { len: buf.read_u64::().unwrap() as usize, jump_map: JumpMap::from_slice(buf), diff --git a/crates/primitives/src/bits.rs b/crates/primitives/src/bits.rs index 2ffd8f3d4..daa377b47 100644 --- a/crates/primitives/src/bits.rs +++ b/crates/primitives/src/bits.rs @@ -1,4 +1,6 @@ //! Fixed hash types +#![allow(clippy::non_canonical_clone_impl)] + use bytes::Buf; use derive_more::{AsRef, Deref}; use fixed_hash::construct_fixed_hash; diff --git a/crates/primitives/src/bloom.rs b/crates/primitives/src/bloom.rs index b973cef33..947a2a128 100644 --- a/crates/primitives/src/bloom.rs +++ b/crates/primitives/src/bloom.rs @@ -2,6 +2,8 @@ //! //! Adapted from #![allow(missing_docs)] +#![allow(clippy::non_canonical_clone_impl)] + use crate::{impl_fixed_hash_type, keccak256, Log}; use bytes::Buf; use core::{mem, ops}; diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 95fd279d1..cdfef6bb5 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -99,7 +99,7 @@ pub fn calculate_receipt_root(receipts: &[ReceiptWithBloom]) -> H256 { /// Calculates the receipt root for a header for the reference type of [ReceiptWithBloom]. /// /// NOTE: Prefer [calculate_receipt_root] if you have log blooms memoized. -pub fn calculate_receipt_root_ref(receipts: &[T]) -> H256 +pub fn calculate_receipt_root_ref(receipts: &[&T]) -> H256 where for<'a> ReceiptWithBloomRef<'a>: From<&'a T>, { diff --git a/crates/primitives/src/prune/part.rs b/crates/primitives/src/prune/part.rs index 03b64b916..7d1139c25 100644 --- a/crates/primitives/src/prune/part.rs +++ b/crates/primitives/src/prune/part.rs @@ -21,7 +21,7 @@ pub enum PrunePart { } /// PrunePart error type. -#[derive(Debug, Error, PartialEq, Eq)] +#[derive(Debug, Error, PartialEq, Eq, Clone)] pub enum PrunePartError { /// Invalid configuration of a prune part. #[error("The configuration provided for {0} is invalid.")] diff --git a/crates/primitives/src/storage.rs b/crates/primitives/src/storage.rs index 07434f4b6..89879aa49 100644 --- a/crates/primitives/src/storage.rs +++ b/crates/primitives/src/storage.rs @@ -14,6 +14,13 @@ pub struct StorageEntry { pub value: U256, } +impl StorageEntry { + /// Create a new StorageEntry with given key and value. + pub fn new(key: H256, value: U256) -> Self { + Self { key, value } + } +} + impl From<(H256, U256)> for StorageEntry { fn from((key, value): (H256, U256)) -> Self { StorageEntry { key, value } diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 4d2e5d06c..f479ec6f2 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1083,7 +1083,7 @@ impl<'a> arbitrary::Arbitrary<'a> for TransactionSigned { } /// Signed transaction with recovered signer. -#[derive(Debug, Clone, PartialEq, Eq, Hash, AsRef, Deref, Default)] +#[derive(Debug, Clone, PartialEq, Hash, Eq, AsRef, Deref, Default)] pub struct TransactionSignedEcRecovered { /// Signer of the transaction signer: Address, diff --git a/crates/primitives/src/withdrawal.rs b/crates/primitives/src/withdrawal.rs index 098110803..6df6d73ec 100644 --- a/crates/primitives/src/withdrawal.rs +++ b/crates/primitives/src/withdrawal.rs @@ -1,8 +1,7 @@ -use std::mem; - -use crate::{constants::GWEI_TO_WEI, serde_helper::u64_hex, Address, U256}; +use crate::{constants::GWEI_TO_WEI, serde_helper::u64_hex, Address}; use reth_codecs::{main_codec, Compact}; use reth_rlp::{RlpDecodable, RlpEncodable}; +use std::mem; /// Withdrawal represents a validator withdrawal from the consensus layer. #[main_codec] @@ -23,8 +22,8 @@ pub struct Withdrawal { impl Withdrawal { /// Return the withdrawal amount in wei. - pub fn amount_wei(&self) -> U256 { - U256::from(self.amount) * U256::from(GWEI_TO_WEI) + pub fn amount_wei(&self) -> u128 { + self.amount as u128 * GWEI_TO_WEI as u128 } /// Calculate a heuristic for the in-memory size of the [Withdrawal]. diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 037a717d4..c6032f436 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -21,8 +21,4 @@ reth-consensus-common = { path = "../consensus/common" } revm.workspace = true # common -tracing.workspace = true - -[dev-dependencies] -reth-rlp.workspace = true -once_cell = "1.17.0" +tracing.workspace = true \ No newline at end of file diff --git a/crates/revm/revm-inspectors/src/access_list.rs b/crates/revm/revm-inspectors/src/access_list.rs index efe61cc23..49b1913a4 100644 --- a/crates/revm/revm-inspectors/src/access_list.rs +++ b/crates/revm/revm-inspectors/src/access_list.rs @@ -66,7 +66,6 @@ where &mut self, interpreter: &mut Interpreter, _data: &mut EVMData<'_, DB>, - _is_static: bool, ) -> InstructionResult { match interpreter.current_opcode() { opcode::SLOAD | opcode::SSTORE => { diff --git a/crates/revm/revm-inspectors/src/stack/maybe_owned.rs b/crates/revm/revm-inspectors/src/stack/maybe_owned.rs index 897546f16..1449093e2 100644 --- a/crates/revm/revm-inspectors/src/stack/maybe_owned.rs +++ b/crates/revm/revm-inspectors/src/stack/maybe_owned.rs @@ -1,3 +1,4 @@ +use reth_primitives::U256; use revm::{ interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}, primitives::{db::Database, Bytes, B160, B256}, @@ -72,11 +73,10 @@ where &mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>, - is_static: bool, ) -> InstructionResult { match self { MaybeOwnedInspector::Owned(insp) => { - return insp.borrow_mut().initialize_interp(interp, data, is_static) + return insp.borrow_mut().initialize_interp(interp, data) } MaybeOwnedInspector::Stacked(_) => {} } @@ -84,16 +84,9 @@ where InstructionResult::Continue } - fn step( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - ) -> InstructionResult { + fn step(&mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>) -> InstructionResult { match self { - MaybeOwnedInspector::Owned(insp) => { - return insp.borrow_mut().step(interp, data, is_static) - } + MaybeOwnedInspector::Owned(insp) => return insp.borrow_mut().step(interp, data), MaybeOwnedInspector::Stacked(_) => {} } @@ -119,12 +112,11 @@ where &mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>, - is_static: bool, eval: InstructionResult, ) -> InstructionResult { match self { MaybeOwnedInspector::Owned(insp) => { - return insp.borrow_mut().step_end(interp, data, is_static, eval) + return insp.borrow_mut().step_end(interp, data, eval) } MaybeOwnedInspector::Stacked(_) => {} } @@ -136,12 +128,9 @@ where &mut self, data: &mut EVMData<'_, DB>, inputs: &mut CallInputs, - is_static: bool, ) -> (InstructionResult, Gas, Bytes) { match self { - MaybeOwnedInspector::Owned(insp) => { - return insp.borrow_mut().call(data, inputs, is_static) - } + MaybeOwnedInspector::Owned(insp) => return insp.borrow_mut().call(data, inputs), MaybeOwnedInspector::Stacked(_) => {} } @@ -155,11 +144,10 @@ where remaining_gas: Gas, ret: InstructionResult, out: Bytes, - is_static: bool, ) -> (InstructionResult, Gas, Bytes) { match self { MaybeOwnedInspector::Owned(insp) => { - return insp.borrow_mut().call_end(data, inputs, remaining_gas, ret, out, is_static) + return insp.borrow_mut().call_end(data, inputs, remaining_gas, ret, out) } MaybeOwnedInspector::Stacked(_) => {} } @@ -198,10 +186,10 @@ where (ret, address, remaining_gas, out) } - fn selfdestruct(&mut self, contract: B160, target: B160) { + fn selfdestruct(&mut self, contract: B160, target: B160, value: U256) { match self { MaybeOwnedInspector::Owned(insp) => { - return insp.borrow_mut().selfdestruct(contract, target) + return insp.borrow_mut().selfdestruct(contract, target, value) } MaybeOwnedInspector::Stacked(_) => {} } diff --git a/crates/revm/revm-inspectors/src/stack/mod.rs b/crates/revm/revm-inspectors/src/stack/mod.rs index 7144c269d..482e09d4c 100644 --- a/crates/revm/revm-inspectors/src/stack/mod.rs +++ b/crates/revm/revm-inspectors/src/stack/mod.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use reth_primitives::{bytes::Bytes, Address, TxHash, H256}; +use reth_primitives::{bytes::Bytes, Address, TxHash, H256, U256}; use revm::{ inspectors::CustomPrintTracer, interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}, @@ -105,10 +105,9 @@ where &mut self, interpreter: &mut Interpreter, data: &mut EVMData<'_, DB>, - is_static: bool, ) -> InstructionResult { call_inspectors!(inspector, [&mut self.custom_print_tracer], { - let status = inspector.initialize_interp(interpreter, data, is_static); + let status = inspector.initialize_interp(interpreter, data); // Allow inspectors to exit early if status != InstructionResult::Continue { @@ -123,10 +122,9 @@ where &mut self, interpreter: &mut Interpreter, data: &mut EVMData<'_, DB>, - is_static: bool, ) -> InstructionResult { call_inspectors!(inspector, [&mut self.custom_print_tracer], { - let status = inspector.step(interpreter, data, is_static); + let status = inspector.step(interpreter, data); // Allow inspectors to exit early if status != InstructionResult::Continue { @@ -153,11 +151,10 @@ where &mut self, interpreter: &mut Interpreter, data: &mut EVMData<'_, DB>, - is_static: bool, eval: InstructionResult, ) -> InstructionResult { call_inspectors!(inspector, [&mut self.custom_print_tracer], { - let status = inspector.step_end(interpreter, data, is_static, eval); + let status = inspector.step_end(interpreter, data, eval); // Allow inspectors to exit early if status != InstructionResult::Continue { @@ -172,10 +169,9 @@ where &mut self, data: &mut EVMData<'_, DB>, inputs: &mut CallInputs, - is_static: bool, ) -> (InstructionResult, Gas, Bytes) { call_inspectors!(inspector, [&mut self.custom_print_tracer], { - let (status, gas, retdata) = inspector.call(data, inputs, is_static); + let (status, gas, retdata) = inspector.call(data, inputs); // Allow inspectors to exit early if status != InstructionResult::Continue { @@ -193,11 +189,10 @@ where remaining_gas: Gas, ret: InstructionResult, out: Bytes, - is_static: bool, ) -> (InstructionResult, Gas, Bytes) { call_inspectors!(inspector, [&mut self.custom_print_tracer], { let (new_ret, new_gas, new_out) = - inspector.call_end(data, inputs, remaining_gas, ret, out.clone(), is_static); + inspector.call_end(data, inputs, remaining_gas, ret, out.clone()); // If the inspector returns a different ret or a revert with a non-empty message, // we assume it wants to tell us something @@ -247,9 +242,9 @@ where (ret, address, remaining_gas, out) } - fn selfdestruct(&mut self, contract: Address, target: Address) { + fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { call_inspectors!(inspector, [&mut self.custom_print_tracer], { - Inspector::::selfdestruct(inspector, contract, target); + Inspector::::selfdestruct(inspector, contract, target, value); }); } } diff --git a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs index 294caa8c4..b6420c375 100644 --- a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs +++ b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs @@ -424,7 +424,7 @@ impl ParityTraceBuilder { opcode::ADD | opcode::EXP | opcode::CALLER | - opcode::SHA3 | + opcode::KECCAK256 | opcode::SUB | opcode::ADDRESS | opcode::GAS | @@ -535,7 +535,7 @@ where /// /// iteratively fill the [VmTrace] code fields pub(crate) fn populate_vm_trace_bytecodes( - db: &DB, + db: DB, trace: &mut VmTrace, breadth_first_addresses: I, ) -> Result<(), DB::Error> @@ -571,8 +571,8 @@ where /// in the [ExecutionResult] state map and compares the balance and nonce against what's in the /// `db`, which should point to the beginning of the transaction. /// -/// It's expected that `DB` is a [CacheDB](revm::db::CacheDB) which at this point already contains -/// all the accounts that are in the state map and never has to fetch them from disk. +/// It's expected that `DB` is a revm [Database](revm::db::Database) which at this point already +/// contains all the accounts that are in the state map and never has to fetch them from disk. pub fn populate_account_balance_nonce_diffs( state_diff: &mut StateDiff, db: DB, diff --git a/crates/revm/revm-inspectors/src/tracing/fourbyte.rs b/crates/revm/revm-inspectors/src/tracing/fourbyte.rs index ffebf976c..977d2a559 100644 --- a/crates/revm/revm-inspectors/src/tracing/fourbyte.rs +++ b/crates/revm/revm-inspectors/src/tracing/fourbyte.rs @@ -51,7 +51,6 @@ where &mut self, _data: &mut EVMData<'_, DB>, call: &mut CallInputs, - _is_static: bool, ) -> (InstructionResult, Gas, Bytes) { if call.input.len() >= 4 { let selector = Selector::try_from(&call.input[..4]).expect("input is at least 4 bytes"); diff --git a/crates/revm/revm-inspectors/src/tracing/js/mod.rs b/crates/revm/revm-inspectors/src/tracing/js/mod.rs index d88b55ee6..3be256be7 100644 --- a/crates/revm/revm-inspectors/src/tracing/js/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/js/mod.rs @@ -287,12 +287,7 @@ impl Inspector for JsInspector where DB: Database, { - fn step( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - _is_static: bool, - ) -> InstructionResult { + fn step(&mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>) -> InstructionResult { if self.step_fn.is_none() { return InstructionResult::Continue } @@ -331,7 +326,6 @@ where &mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>, - _is_static: bool, eval: InstructionResult, ) -> InstructionResult { if self.step_fn.is_none() { @@ -364,7 +358,6 @@ where &mut self, data: &mut EVMData<'_, DB>, inputs: &mut CallInputs, - _is_static: bool, ) -> (InstructionResult, Gas, Bytes) { self.register_precompiles(&data.precompiles); @@ -408,7 +401,6 @@ where remaining_gas: Gas, ret: InstructionResult, out: Bytes, - _is_static: bool, ) -> (InstructionResult, Gas, Bytes) { if self.exit_fn.is_some() { let frame_result = @@ -476,7 +468,7 @@ where (ret, address, remaining_gas, out) } - fn selfdestruct(&mut self, _contract: B160, _target: B160) { + fn selfdestruct(&mut self, _contract: B160, _target: B160, _value: U256) { if self.enter_fn.is_some() { let call = self.active_call(); let frame = diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index aad2e9d9a..75dfefdc5 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -379,19 +379,13 @@ where &mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>, - is_static: bool, ) -> InstructionResult { - self.gas_inspector.initialize_interp(interp, data, is_static) + self.gas_inspector.initialize_interp(interp, data) } - fn step( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - ) -> InstructionResult { + fn step(&mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>) -> InstructionResult { if self.config.record_steps { - self.gas_inspector.step(interp, data, is_static); + self.gas_inspector.step(interp, data); self.start_step(interp, data); } @@ -420,11 +414,10 @@ where &mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>, - is_static: bool, eval: InstructionResult, ) -> InstructionResult { if self.config.record_steps { - self.gas_inspector.step_end(interp, data, is_static, eval); + self.gas_inspector.step_end(interp, data, eval); self.fill_step_on_step_end(interp, data, eval); } InstructionResult::Continue @@ -434,9 +427,8 @@ where &mut self, data: &mut EVMData<'_, DB>, inputs: &mut CallInputs, - is_static: bool, ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector.call(data, inputs, is_static); + self.gas_inspector.call(data, inputs); // determine correct `from` and `to` based on the call scheme let (from, to) = match inputs.context.scheme { @@ -482,9 +474,8 @@ where gas: Gas, ret: InstructionResult, out: Bytes, - is_static: bool, ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector.call_end(data, inputs, gas, ret, out.clone(), is_static); + self.gas_inspector.call_end(data, inputs, gas, ret, out.clone()); self.fill_trace_on_call_end(data, ret, &gas, out.clone(), None); @@ -546,7 +537,7 @@ where (status, address, gas, retdata) } - fn selfdestruct(&mut self, _contract: Address, target: Address) { + fn selfdestruct(&mut self, _contract: Address, target: Address, _value: U256) { let trace_idx = self.last_trace_idx(); let trace = &mut self.traces.arena[trace_idx].trace; trace.selfdestruct_refund_target = Some(target) diff --git a/crates/revm/revm-inspectors/src/tracing/opcount.rs b/crates/revm/revm-inspectors/src/tracing/opcount.rs index c13154747..623443579 100644 --- a/crates/revm/revm-inspectors/src/tracing/opcount.rs +++ b/crates/revm/revm-inspectors/src/tracing/opcount.rs @@ -29,7 +29,6 @@ where &mut self, _interp: &mut Interpreter, _data: &mut EVMData<'_, DB>, - _is_static: bool, ) -> InstructionResult { self.count += 1; InstructionResult::Continue diff --git a/crates/revm/revm-primitives/src/compat.rs b/crates/revm/revm-primitives/src/compat.rs index 35aaa4eb5..507580c9b 100644 --- a/crates/revm/revm-primitives/src/compat.rs +++ b/crates/revm/revm-primitives/src/compat.rs @@ -24,7 +24,7 @@ pub fn into_reth_log(log: Log) -> RethLog { /// Create reth primitive [Account] from [revm::primitives::AccountInfo]. /// Check if revm bytecode hash is [KECCAK_EMPTY] and put None to reth [Account] -pub fn to_reth_acc(revm_acc: &AccountInfo) -> Account { +pub fn into_reth_acc(revm_acc: AccountInfo) -> Account { let code_hash = revm_acc.code_hash; Account { balance: revm_acc.balance, @@ -32,3 +32,13 @@ pub fn to_reth_acc(revm_acc: &AccountInfo) -> Account { bytecode_hash: (code_hash != KECCAK_EMPTY).then_some(code_hash), } } + +/// Create revm primitive [AccountInfo] from [reth_primitives::Account]. +pub fn into_revm_acc(reth_acc: Account) -> AccountInfo { + AccountInfo { + balance: reth_acc.balance, + nonce: reth_acc.nonce, + code_hash: reth_acc.bytecode_hash.unwrap_or(KECCAK_EMPTY), + code: None, + } +} diff --git a/crates/revm/revm-primitives/src/env.rs b/crates/revm/revm-primitives/src/env.rs index 0d717a9e7..2ddea714d 100644 --- a/crates/revm/revm-primitives/src/env.rs +++ b/crates/revm/revm-primitives/src/env.rs @@ -36,9 +36,8 @@ pub fn fill_cfg_env( }, ); - cfg_env.chain_id = U256::from(chain_spec.chain().id()); + cfg_env.chain_id = chain_spec.chain().id(); cfg_env.spec_id = spec_id; - cfg_env.perf_all_precompiles_have_balance = false; cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse; } diff --git a/crates/revm/src/database.rs b/crates/revm/src/database.rs index 1cbb304ac..030553597 100644 --- a/crates/revm/src/database.rs +++ b/crates/revm/src/database.rs @@ -4,16 +4,20 @@ use reth_provider::StateProvider; use revm::{ db::{CacheDB, DatabaseRef}, primitives::{AccountInfo, Bytecode}, + Database, StateDBBox, }; /// SubState of database. Uses revm internal cache with binding to reth StateProvider trait. -pub type SubState = CacheDB>; +pub type SubState = CacheDB>; + +/// State boxed database with reth Error. +pub type RethStateDBBox<'a> = StateDBBox<'a, Error>; /// Wrapper around StateProvider that implements revm database trait #[derive(Debug, Clone)] -pub struct State(pub DB); +pub struct StateProviderDatabase(pub DB); -impl State { +impl StateProviderDatabase { /// Create new State with generic StateProvider. pub fn new(db: DB) -> Self { Self(db) @@ -35,9 +39,39 @@ impl State { } } -impl DatabaseRef for State { +impl Database for StateProviderDatabase { type Error = Error; + fn basic(&mut self, address: H160) -> Result, Self::Error> { + Ok(self.0.basic_account(address)?.map(|account| AccountInfo { + balance: account.balance, + nonce: account.nonce, + code_hash: account.bytecode_hash.unwrap_or(KECCAK_EMPTY), + code: None, + })) + } + + fn code_by_hash(&mut self, code_hash: H256) -> Result { + let bytecode = self.0.bytecode_by_hash(code_hash)?; + + Ok(bytecode.map(|b| b.0).unwrap_or_else(Bytecode::new)) + } + + fn storage(&mut self, address: H160, index: U256) -> Result { + let index = H256(index.to_be_bytes()); + let ret = self.0.storage(address, index)?.unwrap_or_default(); + Ok(ret) + } + + fn block_hash(&mut self, number: U256) -> Result { + // The `number` represents the block number, so it is safe to cast it to u64. + Ok(self.0.block_hash(number.try_into().unwrap())?.unwrap_or_default()) + } +} + +impl DatabaseRef for StateProviderDatabase { + type Error = ::Error; + fn basic(&self, address: H160) -> Result, Self::Error> { Ok(self.0.basic_account(address)?.map(|account| AccountInfo { balance: account.balance, @@ -51,7 +85,7 @@ impl DatabaseRef for State { let bytecode = self.0.bytecode_by_hash(code_hash)?; if let Some(bytecode) = bytecode { - Ok(bytecode.with_code_hash(code_hash).0) + Ok(bytecode.0) } else { Ok(Bytecode::new()) } diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs deleted file mode 100644 index 6ba25f419..000000000 --- a/crates/revm/src/executor.rs +++ /dev/null @@ -1,1340 +0,0 @@ -use crate::{ - database::SubState, - env::{fill_cfg_and_block_env, fill_tx_env}, - eth_dao_fork::{DAO_HARDFORK_BENEFICIARY, DAO_HARDKFORK_ACCOUNTS}, - into_reth_log, - stack::{InspectorStack, InspectorStackConfig}, - to_reth_acc, -}; -use reth_consensus_common::calc; -use reth_interfaces::executor::{BlockExecutionError, BlockValidationError}; -use reth_primitives::{ - Account, Address, Block, BlockNumber, Bloom, Bytecode, ChainSpec, Hardfork, Header, Receipt, - ReceiptWithBloom, TransactionSigned, Withdrawal, H256, U256, -}; -use reth_provider::{BlockExecutor, PostState, StateProvider}; -use revm::{ - db::{AccountState, CacheDB, DatabaseRef}, - primitives::{ - hash_map::{self, Entry}, - Account as RevmAccount, AccountInfo, ResultAndState, - }, - EVM, -}; -use std::{ - collections::{BTreeMap, HashMap}, - sync::Arc, -}; - -/// Main block executor -pub struct Executor -where - DB: StateProvider, -{ - /// The configured chain-spec - pub chain_spec: Arc, - evm: EVM>, - stack: InspectorStack, -} - -impl From> for Executor -where - DB: StateProvider, -{ - /// Instantiates a new executor from the chainspec. Must call - /// `with_db` to set the database before executing. - fn from(chain_spec: Arc) -> Self { - let evm = EVM::new(); - Executor { chain_spec, evm, stack: InspectorStack::new(InspectorStackConfig::default()) } - } -} - -impl Executor -where - DB: StateProvider, -{ - /// Creates a new executor from the given chain spec and database. - pub fn new(chain_spec: Arc, db: SubState) -> Self { - let mut evm = EVM::new(); - evm.database(db); - - Executor { chain_spec, evm, stack: InspectorStack::new(InspectorStackConfig::default()) } - } - - /// Configures the executor with the given inspectors. - pub fn with_stack(mut self, stack: InspectorStack) -> Self { - self.stack = stack; - self - } - - /// Gives a reference to the database - pub fn db(&mut self) -> &mut SubState { - self.evm.db().expect("db to not be moved") - } - - fn recover_senders( - &self, - body: &[TransactionSigned], - senders: Option>, - ) -> Result, BlockExecutionError> { - if let Some(senders) = senders { - if body.len() == senders.len() { - Ok(senders) - } else { - Err(BlockValidationError::SenderRecoveryError.into()) - } - } else { - TransactionSigned::recover_signers(body, body.len()) - .ok_or(BlockValidationError::SenderRecoveryError.into()) - } - } - - /// Initializes the config and block env. - fn init_env(&mut self, header: &Header, total_difficulty: U256) { - fill_cfg_and_block_env( - &mut self.evm.env.cfg, - &mut self.evm.env.block, - &self.chain_spec, - header, - total_difficulty, - ); - } - - /// Commit change to the run-time database, and update the given [PostState] with the changes - /// made in the transaction, which can be persisted to the database. - fn commit_changes( - &mut self, - block_number: BlockNumber, - changes: hash_map::HashMap, - has_state_clear_eip: bool, - post_state: &mut PostState, - ) { - let db = self.db(); - commit_state_changes(db, post_state, block_number, changes, has_state_clear_eip); - } - - /// Collect all balance changes at the end of the block. - /// - /// Balance changes might include the block reward, uncle rewards, withdrawals, or irregular - /// state changes (DAO fork). - fn post_block_balance_increments(&self, block: &Block, td: U256) -> HashMap { - post_block_balance_increments( - &self.chain_spec, - block.number, - block.difficulty, - block.beneficiary, - block.timestamp, - td, - &block.ommers, - block.withdrawals.as_deref(), - ) - } - - /// Irregular state change at Ethereum DAO hardfork - fn apply_dao_fork_changes( - &mut self, - block_number: BlockNumber, - post_state: &mut PostState, - ) -> Result<(), BlockExecutionError> { - let db = self.db(); - - let mut drained_balance = U256::ZERO; - - // drain all accounts ether - for address in DAO_HARDKFORK_ACCOUNTS { - let db_account = - db.load_account(address).map_err(|_| BlockExecutionError::ProviderError)?; - let old = to_reth_acc(&db_account.info); - // drain balance - drained_balance += core::mem::take(&mut db_account.info.balance); - let new = to_reth_acc(&db_account.info); - // assume it is changeset as it is irregular state change - post_state.change_account(block_number, address, old, new); - } - - // add drained ether to beneficiary. - let beneficiary = DAO_HARDFORK_BENEFICIARY; - self.increment_account_balance(block_number, beneficiary, drained_balance, post_state)?; - - Ok(()) - } - - /// Increment the balance for the given account in the [PostState]. - fn increment_account_balance( - &mut self, - block_number: BlockNumber, - address: Address, - increment: U256, - post_state: &mut PostState, - ) -> Result<(), BlockExecutionError> { - increment_account_balance(self.db(), post_state, block_number, address, increment) - .map_err(|_| BlockExecutionError::ProviderError) - } - - /// Runs a single transaction in the configured environment and proceeds - /// to return the result and state diff (without applying it). - /// - /// Assumes the rest of the block environment has been filled via `init_block_env`. - pub fn transact( - &mut self, - transaction: &TransactionSigned, - sender: Address, - ) -> Result { - // Fill revm structure. - fill_tx_env(&mut self.evm.env.tx, transaction, sender); - - let hash = transaction.hash(); - let out = if self.stack.should_inspect(&self.evm.env, hash) { - // execution with inspector. - let output = self.evm.inspect(&mut self.stack); - tracing::trace!( - target: "evm", - ?hash, ?output, ?transaction, env = ?self.evm.env, - "Executed transaction" - ); - output - } else { - // main execution. - self.evm.transact() - }; - out.map_err(|e| BlockValidationError::EVM { hash, message: format!("{e:?}") }.into()) - } - - /// Runs the provided transactions and commits their state to the run-time database. - /// - /// The returned [PostState] can be used to persist the changes to disk, and contains the - /// changes made by each transaction. - /// - /// The changes in [PostState] have a transition ID associated with them: there is one - /// transition ID for each transaction (with the first executed tx having transition ID 0, and - /// so on). - /// - /// The second returned value represents the total gas used by this block of transactions. - pub fn execute_transactions( - &mut self, - block: &Block, - total_difficulty: U256, - senders: Option>, - ) -> Result<(PostState, u64), BlockExecutionError> { - // perf: do not execute empty blocks - if block.body.is_empty() { - return Ok((PostState::default(), 0)) - } - let senders = self.recover_senders(&block.body, senders)?; - - self.init_env(&block.header, total_difficulty); - - let mut cumulative_gas_used = 0; - let mut post_state = PostState::with_tx_capacity(block.number, block.body.len()); - for (transaction, sender) in block.body.iter().zip(senders) { - // The sum of the transaction’s gas limit, Tg, and the gas utilised in this block prior, - // must be no greater than the block’s gasLimit. - let block_available_gas = block.header.gas_limit - cumulative_gas_used; - if transaction.gas_limit() > block_available_gas { - return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { - transaction_gas_limit: transaction.gas_limit(), - block_available_gas, - } - .into()) - } - // Execute transaction. - let ResultAndState { result, state } = self.transact(transaction, sender)?; - - // commit changes - self.commit_changes( - block.number, - state, - self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), - &mut post_state, - ); - - // append gas used - cumulative_gas_used += result.gas_used(); - - tracing::trace!( - target: "revm::executor", - hash = ?transaction.hash, - gas_used = result.gas_used(), - "transaction executed" - ); - - // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt( - block.number, - Receipt { - tx_type: transaction.tx_type(), - // Success flag was added in `EIP-658: Embedding transaction status code in - // receipts`. - success: result.is_success(), - cumulative_gas_used, - // convert to reth log - logs: result.into_logs().into_iter().map(into_reth_log).collect(), - }, - ); - } - - Ok((post_state, cumulative_gas_used)) - } - - /// Applies the post-block changes, assuming the poststate is generated after executing - /// tranactions - pub fn apply_post_block_changes( - &mut self, - block: &Block, - total_difficulty: U256, - mut post_state: PostState, - ) -> Result { - // Add block rewards - let balance_increments = self.post_block_balance_increments(block, total_difficulty); - for (address, increment) in balance_increments.into_iter() { - self.increment_account_balance(block.number, address, increment, &mut post_state)?; - } - - // Perform DAO irregular state change - if self.chain_spec.fork(Hardfork::Dao).transitions_at_block(block.number) { - self.apply_dao_fork_changes(block.number, &mut post_state)?; - } - Ok(post_state) - } -} - -impl BlockExecutor for Executor -where - DB: StateProvider, -{ - fn execute( - &mut self, - block: &Block, - total_difficulty: U256, - senders: Option>, - ) -> Result { - let (post_state, cumulative_gas_used) = - self.execute_transactions(block, total_difficulty, senders)?; - - // Check if gas used matches the value set in header. - if block.gas_used != cumulative_gas_used { - return Err(BlockValidationError::BlockGasUsed { - got: cumulative_gas_used, - expected: block.gas_used, - } - .into()) - } - - self.apply_post_block_changes(block, total_difficulty, post_state) - } - - fn execute_and_verify_receipt( - &mut self, - block: &Block, - total_difficulty: U256, - senders: Option>, - ) -> Result { - let post_state = self.execute(block, total_difficulty, senders)?; - - // TODO Before Byzantium, receipts contained state root that would mean that expensive - // operation as hashing that is needed for state root got calculated in every - // transaction This was replaced with is_success flag. - // See more about EIP here: https://eips.ethereum.org/EIPS/eip-658 - if self.chain_spec.fork(Hardfork::Byzantium).active_at_block(block.header.number) { - verify_receipt( - block.header.receipts_root, - block.header.logs_bloom, - post_state.receipts(block.number).iter(), - )?; - } - - Ok(post_state) - } -} - -/// Increment the balance for the given account in the [PostState]. -/// -/// Returns an error if the database encountered an error while loading the account. -pub fn increment_account_balance( - db: &mut CacheDB, - post_state: &mut PostState, - block_number: BlockNumber, - address: Address, - increment: U256, -) -> Result<(), ::Error> -where - DB: DatabaseRef, -{ - let beneficiary = db.load_account(address)?; - let old = to_reth_acc(&beneficiary.info); - // Increment beneficiary balance by mutating db entry in place. - beneficiary.info.balance += increment; - let new = to_reth_acc(&beneficiary.info); - match beneficiary.account_state { - AccountState::NotExisting => { - // if account was not existing that means that storage is not - // present. - beneficiary.account_state = AccountState::StorageCleared; - - // if account was not present append `Created` changeset - post_state.create_account( - block_number, - address, - Account { nonce: 0, balance: new.balance, bytecode_hash: None }, - ) - } - - AccountState::StorageCleared | AccountState::Touched | AccountState::None => { - // If account is None that means that EVM didn't touch it. - // we are changing the state to Touched as account can have - // storage in db. - if beneficiary.account_state == AccountState::None { - beneficiary.account_state = AccountState::Touched; - } - // if account was present, append changed changeset. - post_state.change_account(block_number, address, old, new); - } - } - - Ok(()) -} - -/// Commit change to the _run-time_ database [CacheDB], and update the given [PostState] with the -/// changes made in the transaction, which can be persisted to the database. -/// -/// Note: This does _not_ commit to the underlying database [DatabaseRef], but only to the -/// [CacheDB]. -pub fn commit_state_changes( - db: &mut CacheDB, - post_state: &mut PostState, - block_number: BlockNumber, - changes: hash_map::HashMap, - has_state_clear_eip: bool, -) where - DB: DatabaseRef, -{ - // iterate over all changed accounts - for (address, account) in changes { - if account.is_destroyed { - // get old account that we are destroying. - let db_account = match db.accounts.entry(address) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(_entry) => { - panic!("Left panic to critically jumpout if happens, as every account should be hot loaded."); - } - }; - - let account_exists = !matches!(db_account.account_state, AccountState::NotExisting); - if account_exists { - // Insert into `change` a old account and None for new account - // and mark storage to be wiped - post_state.destroy_account(block_number, address, to_reth_acc(&db_account.info)); - } - - // clear cached DB and mark account as not existing - db_account.storage.clear(); - db_account.account_state = AccountState::NotExisting; - db_account.info = AccountInfo::default(); - - continue - } else { - // check if account code is new or old. - // does it exist inside cached contracts if it doesn't it is new bytecode that - // we are inserting inside `change` - if let Some(ref code) = account.info.code { - if !code.is_empty() && !db.contracts.contains_key(&account.info.code_hash) { - db.contracts.insert(account.info.code_hash, code.clone()); - post_state.add_bytecode(account.info.code_hash, Bytecode(code.clone())); - } - } - - // get old account that is going to be overwritten or none if it does not exist - // and get new account that was just inserted. new account mut ref is used for - // inserting storage - let cached_account = match db.accounts.entry(address) { - Entry::Vacant(entry) => { - let entry = entry.insert(Default::default()); - entry.info = account.info.clone(); - entry.account_state = AccountState::NotExisting; // we will promote account state down the road - let new_account = to_reth_acc(&entry.info); - - #[allow(clippy::nonminimal_bool)] - // If account was touched before state clear EIP, create it. - if !has_state_clear_eip || - // If account was touched after state clear EIP, create it only if it is not empty. - (has_state_clear_eip && !new_account.is_empty()) - { - post_state.create_account(block_number, address, new_account); - } - - entry - } - Entry::Occupied(entry) => { - let entry = entry.into_mut(); - - let old_account = to_reth_acc(&entry.info); - let new_account = to_reth_acc(&account.info); - - let account_non_existent = - matches!(entry.account_state, AccountState::NotExisting); - - // Before state clear EIP, create account if it doesn't exist - if (!has_state_clear_eip && account_non_existent) - // After state clear EIP, create account only if it is not empty - || (has_state_clear_eip && entry.info.is_empty() && !new_account.is_empty()) - { - post_state.create_account(block_number, address, new_account); - } else if old_account != new_account { - post_state.change_account( - block_number, - address, - to_reth_acc(&entry.info), - new_account, - ); - } else if has_state_clear_eip && new_account.is_empty() && !account_non_existent - { - // The account was touched, but it is empty, so it should be deleted. - // This also deletes empty accounts which were created before state clear - // EIP. - post_state.destroy_account(block_number, address, new_account); - } - - entry.info = account.info.clone(); - entry - } - }; - - cached_account.account_state = if account.storage_cleared { - cached_account.storage.clear(); - AccountState::StorageCleared - } else if cached_account.account_state.is_storage_cleared() { - // the account already exists and its storage was cleared, preserve its previous - // state - AccountState::StorageCleared - } else if has_state_clear_eip && - matches!(cached_account.account_state, AccountState::NotExisting) && - cached_account.info.is_empty() - { - AccountState::NotExisting - } else { - AccountState::Touched - }; - - // Insert storage. - let mut storage_changeset = BTreeMap::new(); - - // insert storage into new db account. - cached_account.storage.extend(account.storage.into_iter().map(|(key, value)| { - if value.is_changed() { - storage_changeset.insert(key, (value.original_value(), value.present_value())); - } - (key, value.present_value()) - })); - - // Insert into change. - if !storage_changeset.is_empty() { - post_state.change_storage(block_number, address, storage_changeset); - } - } - } -} - -/// Verify receipts -pub fn verify_receipt<'a>( - expected_receipts_root: H256, - expected_logs_bloom: Bloom, - receipts: impl Iterator + Clone, -) -> Result<(), BlockExecutionError> { - // Check receipts root. - let receipts_with_bloom = receipts.map(|r| r.clone().into()).collect::>(); - let receipts_root = reth_primitives::proofs::calculate_receipt_root(&receipts_with_bloom); - if receipts_root != expected_receipts_root { - return Err(BlockValidationError::ReceiptRootDiff { - got: receipts_root, - expected: expected_receipts_root, - } - .into()) - } - - // Create header log bloom. - let logs_bloom = receipts_with_bloom.iter().fold(Bloom::zero(), |bloom, r| bloom | r.bloom); - if logs_bloom != expected_logs_bloom { - return Err(BlockValidationError::BloomLogDiff { - expected: Box::new(expected_logs_bloom), - got: Box::new(logs_bloom), - } - .into()) - } - - Ok(()) -} - -/// Collect all balance changes at the end of the block. -/// -/// Balance changes might include the block reward, uncle rewards, withdrawals, or irregular -/// state changes (DAO fork). -#[allow(clippy::too_many_arguments)] -#[inline] -pub fn post_block_balance_increments( - chain_spec: &ChainSpec, - block_number: u64, - block_difficulty: U256, - beneficiary: Address, - block_timestamp: u64, - total_difficulty: U256, - ommers: &[Header], - withdrawals: Option<&[Withdrawal]>, -) -> HashMap { - let mut balance_increments = HashMap::new(); - - // Add block rewards if they are enabled. - if let Some(base_block_reward) = - calc::base_block_reward(chain_spec, block_number, block_difficulty, total_difficulty) - { - // Ommer rewards - for ommer in ommers { - *balance_increments.entry(ommer.beneficiary).or_default() += - calc::ommer_reward(base_block_reward, block_number, ommer.number); - } - - // Full block reward - *balance_increments.entry(beneficiary).or_default() += - calc::block_reward(base_block_reward, ommers.len()); - } - - // process withdrawals - insert_post_block_withdrawals_balance_increments( - chain_spec, - block_timestamp, - withdrawals, - &mut balance_increments, - ); - - balance_increments -} - -/// Returns a map of addresses to their balance increments if shanghai is active at the given -/// timestamp. -#[inline] -pub fn post_block_withdrawals_balance_increments( - chain_spec: &ChainSpec, - block_timestamp: u64, - withdrawals: &[Withdrawal], -) -> HashMap { - let mut balance_increments = HashMap::with_capacity(withdrawals.len()); - insert_post_block_withdrawals_balance_increments( - chain_spec, - block_timestamp, - Some(withdrawals), - &mut balance_increments, - ); - balance_increments -} - -/// Applies all withdrawal balance increments if shanghai is active at the given timestamp to the -/// given `balance_increments` map. -#[inline] -pub fn insert_post_block_withdrawals_balance_increments( - chain_spec: &ChainSpec, - block_timestamp: u64, - withdrawals: Option<&[Withdrawal]>, - balance_increments: &mut HashMap, -) { - // Process withdrawals - if chain_spec.fork(Hardfork::Shanghai).active_at_timestamp(block_timestamp) { - if let Some(withdrawals) = withdrawals { - for withdrawal in withdrawals { - *balance_increments.entry(withdrawal.address).or_default() += - withdrawal.amount_wei(); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::database::State; - use once_cell::sync::Lazy; - use reth_consensus_common::calc; - use reth_primitives::{ - constants::ETH_TO_WEI, hex_literal::hex, keccak256, Account, Address, BlockNumber, - Bytecode, Bytes, ChainSpecBuilder, ForkCondition, StorageKey, H256, MAINNET, U256, - }; - use reth_provider::{ - post_state::{AccountChanges, Storage, StorageTransition, StorageWipe}, - AccountReader, BlockHashReader, StateProvider, StateRootProvider, - }; - use reth_rlp::Decodable; - use std::{collections::HashMap, str::FromStr}; - - static DEFAULT_REVM_ACCOUNT: Lazy = Lazy::new(|| RevmAccount { - info: AccountInfo::default(), - storage: hash_map::HashMap::default(), - is_destroyed: false, - is_touched: false, - storage_cleared: false, - is_not_existing: false, - }); - - #[derive(Debug, Default, Clone, Eq, PartialEq)] - struct StateProviderTest { - accounts: HashMap, Account)>, - contracts: HashMap, - block_hash: HashMap, - } - - impl StateProviderTest { - /// Insert account. - fn insert_account( - &mut self, - address: Address, - mut account: Account, - bytecode: Option, - storage: HashMap, - ) { - if let Some(bytecode) = bytecode { - let hash = keccak256(&bytecode); - account.bytecode_hash = Some(hash); - self.contracts.insert(hash, Bytecode::new_raw(bytecode.into())); - } - self.accounts.insert(address, (storage, account)); - } - } - - impl AccountReader for StateProviderTest { - fn basic_account(&self, address: Address) -> reth_interfaces::Result> { - let ret = Ok(self.accounts.get(&address).map(|(_, acc)| *acc)); - ret - } - } - - impl BlockHashReader for StateProviderTest { - fn block_hash(&self, number: u64) -> reth_interfaces::Result> { - Ok(self.block_hash.get(&number).cloned()) - } - - fn canonical_hashes_range( - &self, - start: BlockNumber, - end: BlockNumber, - ) -> reth_interfaces::Result> { - let range = start..end; - Ok(self - .block_hash - .iter() - .filter_map(|(block, hash)| range.contains(block).then_some(*hash)) - .collect()) - } - } - - impl StateRootProvider for StateProviderTest { - fn state_root(&self, _post_state: PostState) -> reth_interfaces::Result { - todo!() - } - } - - impl StateProvider for StateProviderTest { - fn storage( - &self, - account: Address, - storage_key: reth_primitives::StorageKey, - ) -> reth_interfaces::Result> { - Ok(self - .accounts - .get(&account) - .and_then(|(storage, _)| storage.get(&storage_key).cloned())) - } - - fn bytecode_by_hash(&self, code_hash: H256) -> reth_interfaces::Result> { - Ok(self.contracts.get(&code_hash).cloned()) - } - - fn proof( - &self, - _address: Address, - _keys: &[H256], - ) -> reth_interfaces::Result<(Vec, H256, Vec>)> { - todo!() - } - } - - #[test] - fn sanity_execution() { - // Got rlp block from: src/GeneralStateTestsFiller/stChainId/chainIdGasCostFiller.json - - let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice(); - let mut block = Block::decode(&mut block_rlp).unwrap(); - - let mut ommer = Header::default(); - let ommer_beneficiary = - Address::from_str("3000000000000000000000000000000000000000").unwrap(); - ommer.beneficiary = ommer_beneficiary; - ommer.number = block.number; - block.ommers = vec![ommer]; - - let mut db = StateProviderTest::default(); - - let account1 = Address::from_str("1000000000000000000000000000000000000000").unwrap(); - let account2 = Address::from_str("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap(); - let account3 = Address::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); - - // pre state - db.insert_account( - account1, - Account { balance: U256::ZERO, nonce: 0x00, bytecode_hash: None }, - Some(hex!("5a465a905090036002900360015500").into()), - HashMap::new(), - ); - - let account3_old_info = Account { - balance: U256::from(0x3635c9adc5dea00000u128), - nonce: 0x00, - bytecode_hash: None, - }; - - db.insert_account( - account3, - Account { - balance: U256::from(0x3635c9adc5dea00000u128), - nonce: 0x00, - bytecode_hash: None, - }, - None, - HashMap::new(), - ); - - // spec at berlin fork - let chain_spec = Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build()); - - let db = SubState::new(State::new(db)); - - // execute chain and verify receipts - let mut executor = Executor::new(chain_spec, db); - let post_state = executor.execute_and_verify_receipt(&block, U256::ZERO, None).unwrap(); - - let base_block_reward = ETH_TO_WEI * 2; - let block_reward = calc::block_reward(base_block_reward, 1); - - let account1_info = Account { balance: U256::ZERO, nonce: 0x00, bytecode_hash: None }; - let account2_info = Account { - // Block reward decrease - balance: U256::from(0x1bc16d674ece94bau128 - 0x1bc16d674ec80000u128), - nonce: 0x00, - bytecode_hash: None, - }; - let account2_info_with_block_reward = Account { - balance: account2_info.balance + block_reward, - nonce: 0x00, - bytecode_hash: None, - }; - let account3_info = Account { - balance: U256::from(0x3635c9adc5de996b46u128), - nonce: 0x01, - bytecode_hash: None, - }; - let ommer_beneficiary_info = Account { - nonce: 0, - balance: calc::ommer_reward(base_block_reward, block.number, block.ommers[0].number), - bytecode_hash: None, - }; - - // Check if cache is set - // account1 - let db = executor.db(); - let cached_acc1 = db.accounts.get(&account1).unwrap(); - assert_eq!(cached_acc1.info.balance, account1_info.balance); - assert_eq!(cached_acc1.info.nonce, account1_info.nonce); - assert_eq!(cached_acc1.account_state, AccountState::Touched); - assert_eq!(cached_acc1.storage.len(), 1); - assert_eq!(cached_acc1.storage.get(&U256::from(1)), Some(&U256::from(2))); - - // account2 Block reward - let cached_acc2 = db.accounts.get(&account2).unwrap(); - assert_eq!(cached_acc2.info.balance, account2_info.balance + block_reward); - assert_eq!(cached_acc2.info.nonce, account2_info.nonce); - assert_eq!(cached_acc2.account_state, AccountState::Touched); - assert_eq!(cached_acc2.storage.len(), 0); - - // account3 - let cached_acc3 = db.accounts.get(&account3).unwrap(); - assert_eq!(cached_acc3.info.balance, account3_info.balance); - assert_eq!(cached_acc3.info.nonce, account3_info.nonce); - assert_eq!(cached_acc3.account_state, AccountState::Touched); - assert_eq!(cached_acc3.storage.len(), 0); - - assert!( - post_state.accounts().get(&account1).is_none(), - "Account should not be present in post-state since it was not changed" - ); - - // Clone and sort to make the test deterministic - assert_eq!( - post_state.account_changes().inner, - BTreeMap::from([( - block.number, - BTreeMap::from([ - // New account - (account2, None), - // Changed account - (account3, Some(account3_old_info)), - // Ommer reward - (ommer_beneficiary, None) - ]) - ),]), - "Account changeset did not match" - ); - assert_eq!( - post_state.storage_changes().inner, - BTreeMap::from([( - block.number, - BTreeMap::from([( - account1, - StorageTransition { - wipe: StorageWipe::None, - // Slot 1 changed from 0 to 2 - storage: BTreeMap::from([(U256::from(1), U256::ZERO)]) - } - )]) - )]), - "Storage changeset did not match" - ); - - // Check final post-state - assert_eq!( - post_state.storage(), - &BTreeMap::from([( - account1, - Storage { - times_wiped: 0, - storage: BTreeMap::from([(U256::from(1), U256::from(2))]) - } - )]), - "Should have changed 1 storage slot" - ); - assert_eq!(post_state.bytecodes().len(), 0, "Should have zero new bytecodes"); - - let accounts = post_state.accounts(); - assert_eq!( - accounts.len(), - 3, - "Should have 4 accounts (account 2, 3 and the ommer beneficiary)" - ); - assert_eq!( - accounts.get(&account2).unwrap(), - &Some(account2_info_with_block_reward), - "Account 2 state is wrong" - ); - assert_eq!( - accounts.get(&account3).unwrap(), - &Some(account3_info), - "Account 3 state is wrong" - ); - assert_eq!( - accounts.get(&ommer_beneficiary).unwrap(), - &Some(ommer_beneficiary_info), - "Ommer beneficiary state is wrong" - ); - } - - #[test] - fn dao_hardfork_irregular_state_change() { - let header = Header { number: 1, ..Header::default() }; - - let mut db = StateProviderTest::default(); - - let mut beneficiary_balance = 0; - for (i, dao_address) in DAO_HARDKFORK_ACCOUNTS.iter().enumerate() { - db.insert_account( - *dao_address, - Account { balance: U256::from(i), nonce: 0x00, bytecode_hash: None }, - None, - HashMap::new(), - ); - beneficiary_balance += i; - } - - let chain_spec = Arc::new( - ChainSpecBuilder::from(&*MAINNET) - .homestead_activated() - .with_fork(Hardfork::Dao, ForkCondition::Block(1)) - .build(), - ); - - let db = SubState::new(State::new(db)); - // execute chain and verify receipts - let mut executor = Executor::new(chain_spec, db); - let out = executor - .execute_and_verify_receipt( - &Block { header, body: vec![], ommers: vec![], withdrawals: None }, - U256::ZERO, - None, - ) - .unwrap(); - - // Check if cache is set - // beneficiary - let db = executor.db(); - let dao_beneficiary = db.accounts.get(&DAO_HARDFORK_BENEFICIARY).unwrap(); - - assert_eq!(dao_beneficiary.info.balance, U256::from(beneficiary_balance)); - for address in DAO_HARDKFORK_ACCOUNTS.iter() { - let account = db.accounts.get(address).unwrap(); - assert_eq!(account.info.balance, U256::ZERO); - } - - // check changesets - let beneficiary_state = out.accounts().get(&DAO_HARDFORK_BENEFICIARY).unwrap().unwrap(); - assert_eq!( - beneficiary_state, - Account { balance: U256::from(beneficiary_balance), ..Default::default() }, - ); - for address in DAO_HARDKFORK_ACCOUNTS.iter() { - let updated_account = out.accounts().get(address).unwrap().unwrap(); - assert_eq!(updated_account, Account { balance: U256::ZERO, ..Default::default() }); - } - } - - #[test] - fn test_selfdestruct() { - // Modified version of eth test. Storage is added for selfdestructed account to see - // that changeset is set. - // Got rlp block from: src/GeneralStateTestsFiller/stArgsZeroOneBalance/suicideNonConst.json - - let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice(); - let block = Block::decode(&mut block_rlp).unwrap(); - let mut db = StateProviderTest::default(); - - let address_caller = Address::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); - let address_selfdestruct = - Address::from_str("095e7baea6a6c7c4c2dfeb977efac326af552d87").unwrap(); - - // pre state - let pre_account_caller = Account { - balance: U256::from(0x0de0b6b3a7640000u64), - nonce: 0x00, - bytecode_hash: None, - }; - - db.insert_account(address_caller, pre_account_caller, None, HashMap::new()); - - // insert account that will selfd - - let pre_account_selfdestroyed = Account { - balance: U256::ZERO, - nonce: 0x00, - bytecode_hash: Some(H256(hex!( - "56a7d44a4ecf086c34482ad1feb1007087fc56fae6dbefbd3f416002933f1705" - ))), - }; - - let selfdestroyed_storage = - BTreeMap::from([(H256::zero(), U256::ZERO), (H256::from_low_u64_be(1), U256::from(1))]); - db.insert_account( - address_selfdestruct, - pre_account_selfdestroyed, - Some(hex!("73095e7baea6a6c7c4c2dfeb977efac326af552d8731ff00").into()), - selfdestroyed_storage.into_iter().collect::>(), - ); - - // spec at berlin fork - let chain_spec = Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build()); - - let db = SubState::new(State::new(db)); - - // execute chain and verify receipts - let mut executor = Executor::new(chain_spec, db); - let out = executor.execute_and_verify_receipt(&block, U256::ZERO, None).unwrap(); - - assert_eq!(out.bytecodes().len(), 0, "Should have zero new bytecodes"); - - let post_account_caller = Account { - balance: U256::from(0x0de0b6b3a761cf60u64), - nonce: 0x01, - bytecode_hash: None, - }; - - assert_eq!( - out.accounts().get(&address_caller).unwrap().unwrap(), - post_account_caller, - "Caller account has changed and fee is deduced" - ); - - assert_eq!( - out.accounts().get(&address_selfdestruct).unwrap(), - &None, - "Selfdestructed account should have been deleted" - ); - assert!( - out.storage().get(&address_selfdestruct).unwrap().wiped(), - "Selfdestructed account should have its storage wiped" - ); - } - - // Test vector from https://github.com/ethereum/tests/blob/3156db5389921125bb9e04142d18e0e7b0cf8d64/BlockchainTests/EIPTests/bc4895-withdrawals/twoIdenticalIndexDifferentValidator.json - #[test] - fn test_withdrawals() { - let block_rlp = hex!("f9028cf90219a0151934ad9b654c50197f37018ee5ee9bb922dec0a1b5e24a6d679cb111cdb107a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa048cd9a5957e45beebf80278a5208b0cbe975ab4b4adb0da1509c67b26f2be3ffa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff8082079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a04a220ebe55034d51f8a58175bb504b6ebf883105010a1f6d42e557c18bbd5d69c0c0f86cda808094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710da028094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710da018094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710da020194c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710"); - let block = Block::decode(&mut block_rlp.as_slice()).unwrap(); - let withdrawals = block.withdrawals.as_ref().unwrap(); - assert_eq!(withdrawals.len(), 4); - - let withdrawal_beneficiary = - Address::from_str("c94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); - - // spec at shanghai fork - let chain_spec = Arc::new(ChainSpecBuilder::mainnet().shanghai_activated().build()); - - let db = SubState::new(State::new(StateProviderTest::default())); - - // execute chain and verify receipts - let mut executor = Executor::new(chain_spec, db); - let out = executor.execute_and_verify_receipt(&block, U256::ZERO, None).unwrap(); - - let withdrawal_sum = withdrawals.iter().fold(U256::ZERO, |sum, w| sum + w.amount_wei()); - let beneficiary_account = executor.db().accounts.get(&withdrawal_beneficiary).unwrap(); - assert_eq!(beneficiary_account.info.balance, withdrawal_sum); - assert_eq!(beneficiary_account.info.nonce, 0); - assert_eq!(beneficiary_account.account_state, AccountState::StorageCleared); - - assert_eq!( - out.accounts().get(&withdrawal_beneficiary).unwrap(), - &Some(Account { nonce: 0, balance: withdrawal_sum, bytecode_hash: None }), - "Withdrawal account should have gotten its balance set" - ); - - // Execute same block again - let out = executor.execute_and_verify_receipt(&block, U256::ZERO, None).unwrap(); - - assert_eq!( - out.accounts().get(&withdrawal_beneficiary).unwrap(), - &Some(Account { - nonce: 0, - balance: withdrawal_sum + withdrawal_sum, - bytecode_hash: None - }), - "Withdrawal account should have gotten its balance set" - ); - } - - #[test] - fn test_account_state_preserved() { - let account = Address::from_str("c94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); - - let mut db = StateProviderTest::default(); - db.insert_account(account, Account::default(), None, HashMap::default()); - - let chain_spec = Arc::new(ChainSpecBuilder::mainnet().istanbul_activated().build()); - let db = SubState::new(State::new(db)); - - let mut executor = Executor::new(chain_spec, db); - // touch account - executor.commit_changes( - 1, - hash_map::HashMap::from([(account, DEFAULT_REVM_ACCOUNT.clone())]), - true, - &mut PostState::default(), - ); - // destroy account - executor.commit_changes( - 1, - hash_map::HashMap::from([( - account, - RevmAccount { - is_destroyed: true, - is_touched: true, - ..DEFAULT_REVM_ACCOUNT.clone() - }, - )]), - true, - &mut PostState::default(), - ); - // re-create account - executor.commit_changes( - 1, - hash_map::HashMap::from([( - account, - RevmAccount { - is_touched: true, - storage_cleared: true, - ..DEFAULT_REVM_ACCOUNT.clone() - }, - )]), - true, - &mut PostState::default(), - ); - // touch account - executor.commit_changes( - 1, - hash_map::HashMap::from([(account, DEFAULT_REVM_ACCOUNT.clone())]), - true, - &mut PostState::default(), - ); - - let db = executor.db(); - - let account = db.load_account(account).unwrap(); - assert_eq!(account.account_state, AccountState::StorageCleared); - } - - /// If the account is created and destroyed within the same transaction, we shouldn't generate - /// the changeset. - #[test] - fn test_account_created_destroyed() { - let address = Address::random(); - - let mut db = SubState::new(State::new(StateProviderTest::default())); - db.load_account(address).unwrap(); // hot load the non-existing account - - let chain_spec = Arc::new(ChainSpecBuilder::mainnet().shanghai_activated().build()); - let mut executor = Executor::new(chain_spec, db); - let mut post_state = PostState::default(); - - executor.commit_changes( - 1, - hash_map::HashMap::from([( - address, - RevmAccount { - is_destroyed: true, - storage_cleared: true, - ..DEFAULT_REVM_ACCOUNT.clone() - }, - )]), - true, - &mut post_state, - ); - - assert!(post_state.account_changes().is_empty()); - } - - /// If the account was touched, but remained unchanged over the course of multiple transactions, - /// no changeset should be generated. - #[test] - fn test_touched_unchanged_account() { - let address = Address::random(); - - let db = SubState::new(State::new(StateProviderTest::default())); - - let chain_spec = Arc::new(ChainSpecBuilder::mainnet().shanghai_activated().build()); - let mut executor = Executor::new(chain_spec, db); - let mut post_state = PostState::default(); - - executor.commit_changes( - 1, - hash_map::HashMap::from([( - address, - RevmAccount { is_touched: true, ..DEFAULT_REVM_ACCOUNT.clone() }, - )]), - true, - &mut post_state, - ); - assert!(post_state.account_changes().is_empty()); - - executor.commit_changes( - 1, - hash_map::HashMap::from([( - address, - RevmAccount { is_touched: true, ..DEFAULT_REVM_ACCOUNT.clone() }, - )]), - true, - &mut post_state, - ); - assert_eq!(post_state.account_changes(), &AccountChanges::default()); - } - - #[test] - fn test_state_clear_eip_touch_account() { - let address = Address::random(); - - let mut state_provider = StateProviderTest::default(); - state_provider.insert_account(address, Account::default(), None, HashMap::default()); - let mut db = SubState::new(State::new(state_provider)); - db.load_account(address).unwrap(); // hot load the account - - let chain_spec = Arc::new(ChainSpecBuilder::mainnet().shanghai_activated().build()); - let mut executor = Executor::new(chain_spec, db); - let mut post_state = PostState::default(); - - // Touch an empty account before state clearing EIP. Nothing should happen. - executor.commit_changes( - 1, - hash_map::HashMap::from([( - address, - RevmAccount { is_touched: true, ..DEFAULT_REVM_ACCOUNT.clone() }, - )]), - false, - &mut post_state, - ); - assert_eq!(post_state.accounts(), &BTreeMap::default()); - assert_eq!(post_state.account_changes(), &AccountChanges::default()); - - // Touch an empty account after state clearing EIP. The account should be destroyed. - executor.commit_changes( - 2, - hash_map::HashMap::from([( - address, - RevmAccount { is_touched: true, ..DEFAULT_REVM_ACCOUNT.clone() }, - )]), - true, - &mut post_state, - ); - assert_eq!(post_state.accounts(), &BTreeMap::from([(address, None)])); - assert_eq!( - post_state.account_changes(), - &AccountChanges { - size: 1, - inner: BTreeMap::from([(2, BTreeMap::from([(address, Some(Account::default()))]))]) - } - ); - } - - #[test] - fn test_state_clear_eip_create_account() { - let address1 = Address::random(); - let address2 = Address::random(); - let address3 = Address::random(); - let address4 = Address::random(); - - let state_provider = StateProviderTest::default(); - let mut db = SubState::new(State::new(state_provider)); - db.load_account(address1).unwrap(); // hot load account 1 - - let chain_spec = Arc::new(ChainSpecBuilder::mainnet().shanghai_activated().build()); - let mut executor = Executor::new(chain_spec, db); - - // Create empty accounts before state clearing EIP. - let mut post_state_before_state_clear = PostState::default(); - executor.commit_changes( - 1, - hash_map::HashMap::from([ - (address1, RevmAccount { is_touched: true, ..DEFAULT_REVM_ACCOUNT.clone() }), - (address2, RevmAccount { is_touched: true, ..DEFAULT_REVM_ACCOUNT.clone() }), - ]), - false, - &mut post_state_before_state_clear, - ); - assert_eq!( - post_state_before_state_clear.accounts(), - &BTreeMap::from([ - (address1, Some(Account::default())), - (address2, Some(Account::default())) - ]) - ); - assert_eq!( - post_state_before_state_clear.account_changes(), - &AccountChanges { - size: 2, - inner: BTreeMap::from([(1, BTreeMap::from([(address1, None), (address2, None)]))]) - } - ); - - // Empty accounts should not be created after state clearing EIP. - let mut post_state_after_state_clear = PostState::default(); - executor.commit_changes( - 2, - hash_map::HashMap::from([ - (address3, RevmAccount { is_touched: true, ..DEFAULT_REVM_ACCOUNT.clone() }), - (address4, RevmAccount { is_touched: true, ..DEFAULT_REVM_ACCOUNT.clone() }), - ]), - true, - &mut post_state_after_state_clear, - ); - assert_eq!(post_state_after_state_clear.accounts(), &BTreeMap::default()); - assert_eq!(post_state_after_state_clear.account_changes(), &AccountChanges::default()); - } -} diff --git a/crates/revm/src/factory.rs b/crates/revm/src/factory.rs index b3a2fe59e..6e326b5cd 100644 --- a/crates/revm/src/factory.rs +++ b/crates/revm/src/factory.rs @@ -1,11 +1,10 @@ use crate::{ - database::{State, SubState}, + database::StateProviderDatabase, + processor::EVMProcessor, stack::{InspectorStack, InspectorStackConfig}, }; use reth_primitives::ChainSpec; -use reth_provider::{ExecutorFactory, StateProvider}; - -use crate::executor::Executor; +use reth_provider::{ExecutorFactory, PrunableBlockExecutor, StateProvider}; use std::sync::Arc; /// Factory that spawn Executor. @@ -35,17 +34,16 @@ impl Factory { } impl ExecutorFactory for Factory { - type Executor = Executor; - - /// Executor with [`StateProvider`] - fn with_sp(&self, sp: SP) -> Self::Executor { - let substate = SubState::new(State::new(sp)); - - let mut executor = Executor::new(self.chain_spec.clone(), substate); + fn with_state<'a, SP: StateProvider + 'a>( + &'a self, + sp: SP, + ) -> Box { + let database_state = StateProviderDatabase::new(sp); + let mut evm = Box::new(EVMProcessor::new_with_db(self.chain_spec.clone(), database_state)); if let Some(ref stack) = self.stack { - executor = executor.with_stack(stack.clone()); + evm.set_stack(stack.clone()); } - executor + evm } /// Return internal chainspec diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 6a99b0203..ac5da0b58 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -17,9 +17,14 @@ pub mod database; /// revm implementation of reth block and transaction executors. -pub mod executor; mod factory; +/// new revm account state executor +pub mod processor; + +/// State changes that are not related to transactions. +pub mod state_change; + /// revm executor factory. pub use factory::Factory; diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs new file mode 100644 index 000000000..9ded17dd1 --- /dev/null +++ b/crates/revm/src/processor.rs @@ -0,0 +1,531 @@ +use crate::{ + database::StateProviderDatabase, + env::{fill_cfg_and_block_env, fill_tx_env}, + eth_dao_fork::{DAO_HARDFORK_BENEFICIARY, DAO_HARDKFORK_ACCOUNTS}, + into_reth_log, + stack::{InspectorStack, InspectorStackConfig}, + state_change::post_block_balance_increments, +}; +use reth_interfaces::{ + executor::{BlockExecutionError, BlockValidationError}, + Error, +}; +use reth_primitives::{ + Address, Block, BlockNumber, Bloom, ChainSpec, Hardfork, Header, PruneMode, PruneModes, + PrunePartError, Receipt, ReceiptWithBloom, TransactionSigned, H256, MINIMUM_PRUNING_DISTANCE, + U256, +}; +use reth_provider::{ + BlockExecutor, BlockExecutorStats, BundleStateWithReceipts, PrunableBlockExecutor, + StateProvider, +}; +use revm::{ + db::{states::bundle_state::BundleRetention, StateDBBox}, + primitives::ResultAndState, + DatabaseCommit, State, EVM, +}; +use std::{sync::Arc, time::Instant}; +use tracing::{debug, trace}; + +/// EVMProcessor is a block executor that uses revm to execute blocks or multiple blocks. +/// +/// Output is obtained by calling `take_output_state` function. +/// +/// It is capable of pruning the data that will be written to the database +/// and implemented [PrunableBlockExecutor] traits. +/// +/// It implemented the [BlockExecutor] that give it the ability to take block +/// apply pre state (Cancun system contract call), execute transaction and apply +/// state change and then apply post execution changes (block reward, withdrawals, irregular DAO +/// hardfork state change). And if `execute_and_verify_receipt` is called it will verify the +/// receipt. +/// +/// InspectorStack are used for optional inspecting execution. And it contains +/// various duration of parts of execution. +pub struct EVMProcessor<'a> { + /// The configured chain-spec + chain_spec: Arc, + /// revm instance that contains database and env environment. + evm: EVM>, + /// Hook and inspector stack that we want to invoke on that hook. + stack: InspectorStack, + /// The collection of receipts. + /// Outer vector stores receipts for each block sequentially. + /// The inner vector stores receipts ordered by transaction number. + /// + /// If receipt is None it means it is pruned. + receipts: Vec>>, + /// First block will be initialized to `None` + /// and be set to the block number of first block executed. + first_block: Option, + /// The maximum known block. + tip: Option, + /// Pruning configuration. + prune_modes: PruneModes, + /// Memoized address pruning filter. + /// Empty implies that there is going to be addresses to include in the filter in a future + /// block. None means there isn't any kind of configuration. + pruning_address_filter: Option<(u64, Vec
)>, + /// Execution stats + stats: BlockExecutorStats, +} + +impl<'a> EVMProcessor<'a> { + /// Return chain spec. + pub fn chain_spec(&self) -> &Arc { + &self.chain_spec + } + + /// Create a new pocessor with the given chain spec. + pub fn new(chain_spec: Arc) -> Self { + let evm = EVM::new(); + EVMProcessor { + chain_spec, + evm, + stack: InspectorStack::new(InspectorStackConfig::default()), + receipts: Vec::new(), + first_block: None, + tip: None, + prune_modes: PruneModes::none(), + pruning_address_filter: None, + stats: BlockExecutorStats::default(), + } + } + + /// Creates a new executor from the given chain spec and database. + pub fn new_with_db( + chain_spec: Arc, + db: StateProviderDatabase, + ) -> Self { + let state = State::builder() + .with_database_boxed(Box::new(db)) + .with_bundle_update() + .without_state_clear() + .build(); + EVMProcessor::new_with_state(chain_spec, state) + } + + /// Create a new EVM processor with the given revm state. + pub fn new_with_state(chain_spec: Arc, revm_state: StateDBBox<'a, Error>) -> Self { + let mut evm = EVM::new(); + evm.database(revm_state); + EVMProcessor { + chain_spec, + evm, + stack: InspectorStack::new(InspectorStackConfig::default()), + receipts: Vec::new(), + first_block: None, + tip: None, + prune_modes: PruneModes::none(), + pruning_address_filter: None, + stats: BlockExecutorStats::default(), + } + } + + /// Configures the executor with the given inspectors. + pub fn set_stack(&mut self, stack: InspectorStack) { + self.stack = stack; + } + + /// Returns a reference to the database + pub fn db_mut(&mut self) -> &mut StateDBBox<'a, Error> { + // Option will be removed from EVM in the future. + // as it is always some. + // https://github.com/bluealloy/revm/issues/697 + self.evm.db().expect("Database inside EVM is always set") + } + + fn recover_senders( + &mut self, + body: &[TransactionSigned], + senders: Option>, + ) -> Result, BlockExecutionError> { + if let Some(senders) = senders { + if body.len() == senders.len() { + Ok(senders) + } else { + Err(BlockValidationError::SenderRecoveryError.into()) + } + } else { + let time = Instant::now(); + let ret = TransactionSigned::recover_signers(body, body.len()) + .ok_or(BlockValidationError::SenderRecoveryError.into()); + self.stats.sender_recovery_duration += time.elapsed(); + ret + } + } + + /// Initializes the config and block env. + fn init_env(&mut self, header: &Header, total_difficulty: U256) { + // Set state clear flag. + let state_clear_flag = + self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(header.number); + + self.db_mut().set_state_clear_flag(state_clear_flag); + + fill_cfg_and_block_env( + &mut self.evm.env.cfg, + &mut self.evm.env.block, + &self.chain_spec, + header, + total_difficulty, + ); + } + + /// Apply post execution state changes, including block rewards, withdrawals, and irregular DAO + /// hardfork state change. + pub fn apply_post_execution_state_change( + &mut self, + block: &Block, + total_difficulty: U256, + ) -> Result<(), BlockExecutionError> { + let mut balance_increments = post_block_balance_increments( + &self.chain_spec, + block.number, + block.difficulty, + block.beneficiary, + block.timestamp, + total_difficulty, + &block.ommers, + block.withdrawals.as_deref(), + ); + + // Irregular state change at Ethereum DAO hardfork + if self.chain_spec.fork(Hardfork::Dao).transitions_at_block(block.number) { + // drain balances from hardcoded addresses. + let drained_balance: u128 = self + .db_mut() + .drain_balances(DAO_HARDKFORK_ACCOUNTS) + .map_err(|_| BlockValidationError::IncrementBalanceFailed)? + .into_iter() + .sum(); + + // return balance to DAO beneficiary. + *balance_increments.entry(DAO_HARDFORK_BENEFICIARY).or_default() += drained_balance; + } + // increment balances + self.db_mut() + .increment_balances(balance_increments.into_iter().map(|(k, v)| (k, v))) + .map_err(|_| BlockValidationError::IncrementBalanceFailed)?; + + Ok(()) + } + + /// Runs a single transaction in the configured environment and proceeds + /// to return the result and state diff (without applying it). + /// + /// Assumes the rest of the block environment has been filled via `init_block_env`. + pub fn transact( + &mut self, + transaction: &TransactionSigned, + sender: Address, + ) -> Result { + // Fill revm structure. + fill_tx_env(&mut self.evm.env.tx, transaction, sender); + + let hash = transaction.hash(); + let out = if self.stack.should_inspect(&self.evm.env, hash) { + // execution with inspector. + let output = self.evm.inspect(&mut self.stack); + tracing::trace!( + target: "evm", + ?hash, ?output, ?transaction, env = ?self.evm.env, + "Executed transaction" + ); + output + } else { + // main execution. + self.evm.transact() + }; + out.map_err(|e| BlockValidationError::EVM { hash, message: format!("{e:?}") }.into()) + } + + /// Runs the provided transactions and commits their state to the run-time database. + /// + /// The returned [BundleStateWithReceipts] can be used to persist the changes to disk, and + /// contains the changes made by each transaction. + /// + /// The changes in [BundleStateWithReceipts] have a transition ID associated with them: there is + /// one transition ID for each transaction (with the first executed tx having transition ID + /// 0, and so on). + /// + /// The second returned value represents the total gas used by this block of transactions. + pub fn execute_transactions( + &mut self, + block: &Block, + total_difficulty: U256, + senders: Option>, + ) -> Result<(Vec, u64), BlockExecutionError> { + // perf: do not execute empty blocks + if block.body.is_empty() { + return Ok((Vec::new(), 0)) + } + + let senders = self.recover_senders(&block.body, senders)?; + + self.init_env(&block.header, total_difficulty); + + let mut cumulative_gas_used = 0; + let mut receipts = Vec::with_capacity(block.body.len()); + for (transaction, sender) in block.body.iter().zip(senders) { + let time = Instant::now(); + // The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior, + // must be no greater than the block’s gasLimit. + let block_available_gas = block.header.gas_limit - cumulative_gas_used; + if transaction.gas_limit() > block_available_gas { + return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { + transaction_gas_limit: transaction.gas_limit(), + block_available_gas, + } + .into()) + } + // Execute transaction. + let ResultAndState { result, state } = self.transact(transaction, sender)?; + trace!( + target: "evm", + ?transaction, ?result, ?state, + "Executed transaction" + ); + self.stats.execution_duration += time.elapsed(); + let time = Instant::now(); + + self.db_mut().commit(state); + + self.stats.apply_state_duration += time.elapsed(); + + // append gas used + cumulative_gas_used += result.gas_used(); + + // Push transaction changeset and calculate header bloom filter for receipt. + receipts.push(Receipt { + tx_type: transaction.tx_type(), + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), + cumulative_gas_used, + // convert to reth log + logs: result.into_logs().into_iter().map(into_reth_log).collect(), + }); + } + + Ok((receipts, cumulative_gas_used)) + } + + /// Execute the block, verify gas usage and apply post-block state changes. + fn execute_inner( + &mut self, + block: &Block, + total_difficulty: U256, + senders: Option>, + ) -> Result, BlockExecutionError> { + let (receipts, cumulative_gas_used) = + self.execute_transactions(block, total_difficulty, senders)?; + + // Check if gas used matches the value set in header. + if block.gas_used != cumulative_gas_used { + return Err(BlockValidationError::BlockGasUsed { + got: cumulative_gas_used, + expected: block.gas_used, + gas_spent_by_tx: self + .receipts + .last() + .map(|block_r| { + block_r + .iter() + .enumerate() + .map(|(id, tx_r)| { + ( + id as u64, + tx_r.as_ref() + .expect("receipts have not been pruned") + .cumulative_gas_used, + ) + }) + .collect() + }) + .unwrap_or_default(), + } + .into()) + } + let time = Instant::now(); + self.apply_post_execution_state_change(block, total_difficulty)?; + self.stats.apply_post_execution_state_changes_duration += time.elapsed(); + + let time = Instant::now(); + let retention = if self.tip.map_or(true, |tip| { + !self.prune_modes.should_prune_account_history(block.number, tip) && + !self.prune_modes.should_prune_storage_history(block.number, tip) + }) { + BundleRetention::Reverts + } else { + BundleRetention::PlainState + }; + self.db_mut().merge_transitions(retention); + self.stats.merge_transitions_duration += time.elapsed(); + + if self.first_block.is_none() { + self.first_block = Some(block.number); + } + + Ok(receipts) + } + + /// Save receipts to the executor. + pub fn save_receipts(&mut self, receipts: Vec) -> Result<(), BlockExecutionError> { + let mut receipts = receipts.into_iter().map(Option::Some).collect(); + // Prune receipts if necessary. + self.prune_receipts(&mut receipts)?; + // Save receipts. + self.receipts.push(receipts); + Ok(()) + } + + /// Prune receipts according to the pruning configuration. + fn prune_receipts( + &mut self, + receipts: &mut Vec>, + ) -> Result<(), PrunePartError> { + let (first_block, tip) = match self.first_block.zip(self.tip) { + Some((block, tip)) => (block, tip), + _ => return Ok(()), + }; + + let block_number = first_block + self.receipts.len() as u64; + + // Block receipts should not be retained + if self.prune_modes.receipts == Some(PruneMode::Full) || + // [`PrunePart::Receipts`] takes priority over [`PrunePart::ContractLogs`] + self.prune_modes.should_prune_receipts(block_number, tip) + { + receipts.clear(); + return Ok(()) + } + + // All receipts from the last 128 blocks are required for blockchain tree, even with + // [`PrunePart::ContractLogs`]. + let prunable_receipts = + PruneMode::Distance(MINIMUM_PRUNING_DISTANCE).should_prune(block_number, tip); + if !prunable_receipts { + return Ok(()) + } + + let contract_log_pruner = self.prune_modes.receipts_log_filter.group_by_block(tip, None)?; + + if !contract_log_pruner.is_empty() { + let (prev_block, filter) = self.pruning_address_filter.get_or_insert((0, Vec::new())); + for (_, addresses) in contract_log_pruner.range(*prev_block..=block_number) { + filter.extend(addresses.iter().copied()); + } + } + + for receipt in receipts.iter_mut() { + let inner_receipt = receipt.as_ref().expect("receipts have not been pruned"); + + // If there is an address_filter, and it does not contain any of the + // contract addresses, then remove this receipts + if let Some((_, filter)) = &self.pruning_address_filter { + if !inner_receipt.logs.iter().any(|log| filter.contains(&log.address)) { + receipt.take(); + } + } + } + + Ok(()) + } +} + +impl<'a> BlockExecutor for EVMProcessor<'a> { + fn execute( + &mut self, + block: &Block, + total_difficulty: U256, + senders: Option>, + ) -> Result<(), BlockExecutionError> { + let receipts = self.execute_inner(block, total_difficulty, senders)?; + self.save_receipts(receipts) + } + + fn execute_and_verify_receipt( + &mut self, + block: &Block, + total_difficulty: U256, + senders: Option>, + ) -> Result<(), BlockExecutionError> { + // execute block + let receipts = self.execute_inner(block, total_difficulty, senders)?; + + // TODO Before Byzantium, receipts contained state root that would mean that expensive + // operation as hashing that is needed for state root got calculated in every + // transaction This was replaced with is_success flag. + // See more about EIP here: https://eips.ethereum.org/EIPS/eip-658 + if self.chain_spec.fork(Hardfork::Byzantium).active_at_block(block.header.number) { + let time = Instant::now(); + if let Err(error) = + verify_receipt(block.header.receipts_root, block.header.logs_bloom, receipts.iter()) + { + debug!(target: "evm", ?error, ?receipts, "receipts verification failed"); + return Err(error) + }; + self.stats.receipt_root_duration += time.elapsed(); + } + + self.save_receipts(receipts) + } + + fn take_output_state(&mut self) -> BundleStateWithReceipts { + let receipts = std::mem::take(&mut self.receipts); + BundleStateWithReceipts::new( + self.evm.db().unwrap().take_bundle(), + receipts, + self.first_block.unwrap_or_default(), + ) + } + + fn stats(&self) -> BlockExecutorStats { + self.stats.clone() + } + + fn size_hint(&self) -> Option { + self.evm.db.as_ref().map(|db| db.bundle_size_hint()) + } +} + +impl<'a> PrunableBlockExecutor for EVMProcessor<'a> { + fn set_tip(&mut self, tip: BlockNumber) { + self.tip = Some(tip); + } + + fn set_prune_modes(&mut self, prune_modes: PruneModes) { + self.prune_modes = prune_modes; + } +} + +/// Verify receipts +pub fn verify_receipt<'a>( + expected_receipts_root: H256, + expected_logs_bloom: Bloom, + receipts: impl Iterator + Clone, +) -> Result<(), BlockExecutionError> { + // Check receipts root. + let receipts_with_bloom = receipts.map(|r| r.clone().into()).collect::>(); + let receipts_root = reth_primitives::proofs::calculate_receipt_root(&receipts_with_bloom); + if receipts_root != expected_receipts_root { + return Err(BlockValidationError::ReceiptRootDiff { + got: receipts_root, + expected: expected_receipts_root, + } + .into()) + } + + // Create header log bloom. + let logs_bloom = receipts_with_bloom.iter().fold(Bloom::zero(), |bloom, r| bloom | r.bloom); + if logs_bloom != expected_logs_bloom { + return Err(BlockValidationError::BloomLogDiff { + expected: Box::new(expected_logs_bloom), + got: Box::new(logs_bloom), + } + .into()) + } + + Ok(()) +} diff --git a/crates/revm/src/state_change.rs b/crates/revm/src/state_change.rs new file mode 100644 index 000000000..6b4cbeff5 --- /dev/null +++ b/crates/revm/src/state_change.rs @@ -0,0 +1,85 @@ +use reth_consensus_common::calc; +use reth_primitives::{Address, ChainSpec, Hardfork, Header, Withdrawal, U256}; +use std::collections::HashMap; + +/// Collect all balance changes at the end of the block. +/// +/// Balance changes might include the block reward, uncle rewards, withdrawals, or irregular +/// state changes (DAO fork). +#[allow(clippy::too_many_arguments)] +#[inline] +pub fn post_block_balance_increments( + chain_spec: &ChainSpec, + block_number: u64, + block_difficulty: U256, + beneficiary: Address, + block_timestamp: u64, + total_difficulty: U256, + ommers: &[Header], + withdrawals: Option<&[Withdrawal]>, +) -> HashMap { + let mut balance_increments = HashMap::new(); + + // Add block rewards if they are enabled. + if let Some(base_block_reward) = + calc::base_block_reward(chain_spec, block_number, block_difficulty, total_difficulty) + { + // Ommer rewards + for ommer in ommers { + *balance_increments.entry(ommer.beneficiary).or_default() += + calc::ommer_reward(base_block_reward, block_number, ommer.number); + } + + // Full block reward + *balance_increments.entry(beneficiary).or_default() += + calc::block_reward(base_block_reward, ommers.len()); + } + + // process withdrawals + insert_post_block_withdrawals_balance_increments( + chain_spec, + block_timestamp, + withdrawals, + &mut balance_increments, + ); + + balance_increments +} + +/// Returns a map of addresses to their balance increments if shanghai is active at the given +/// timestamp. +#[inline] +pub fn post_block_withdrawals_balance_increments( + chain_spec: &ChainSpec, + block_timestamp: u64, + withdrawals: &[Withdrawal], +) -> HashMap { + let mut balance_increments = HashMap::with_capacity(withdrawals.len()); + insert_post_block_withdrawals_balance_increments( + chain_spec, + block_timestamp, + Some(withdrawals), + &mut balance_increments, + ); + balance_increments +} + +/// Applies all withdrawal balance increments if shanghai is active at the given timestamp to the +/// given `balance_increments` map. +#[inline] +pub fn insert_post_block_withdrawals_balance_increments( + chain_spec: &ChainSpec, + block_timestamp: u64, + withdrawals: Option<&[Withdrawal]>, + balance_increments: &mut HashMap, +) { + // Process withdrawals + if chain_spec.fork(Hardfork::Shanghai).active_at_timestamp(block_timestamp) { + if let Some(withdrawals) = withdrawals { + for withdrawal in withdrawals { + *balance_increments.entry(withdrawal.address).or_default() += + withdrawal.amount_wei(); + } + } + } +} diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index a18a5cf6f..264c56736 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -17,7 +17,7 @@ use reth_primitives::{ }; use reth_provider::{BlockReaderIdExt, HeaderProvider, StateProviderBox}; use reth_revm::{ - database::{State, SubState}, + database::{StateProviderDatabase, SubState}, env::tx_env_with_recovered, tracing::{ js::{JsDbRequest, JsInspector}, @@ -96,7 +96,7 @@ where .eth_api .spawn_with_state_at_block(at, move |state| { let mut results = Vec::with_capacity(transactions.len()); - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); let mut transactions = transactions.into_iter().peekable(); while let Some(tx) = transactions.next() { @@ -190,7 +190,7 @@ where // configure env for the target transaction let tx = transaction.into_recovered(); - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); // replay all transactions prior to the targeted transaction replay_transactions_until( &mut db, @@ -289,7 +289,7 @@ where // because JSTracer and all JS types are not Send let (_, _, at) = self.inner.eth_api.evm_env_at(at).await?; let state = self.inner.eth_api.state_at(at)?; - let db = SubState::new(State::new(state)); + let db = SubState::new(StateProviderDatabase::new(state)); let has_state_overrides = overrides.has_state(); // If the caller provided state overrides we need to clone the DB so the js @@ -379,7 +379,7 @@ where .eth_api .spawn_with_state_at_block(at.into(), move |state| { let mut results = Vec::with_capacity(bundles.len()); - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); if replay_block_txs { // only need to replay the transactions in the block if not all transactions are @@ -585,9 +585,15 @@ where let db = if let Some(db) = db { let CacheDB { accounts, contracts, logs, block_hashes, .. } = db; - CacheDB { accounts, contracts, logs, block_hashes, db: State::new(state) } + CacheDB { + accounts, + contracts, + logs, + block_hashes, + db: StateProviderDatabase::new(state), + } } else { - CacheDB::new(State::new(state)) + CacheDB::new(StateProviderDatabase::new(state)) }; let mut stream = ReceiverStream::new(rx); diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index 6f27d1a3b..246ae4050 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -19,7 +19,7 @@ use reth_provider::{ }; use reth_revm::{ access_list::AccessListInspector, - database::{State, SubState}, + database::{StateProviderDatabase, SubState}, env::tx_env_with_recovered, }; use reth_rpc_types::{ @@ -110,7 +110,7 @@ where self.spawn_with_state_at_block(at.into(), move |state| { let mut results = Vec::with_capacity(transactions.len()); - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); if replay_block_txs { // only need to replay the transactions in the block if not all transactions are @@ -199,7 +199,7 @@ where // Configure the evm env let mut env = build_call_evm_env(cfg, block, request)?; - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); // if the request is a simple transfer we can optimize if env.tx.data.is_empty() { @@ -358,7 +358,7 @@ where // env.cfg.disable_base_fee = true; - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); if request.gas.is_none() && env.tx.gas_price > U256::ZERO { // no gas limit was provided in the request, so we need to cap the request's gas limit @@ -400,7 +400,7 @@ where fn map_out_of_gas_err( env_gas_limit: U256, mut env: Env, - mut db: &mut CacheDB>, + mut db: &mut CacheDB>, ) -> EthApiError where S: StateProvider, diff --git a/crates/rpc/rpc/src/eth/api/pending_block.rs b/crates/rpc/rpc/src/eth/api/pending_block.rs index 3700de63d..0cf7c36e1 100644 --- a/crates/rpc/rpc/src/eth/api/pending_block.rs +++ b/crates/rpc/rpc/src/eth/api/pending_block.rs @@ -6,12 +6,10 @@ use reth_primitives::{ proofs, Block, Header, IntoRecoveredTransaction, Receipt, SealedBlock, SealedHeader, EMPTY_OMMER_ROOT, H256, U256, }; -use reth_provider::{PostState, StateProviderFactory}; -use reth_revm::{ - database::State, env::tx_env_with_recovered, executor::commit_state_changes, into_reth_log, -}; +use reth_provider::{BundleStateWithReceipts, StateProviderFactory}; +use reth_revm::{database::StateProviderDatabase, env::tx_env_with_recovered, into_reth_log}; use reth_transaction_pool::TransactionPool; -use revm::db::CacheDB; +use revm::{db::states::bundle_state::BundleRetention, DatabaseCommit, State}; use revm_primitives::{BlockEnv, CfgEnv, EVMError, Env, InvalidTransaction, ResultAndState}; use std::time::Instant; @@ -40,9 +38,9 @@ impl PendingBlockEnv { let Self { cfg, block_env, origin } = self; let parent_hash = origin.build_target_hash(); - let state = State::new(client.history_by_block_hash(parent_hash)?); - let mut db = CacheDB::new(state); - let mut post_state = PostState::default(); + let state_provider = client.history_by_block_hash(parent_hash)?; + let state = StateProviderDatabase::new(&state_provider); + let mut db = State::builder().with_database(Box::new(state)).with_bundle_update().build(); let mut cumulative_gas_used = 0; let block_gas_limit: u64 = block_env.gas_limit.try_into().unwrap_or(u64::MAX); @@ -52,6 +50,8 @@ impl PendingBlockEnv { let mut executed_txs = Vec::new(); let mut best_txs = pool.best_transactions_with_base_fee(base_fee); + let mut receipts = Vec::new(); + while let Some(pool_tx) = best_txs.next() { // ensure we still have capacity for this transaction if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { @@ -93,34 +93,35 @@ impl PendingBlockEnv { } } }; + // commit changes + db.commit(state); let gas_used = result.gas_used(); - // commit changes - commit_state_changes(&mut db, &mut post_state, block_number, state, true); - // add gas used by the transaction to cumulative gas used, before creating the receipt cumulative_gas_used += gas_used; // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt( - block_number, - Receipt { - tx_type: tx.tx_type(), - success: result.is_success(), - cumulative_gas_used, - logs: result.logs().into_iter().map(into_reth_log).collect(), - }, - ); + receipts.push(Some(Receipt { + tx_type: tx.tx_type(), + success: result.is_success(), + cumulative_gas_used, + logs: result.logs().into_iter().map(into_reth_log).collect(), + })); + // append transaction to the list of executed transactions executed_txs.push(tx.into_signed()); } + // merge all transitions into bundle state. + db.merge_transitions(BundleRetention::PlainState); - let receipts_root = post_state.receipts_root(block_number); - let logs_bloom = post_state.logs_bloom(block_number); + let bundle = BundleStateWithReceipts::new(db.take_bundle(), vec![receipts], block_number); + + let receipts_root = bundle.receipts_root_slow(block_number).expect("Block is present"); + let logs_bloom = bundle.block_logs_bloom(block_number).expect("Block is present"); // calculate the state root - let state_root = db.db.state().state_root(post_state)?; + let state_root = state_provider.state_root(bundle)?; // create the block header let transactions_root = proofs::calculate_transaction_root(&executed_txs); diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 3aa07d973..d28a8a960 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -26,7 +26,7 @@ use reth_provider::{ BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory, }; use reth_revm::{ - database::{State, SubState}, + database::{StateProviderDatabase, SubState}, env::{fill_block_env_with_coinbase, tx_env_with_recovered}, tracing::{TracingInspector, TracingInspectorConfig}, }; @@ -43,7 +43,7 @@ use revm::{ use revm_primitives::{utilities::create_address, Env, ResultAndState, SpecId}; /// Helper alias type for the state's [CacheDB] -pub(crate) type StateCacheDB<'r> = CacheDB>>; +pub(crate) type StateCacheDB<'r> = CacheDB>>; /// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace. /// @@ -530,7 +530,7 @@ where .tracing_call_pool .spawn(move || { let state = this.state_at(at)?; - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); let env = prepare_call_env( cfg, @@ -581,7 +581,7 @@ where F: FnOnce(TracingInspector, ResultAndState) -> EthResult, { self.with_state_at_block(at, |state| { - let db = SubState::new(State::new(state)); + let db = SubState::new(StateProviderDatabase::new(state)); let mut inspector = TracingInspector::new(config); let (res, _) = inspect(db, env, &mut inspector)?; @@ -604,7 +604,7 @@ where R: Send + 'static, { self.spawn_with_state_at_block(at, move |state| { - let db = SubState::new(State::new(state)); + let db = SubState::new(StateProviderDatabase::new(state)); let mut inspector = TracingInspector::new(config); let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; @@ -662,7 +662,7 @@ where let block_txs = block.body; self.spawn_with_state_at_block(parent_block.into(), move |state| { - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); // replay all transactions prior to the targeted transaction replay_transactions_until(&mut db, cfg.clone(), block_env.clone(), block_txs, tx.hash)?; diff --git a/crates/rpc/rpc/src/eth/cache/mod.rs b/crates/rpc/rpc/src/eth/cache/mod.rs index 61424c1b1..dfea9821d 100644 --- a/crates/rpc/rpc/src/eth/cache/mod.rs +++ b/crates/rpc/rpc/src/eth/cache/mod.rs @@ -434,7 +434,9 @@ where for block_receipts in receipts { this.on_new_receipts( block_receipts.block_hash, - Ok(Some(block_receipts.receipts)), + Ok(Some( + block_receipts.receipts.into_iter().flatten().collect(), + )), ); } } @@ -460,7 +462,7 @@ enum CacheAction { struct BlockReceipts { block_hash: H256, - receipts: Vec, + receipts: Vec>, } /// Awaits for new chain events and directly inserts them into the cache so they're available @@ -481,7 +483,7 @@ where for block in &blocks { let block_receipts = BlockReceipts { block_hash: block.hash, - receipts: state.receipts(block.number).to_vec(), + receipts: state.receipts_by_block(block.number).to_vec(), }; receipts.push(block_receipts); } diff --git a/crates/rpc/rpc/src/eth/error.rs b/crates/rpc/rpc/src/eth/error.rs index 6062b0010..29b5d3aac 100644 --- a/crates/rpc/rpc/src/eth/error.rs +++ b/crates/rpc/rpc/src/eth/error.rs @@ -278,6 +278,9 @@ pub enum RpcInvalidTransactionError { /// The transaction is before Spurious Dragon and has a chain ID #[error("Transactions before Spurious Dragon should not have a chain ID.")] OldLegacyChainId, + /// The transitions is before Berlin and has access list + #[error("Transactions before Berlin should not have access list")] + AccessListNotSupported, } impl RpcInvalidTransactionError { @@ -350,7 +353,7 @@ impl From for RpcInvalidTransactionError { RpcInvalidTransactionError::GasTooHigh } InvalidTransaction::RejectCallerWithCode => RpcInvalidTransactionError::SenderNoEOA, - InvalidTransaction::LackOfFundForGasLimit { .. } => { + InvalidTransaction::LackOfFundForMaxFee { .. } => { RpcInvalidTransactionError::InsufficientFunds } InvalidTransaction::OverflowPaymentInTransaction => { @@ -364,6 +367,9 @@ impl From for RpcInvalidTransactionError { } InvalidTransaction::NonceTooHigh { .. } => RpcInvalidTransactionError::NonceTooHigh, InvalidTransaction::NonceTooLow { .. } => RpcInvalidTransactionError::NonceTooLow, + InvalidTransaction::AccessListNotSupported => { + RpcInvalidTransactionError::AccessListNotSupported + } } } } diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index ed67c66c6..260bb6196 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -16,7 +16,7 @@ use reth_provider::{ BlockReader, ChainSpecProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory, }; use reth_revm::{ - database::{State, SubState}, + database::{StateProviderDatabase, SubState}, env::tx_env_with_recovered, tracing::{ parity::populate_account_balance_nonce_diffs, TracingInspector, TracingInspectorConfig, @@ -145,7 +145,7 @@ where .eth_api .spawn_with_state_at_block(at, move |state| { let mut results = Vec::with_capacity(calls.len()); - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); let mut calls = calls.into_iter().peekable(); @@ -278,8 +278,8 @@ where /// 2. configures the EVM evn /// 3. loops over all transactions and executes them /// 4. calls the callback with the transaction info, the execution result, the changed state - /// _after_ the transaction [State] and the database that points to the state right _before_ the - /// transaction. + /// _after_ the transaction [StateProviderDatabase] and the database that points to the state + /// right _before_ the transaction. async fn trace_block_with( &self, block_id: BlockId, @@ -293,7 +293,7 @@ where TracingInspector, ExecutionResult, &'a revm_primitives::State, - &'a CacheDB>>, + &'a CacheDB>>, ) -> EthResult + Send + 'static, @@ -321,7 +321,7 @@ where .eth_api .spawn_with_state_at_block(state_at.into(), move |state| { let mut results = Vec::with_capacity(transactions.len()); - let mut db = SubState::new(State::new(state)); + let mut db = SubState::new(StateProviderDatabase::new(state)); let mut transactions = transactions.into_iter().enumerate().peekable(); @@ -404,8 +404,10 @@ where RewardAction { author: block.header.beneficiary, reward_type: RewardType::Uncle, - value: block_reward(base_block_reward, block.ommers.len()) - - U256::from(base_block_reward), + value: U256::from( + block_reward(base_block_reward, block.ommers.len()) - + base_block_reward, + ), }, )); } diff --git a/crates/stages/Cargo.toml b/crates/stages/Cargo.toml index a1770fa76..6f8ef3482 100644 --- a/crates/stages/Cargo.toml +++ b/crates/stages/Cargo.toml @@ -23,6 +23,9 @@ reth-codecs = { path = "../storage/codecs" } reth-provider.workspace = true reth-trie = { path = "../trie" } +# revm +revm.workspace = true + # async tokio = { workspace = true, features = ["sync"] } tokio-stream.workspace = true diff --git a/crates/stages/src/pipeline/mod.rs b/crates/stages/src/pipeline/mod.rs index d15fefacf..8b617a178 100644 --- a/crates/stages/src/pipeline/mod.rs +++ b/crates/stages/src/pipeline/mod.rs @@ -424,7 +424,7 @@ where .max(1); Ok(ControlFlow::Unwind { target: unwind_to, bad_block: local_head }) } else if let StageError::Validation { block, error } = err { - warn!( + error!( target: "sync::pipeline", stage = %stage_id, bad_block = %block.number, @@ -456,7 +456,7 @@ where error: BlockExecutionError::Validation(error), } = err { - warn!( + error!( target: "sync::pipeline", stage = %stage_id, bad_block = %block.number, diff --git a/crates/stages/src/stages/execution.rs b/crates/stages/src/stages/execution.rs index ef9e1ce49..e2e57810d 100644 --- a/crates/stages/src/stages/execution.rs +++ b/crates/stages/src/stages/execution.rs @@ -18,10 +18,13 @@ use reth_primitives::{ BlockNumber, Header, PruneModes, U256, }; use reth_provider::{ - post_state::PostState, BlockExecutor, BlockReader, DatabaseProviderRW, ExecutorFactory, - HeaderProvider, LatestStateProviderRef, ProviderError, + BlockReader, DatabaseProviderRW, ExecutorFactory, HeaderProvider, LatestStateProviderRef, + OriginalValuesKnown, ProviderError, +}; +use std::{ + ops::RangeInclusive, + time::{Duration, Instant}, }; -use std::{ops::RangeInclusive, time::Instant}; use tracing::*; /// The execution stage executes all transactions and @@ -120,18 +123,24 @@ impl ExecutionStage { // Build executor let mut executor = - self.executor_factory.with_sp(LatestStateProviderRef::new(provider.tx_ref())); + self.executor_factory.with_state(LatestStateProviderRef::new(provider.tx_ref())); + executor.set_prune_modes(prune_modes); + executor.set_tip(max_block); // Progress tracking let mut stage_progress = start_block; let mut stage_checkpoint = execution_checkpoint(provider, start_block, max_block, input.checkpoint())?; + let mut fetch_block_duration = Duration::default(); + let mut execution_duration = Duration::default(); + debug!(target: "sync::stages::execution", start = start_block, end = max_block, "Executing range"); // Execute block range - let mut state = PostState::default(); - state.add_prune_modes(prune_modes); + + let mut cumulative_gas = 0; for block_number in start_block..=max_block { + let time = Instant::now(); let td = provider .header_td_by_number(block_number)? .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?; @@ -139,17 +148,21 @@ impl ExecutionStage { .block_with_senders(block_number)? .ok_or_else(|| ProviderError::BlockNotFound(block_number.into()))?; + fetch_block_duration += time.elapsed(); + + cumulative_gas += block.gas_used; + // Configure the executor to use the current state. trace!(target: "sync::stages::execution", number = block_number, txs = block.body.len(), "Executing block"); + let time = Instant::now(); // Execute the block let (block, senders) = block.into_components(); - let block_state = executor - .execute_and_verify_receipt(&block, td, Some(senders)) - .map_err(|error| StageError::ExecutionError { - block: block.header.clone().seal_slow(), - error, - })?; + executor.execute_and_verify_receipt(&block, td, Some(senders)).map_err(|error| { + StageError::ExecutionError { block: block.header.clone().seal_slow(), error } + })?; + + execution_duration += time.elapsed(); // Gas metrics if let Some(metrics_tx) = &mut self.metrics_tx { @@ -157,23 +170,32 @@ impl ExecutionStage { metrics_tx.send(MetricEvent::ExecutionStageGas { gas: block.header.gas_used }); } - // Merge state changes - state.extend(block_state); stage_progress = block_number; + stage_checkpoint.progress.processed += block.gas_used; // Check if we should commit now - if self.thresholds.is_end_of_batch(block_number - start_block, state.size_hint() as u64) - { + let bundle_size_hint = executor.size_hint().unwrap_or_default() as u64; + if self.thresholds.is_end_of_batch( + block_number - start_block, + bundle_size_hint, + cumulative_gas, + ) { break } } + let time = Instant::now(); + let state = executor.take_output_state(); + let write_preparation_duration = time.elapsed(); - // Write remaining changes - trace!(target: "sync::stages::execution", accounts = state.accounts().len(), "Writing updated state to database"); - let start = Instant::now(); - state.write_to_db(provider.tx_ref(), max_block)?; - trace!(target: "sync::stages::execution", took = ?start.elapsed(), "Wrote state"); + let time = Instant::now(); + // write output + state.write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes)?; + let db_write_duration = time.elapsed(); + info!(target: "sync::stages::execution", block_fetch=?fetch_block_duration, execution=?execution_duration, + write_preperation=?write_preparation_duration, write=?db_write_duration, " Execution duration."); + + executor.stats().log_info(); let done = stage_progress == max_block; Ok(ExecOutput { @@ -442,26 +464,42 @@ impl Stage for ExecutionStage { /// /// If either of the thresholds (`max_blocks` and `max_changes`) are hit, then the execution stage /// commits all pending changes to the database. -#[derive(Debug)] +/// +/// A third threshold, `max_changesets`, can be set to periodically write changesets to the +/// current database transaction, which frees up memory. +#[derive(Debug, Clone)] pub struct ExecutionStageThresholds { /// The maximum number of blocks to process before the execution stage commits. pub max_blocks: Option, /// The maximum amount of state changes to keep in memory before the execution stage commits. pub max_changes: Option, + /// The maximum amount of cumultive gas used in the batch. + pub max_cumulative_gas: Option, } impl Default for ExecutionStageThresholds { fn default() -> Self { - Self { max_blocks: Some(500_000), max_changes: Some(5_000_000) } + Self { + max_blocks: Some(500_000), + max_changes: Some(5_000_000), + // 30M block per gas on 50k blocks + max_cumulative_gas: Some(30_000_000 * 50_000), + } } } impl ExecutionStageThresholds { /// Check if the batch thresholds have been hit. #[inline] - pub fn is_end_of_batch(&self, blocks_processed: u64, changes_processed: u64) -> bool { + pub fn is_end_of_batch( + &self, + blocks_processed: u64, + changes_processed: u64, + cumulative_gas_used: u64, + ) -> bool { blocks_processed >= self.max_blocks.unwrap_or(u64::MAX) || - changes_processed >= self.max_changes.unwrap_or(u64::MAX) + changes_processed >= self.max_changes.unwrap_or(u64::MAX) || + cumulative_gas_used >= self.max_cumulative_gas.unwrap_or(u64::MAX) } } @@ -485,7 +523,11 @@ mod tests { Factory::new(Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build())); ExecutionStage::new( factory, - ExecutionStageThresholds { max_blocks: Some(100), max_changes: None }, + ExecutionStageThresholds { + max_blocks: Some(100), + max_changes: None, + max_cumulative_gas: None, + }, MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD, PruneModes::none(), ) diff --git a/crates/stages/src/stages/mod.rs b/crates/stages/src/stages/mod.rs index 70ce03c50..deb25b3c0 100644 --- a/crates/stages/src/stages/mod.rs +++ b/crates/stages/src/stages/mod.rs @@ -66,6 +66,7 @@ mod tests { use std::sync::Arc; #[tokio::test] + #[ignore] async fn test_prune() { let test_tx = TestTransaction::default(); let factory = Arc::new(ProviderFactory::new(test_tx.tx.as_ref(), MAINNET.clone())); @@ -129,7 +130,11 @@ mod tests { // configuration let mut execution_stage = ExecutionStage::new( Factory::new(Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build())), - ExecutionStageThresholds { max_blocks: Some(100), max_changes: None }, + ExecutionStageThresholds { + max_blocks: Some(100), + max_changes: None, + max_cumulative_gas: None, + }, MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD, prune_modes.clone(), ); diff --git a/crates/stages/src/stages/tx_lookup.rs b/crates/stages/src/stages/tx_lookup.rs index 43700ab3d..d0bdacb6e 100644 --- a/crates/stages/src/stages/tx_lookup.rs +++ b/crates/stages/src/stages/tx_lookup.rs @@ -120,7 +120,6 @@ impl Stage for TransactionLookupStage { } }); } - let mut tx_list = Vec::with_capacity(transaction_count); // Iterate over channels and append the tx hashes to be sorted out later @@ -147,7 +146,6 @@ impl Stage for TransactionLookupStage { .unwrap_or_default(); // if txhash_cursor.last() is None we will do insert. `zip` would return none if any item is // none. if it is some and if first is smaller than last, we will do append. - for (tx_hash, id) in tx_list { if insert { txhash_cursor.insert(tx_hash, id)?; diff --git a/crates/storage/provider/Cargo.toml b/crates/storage/provider/Cargo.toml index 45382391b..32732f574 100644 --- a/crates/storage/provider/Cargo.toml +++ b/crates/storage/provider/Cargo.toml @@ -27,16 +27,19 @@ tracing.workspace = true auto_impl = "1.0" itertools.workspace = true pin-project.workspace = true -derive_more = "0.99" parking_lot.workspace = true # test-utils reth-rlp = { workspace = true, optional = true } +# parallel utils +rayon = "1.7" + [dev-dependencies] reth-db = { path = "../db", features = ["test-utils"] } reth-primitives = { workspace = true, features = ["arbitrary", "test-utils"] } reth-rlp.workspace = true +revm.workspace = true reth-trie = { path = "../../trie", features = ["test-utils"] } reth-interfaces = { workspace = true, features = ["test-utils"] } parking_lot.workspace = true diff --git a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs new file mode 100644 index 000000000..5936cb940 --- /dev/null +++ b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs @@ -0,0 +1,1198 @@ +use reth_db::{ + cursor::{DbCursorRO, DbCursorRW}, + tables, + transaction::{DbTx, DbTxMut}, +}; +use reth_interfaces::db::DatabaseError; +use reth_primitives::{ + bloom::logs_bloom, keccak256, proofs::calculate_receipt_root_ref, Account, Address, + BlockNumber, Bloom, Bytecode, Log, Receipt, StorageEntry, H256, U256, +}; +use reth_revm_primitives::{ + db::states::BundleState, into_reth_acc, into_revm_acc, primitives::AccountInfo, +}; +use reth_trie::{ + hashed_cursor::{HashedPostState, HashedPostStateCursorFactory, HashedStorage}, + StateRoot, StateRootError, +}; +use std::collections::HashMap; + +pub use reth_revm_primitives::db::states::OriginalValuesKnown; + +use crate::{StateChanges, StateReverts}; + +/// Bundle state of post execution changes and reverts +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct BundleStateWithReceipts { + /// Bundle state with reverts. + bundle: BundleState, + /// The collection of receipts. + /// Outer vector stores receipts for each block sequentially. + /// The inner vector stores receipts ordered by transaction number. + /// + /// If receipt is None it means it is pruned. + receipts: Vec>>, + /// First block of bundle state. + first_block: BlockNumber, +} + +/// Type used to initialize revms bundle state. +pub type BundleStateInit = + HashMap, Option, HashMap)>; + +/// Types used inside RevertsInit to initialize revms reverts. +pub type AccountRevertInit = (Option>, Vec); + +/// Type used to initialize revms reverts. +pub type RevertsInit = HashMap>; + +impl BundleStateWithReceipts { + /// Create Bundle State. + pub fn new( + bundle: BundleState, + receipts: Vec>>, + first_block: BlockNumber, + ) -> Self { + Self { bundle, receipts, first_block } + } + + /// Create new bundle state with receipts. + pub fn new_init( + state_init: BundleStateInit, + revert_init: RevertsInit, + contracts_init: Vec<(H256, Bytecode)>, + receipts: Vec>>, + first_block: BlockNumber, + ) -> Self { + // sort reverts by block number + let mut reverts = revert_init.into_iter().collect::>(); + reverts.sort_unstable_by_key(|a| a.0); + + // initialize revm bundle + let bundle = BundleState::new( + state_init.into_iter().map(|(address, (original, present, storage))| { + ( + address, + original.map(into_revm_acc), + present.map(into_revm_acc), + storage.into_iter().map(|(k, s)| (k.into(), s)).collect(), + ) + }), + reverts.into_iter().map(|(_, reverts)| { + // does not needs to be sorted, it is done when taking reverts. + reverts.into_iter().map(|(address, (original, storage))| { + ( + address, + original.map(|i| i.map(into_revm_acc)), + storage.into_iter().map(|entry| (entry.key.into(), entry.value)), + ) + }) + }), + contracts_init.into_iter().map(|(code_hash, bytecode)| (code_hash, bytecode.0)), + ); + + Self { bundle, receipts, first_block } + } + + /// Return revm bundle state. + pub fn state(&self) -> &BundleState { + &self.bundle + } + + /// Set first block. + pub fn set_first_block(&mut self, first_block: BlockNumber) { + self.first_block = first_block; + } + + /// Return iterator over all accounts + pub fn accounts_iter(&self) -> impl Iterator)> { + self.bundle.state().iter().map(|(a, acc)| (*a, acc.info.as_ref())) + } + + /// Get account if account is known. + pub fn account(&self, address: &Address) -> Option> { + self.bundle.account(address).map(|a| a.info.clone().map(into_reth_acc)) + } + + /// Get storage if value is known. + /// + /// This means that depending on status we can potentially return U256::ZERO. + pub fn storage(&self, address: &Address, storage_key: U256) -> Option { + self.bundle.account(address).and_then(|a| a.storage_slot(storage_key)) + } + + /// Return bytecode if known. + pub fn bytecode(&self, code_hash: &H256) -> Option { + self.bundle.bytecode(code_hash).map(Bytecode) + } + + /// Hash all changed accounts and storage entries that are currently stored in the post state. + /// + /// # Returns + /// + /// The hashed post state. + pub fn hash_state_slow(&self) -> HashedPostState { + //let mut storages = BTreeMap::default(); + let mut hashed_state = HashedPostState::default(); + + for (address, account) in self.bundle.state() { + let hashed_address = keccak256(address); + if let Some(account) = &account.info { + hashed_state.insert_account(hashed_address, into_reth_acc(account.clone())) + } else { + hashed_state.insert_cleared_account(hashed_address); + } + + // insert storage. + let mut hashed_storage = HashedStorage::new(account.status.was_destroyed()); + + for (key, value) in account.storage.iter() { + let hashed_key = keccak256(H256(key.to_be_bytes())); + if value.present_value == U256::ZERO { + hashed_storage.insert_zero_valued_slot(hashed_key); + } else { + hashed_storage.insert_non_zero_valued_storage(hashed_key, value.present_value); + } + } + hashed_state.insert_hashed_storage(hashed_address, hashed_storage) + } + hashed_state.sorted() + } + + /// Calculate the state root for this [BundleState]. + /// Internally, function calls [Self::hash_state_slow] to obtain the [HashedPostState]. + /// Afterwards, it retrieves the prefixsets from the [HashedPostState] and uses them to + /// calculate the incremental state root. + /// + /// # Example + /// + /// ``` + /// use reth_primitives::{Account, U256}; + /// use reth_provider::BundleStateWithReceipts; + /// use reth_db::{test_utils::create_test_rw_db, database::Database}; + /// use std::collections::HashMap; + /// + /// // Initialize the database + /// let db = create_test_rw_db(); + /// + /// // Initialize the bundle state + /// let bundle = BundleStateWithReceipts::new_init( + /// HashMap::from([( + /// [0x11;20].into(), + /// ( + /// None, + /// Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }), + /// HashMap::from([]), + /// ), + /// )]), + /// HashMap::from([]), + /// vec![], + /// vec![], + /// 0, + /// ); + /// + /// // Calculate the state root + /// let tx = db.tx().expect("failed to create transaction"); + /// let state_root = bundle.state_root_slow(&tx); + /// ``` + /// + /// # Returns + /// + /// The state root for this [BundleState]. + pub fn state_root_slow<'a, 'tx, TX: DbTx<'tx>>( + &self, + tx: &'a TX, + ) -> Result { + let hashed_post_state = self.hash_state_slow(); + let (account_prefix_set, storage_prefix_set) = hashed_post_state.construct_prefix_sets(); + let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, &hashed_post_state); + StateRoot::new(tx) + .with_hashed_cursor_factory(&hashed_cursor_factory) + .with_changed_account_prefixes(account_prefix_set) + .with_changed_storage_prefixes(storage_prefix_set) + .root() + } + + /// Transform block number to the index of block. + fn block_number_to_index(&self, block_number: BlockNumber) -> Option { + if self.first_block > block_number { + return None + } + let index = block_number - self.first_block; + if index >= self.receipts.len() as u64 { + return None + } + Some(index as usize) + } + + /// Returns an iterator over all block logs. + pub fn logs(&self, block_number: BlockNumber) -> Option> { + let index = self.block_number_to_index(block_number)?; + Some(self.receipts[index].iter().filter_map(|r| Some(r.as_ref()?.logs.iter())).flatten()) + } + + /// Return blocks logs bloom + pub fn block_logs_bloom(&self, block_number: BlockNumber) -> Option { + Some(logs_bloom(self.logs(block_number)?)) + } + + /// Returns the receipt root for all recorded receipts. + /// Note: this function calculated Bloom filters for every receipt and created merkle trees + /// of receipt. This is a expensive operation. + pub fn receipts_root_slow(&self, block_number: BlockNumber) -> Option { + let index = self.block_number_to_index(block_number)?; + let block_receipts = + self.receipts[index].iter().map(Option::as_ref).collect::>>()?; + Some(calculate_receipt_root_ref(&block_receipts)) + } + + /// Return reference to receipts. + pub fn receipts(&self) -> &Vec>> { + &self.receipts + } + + /// Return all block receipts + pub fn receipts_by_block(&self, block_number: BlockNumber) -> &[Option] { + let Some(index) = self.block_number_to_index(block_number) else { return &[] }; + &self.receipts[index] + } + + /// Is bundle state empty of blocks. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Number of blocks in bundle state. + pub fn len(&self) -> usize { + self.receipts.len() + } + + /// Return first block of the bundle + pub fn first_block(&self) -> BlockNumber { + self.first_block + } + + /// Return last block of the bundle. + pub fn last_block(&self) -> BlockNumber { + self.first_block + self.len() as BlockNumber + } + + /// Revert to given block number. + /// + /// If number is in future, or in the past return false + /// + /// Note: Given Block number will stay inside the bundle state. + pub fn revert_to(&mut self, block_number: BlockNumber) -> bool { + let Some(index) = self.block_number_to_index(block_number) else { return false }; + + // +1 is for number of blocks that we have as index is included. + let new_len = index + 1; + let rm_trx: usize = self.len() - new_len; + + // remove receipts + self.receipts.truncate(new_len); + // Revert last n reverts. + self.bundle.revert(rm_trx); + + true + } + + /// This will detach lower part of the chain and return it back. + /// Specified block number will be included in detachment + /// + /// This plain state will contains some additional information that + /// are is a artifacts of the lower part state. + /// + /// If block number is in future, return None. + pub fn split_at(&mut self, block_number: BlockNumber) -> Option { + let last_block = self.last_block(); + let first_block = self.first_block; + if block_number >= last_block { + return None + } + if block_number < first_block { + return Some(Self::default()) + } + + // detached number should be included so we are adding +1 to it. + // for example if block number is same as first_block then + // number of detached block shoud be 1. + let num_of_detached_block = (block_number - first_block) + 1; + + let mut detached_bundle_state: BundleStateWithReceipts = self.clone(); + detached_bundle_state.revert_to(block_number); + + // split is done as [0, num) and [num, len] + let (_, this) = self.receipts.split_at(num_of_detached_block as usize); + + self.receipts = this.to_vec().clone(); + self.bundle.take_n_reverts(num_of_detached_block as usize); + + self.first_block = block_number + 1; + + Some(detached_bundle_state) + } + + /// Extend one state from another + /// + /// For state this is very sensitive opperation and should be used only when + /// we know that other state was build on top of this one. + /// In most cases this would be true. + pub fn extend(&mut self, other: Self) { + self.bundle.extend(other.bundle); + self.receipts.extend(other.receipts); + } + + /// Write bundle state to database. + /// + /// `omit_changed_check` should be set to true of bundle has some of it data + /// detached, This would make some original values not known. + pub fn write_to_db<'a, TX: DbTxMut<'a> + DbTx<'a>>( + self, + tx: &TX, + is_value_known: OriginalValuesKnown, + ) -> Result<(), DatabaseError> { + let (plain_state, reverts) = self.bundle.into_plain_state_and_reverts(is_value_known); + + StateReverts(reverts).write_to_db(tx, self.first_block)?; + + // write receipts + let mut bodies_cursor = tx.cursor_read::()?; + let mut receipts_cursor = tx.cursor_write::()?; + + for (idx, receipts) in self.receipts.into_iter().enumerate() { + if !receipts.is_empty() { + let (_, body_indices) = bodies_cursor + .seek_exact(self.first_block + idx as u64)? + .expect("body indices exist"); + + let first_tx_index = body_indices.first_tx_num(); + for (tx_idx, receipt) in receipts.into_iter().enumerate() { + if let Some(receipt) = receipt { + receipts_cursor.append(first_tx_index + tx_idx as u64, receipt)?; + } + } + } + } + + StateChanges(plain_state).write_to_db(tx)?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::{StateChanges, StateReverts}; + use crate::{AccountReader, BundleStateWithReceipts, ProviderFactory}; + use reth_db::{ + cursor::{DbCursorRO, DbDupCursorRO}, + models::{AccountBeforeTx, BlockNumberAddress}, + tables, + test_utils::create_test_rw_db, + transaction::DbTx, + DatabaseEnv, + }; + use reth_primitives::{Address, Receipt, StorageEntry, H256, MAINNET, U256}; + use reth_revm_primitives::{into_reth_acc, primitives::HashMap}; + use revm::{ + db::{ + states::{ + bundle_state::{BundleRetention, OriginalValuesKnown}, + changes::PlainStorageRevert, + PlainStorageChangeset, + }, + BundleState, + }, + primitives::{Account, AccountInfo as RevmAccountInfo, AccountStatus, StorageSlot}, + CacheState, DatabaseCommit, State, + }; + use std::sync::Arc; + + #[test] + fn write_to_db_account_info() { + let db: Arc = create_test_rw_db(); + let factory = ProviderFactory::new(db, MAINNET.clone()); + let provider = factory.provider_rw().unwrap(); + + let address_a = Address::zero(); + let address_b = Address::repeat_byte(0xff); + + let account_a = RevmAccountInfo { balance: U256::from(1), nonce: 1, ..Default::default() }; + let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() }; + let account_b_changed = + RevmAccountInfo { balance: U256::from(3), nonce: 3, ..Default::default() }; + + let mut cache_state = CacheState::new(true); + cache_state.insert_not_existing(address_a); + cache_state.insert_account(address_b, account_b.clone()); + let mut state = + State::builder().with_cached_prestate(cache_state).with_bundle_update().build(); + + // 0x00.. is created + state.commit(HashMap::from([( + address_a, + Account { + info: account_a.clone(), + status: AccountStatus::Touched | AccountStatus::Created, + storage: HashMap::default(), + }, + )])); + + // 0xff.. is changed (balance + 1, nonce + 1) + state.commit(HashMap::from([( + address_b, + Account { + info: account_b_changed.clone(), + status: AccountStatus::Touched, + storage: HashMap::default(), + }, + )])); + + state.merge_transitions(BundleRetention::Reverts); + let mut revm_bundle_state = state.take_bundle(); + + // Write plain state and reverts separately. + let reverts = revm_bundle_state.take_all_reverts().into_plain_state_reverts(); + let plain_state = revm_bundle_state.into_plain_state(OriginalValuesKnown::Yes); + assert!(plain_state.storage.is_empty()); + assert!(plain_state.contracts.is_empty()); + StateChanges(plain_state) + .write_to_db(provider.tx_ref()) + .expect("Could not write plain state to DB"); + + assert_eq!(reverts.storage, [[]]); + StateReverts(reverts) + .write_to_db(provider.tx_ref(), 1) + .expect("Could not write reverts to DB"); + + let reth_account_a = into_reth_acc(account_a); + let reth_account_b = into_reth_acc(account_b); + let reth_account_b_changed = into_reth_acc(account_b_changed.clone()); + + // Check plain state + assert_eq!( + provider.basic_account(address_a).expect("Could not read account state"), + Some(reth_account_a), + "Account A state is wrong" + ); + assert_eq!( + provider.basic_account(address_b).expect("Could not read account state"), + Some(reth_account_b_changed), + "Account B state is wrong" + ); + + // Check change set + let mut changeset_cursor = provider + .tx_ref() + .cursor_dup_read::() + .expect("Could not open changeset cursor"); + assert_eq!( + changeset_cursor.seek_exact(1).expect("Could not read account change set"), + Some((1, AccountBeforeTx { address: address_a, info: None })), + "Account A changeset is wrong" + ); + assert_eq!( + changeset_cursor.next_dup().expect("Changeset table is malformed"), + Some((1, AccountBeforeTx { address: address_b, info: Some(reth_account_b) })), + "Account B changeset is wrong" + ); + + let mut cache_state = CacheState::new(true); + cache_state.insert_account(address_b, account_b_changed.clone()); + let mut state = + State::builder().with_cached_prestate(cache_state).with_bundle_update().build(); + + // 0xff.. is destroyed + state.commit(HashMap::from([( + address_b, + Account { + status: AccountStatus::Touched | AccountStatus::SelfDestructed, + info: account_b_changed, + storage: HashMap::default(), + }, + )])); + + state.merge_transitions(BundleRetention::Reverts); + let mut revm_bundle_state = state.take_bundle(); + + // Write plain state and reverts separately. + let reverts = revm_bundle_state.take_all_reverts().into_plain_state_reverts(); + let plain_state = revm_bundle_state.into_plain_state(OriginalValuesKnown::Yes); + // Account B selfdestructed so flag for it should be present. + assert_eq!( + plain_state.storage, + [PlainStorageChangeset { address: address_b, wipe_storage: true, storage: vec![] }] + ); + assert!(plain_state.contracts.is_empty()); + StateChanges(plain_state) + .write_to_db(provider.tx_ref()) + .expect("Could not write plain state to DB"); + + assert_eq!( + reverts.storage, + [[PlainStorageRevert { address: address_b, wiped: true, storage_revert: vec![] }]] + ); + StateReverts(reverts) + .write_to_db(provider.tx_ref(), 2) + .expect("Could not write reverts to DB"); + + // Check new plain state for account B + assert_eq!( + provider.basic_account(address_b).expect("Could not read account state"), + None, + "Account B should be deleted" + ); + + // Check change set + assert_eq!( + changeset_cursor.seek_exact(2).expect("Could not read account change set"), + Some((2, AccountBeforeTx { address: address_b, info: Some(reth_account_b_changed) })), + "Account B changeset is wrong after deletion" + ); + } + + #[test] + fn write_to_db_storage() { + let db: Arc = create_test_rw_db(); + let factory = ProviderFactory::new(db, MAINNET.clone()); + let provider = factory.provider_rw().unwrap(); + + let address_a = Address::zero(); + let address_b = Address::repeat_byte(0xff); + + let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() }; + + let mut cache_state = CacheState::new(true); + cache_state.insert_not_existing(address_a); + cache_state.insert_account_with_storage( + address_b, + account_b.clone(), + HashMap::from([(U256::from(1), U256::from(1))]), + ); + let mut state = + State::builder().with_cached_prestate(cache_state).with_bundle_update().build(); + + state.commit(HashMap::from([ + ( + address_a, + Account { + status: AccountStatus::Touched | AccountStatus::Created, + info: RevmAccountInfo::default(), + // 0x00 => 0 => 1 + // 0x01 => 0 => 2 + storage: HashMap::from([ + ( + U256::from(0), + StorageSlot { present_value: U256::from(1), ..Default::default() }, + ), + ( + U256::from(1), + StorageSlot { present_value: U256::from(2), ..Default::default() }, + ), + ]), + }, + ), + ( + address_b, + Account { + status: AccountStatus::Touched, + info: account_b, + // 0x01 => 1 => 2 + storage: HashMap::from([( + U256::from(1), + StorageSlot { + present_value: U256::from(2), + previous_or_original_value: U256::from(1), + }, + )]), + }, + ), + ])); + + state.merge_transitions(BundleRetention::Reverts); + + BundleStateWithReceipts::new(state.take_bundle(), Vec::new(), 1) + .write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes) + .expect("Could not write bundle state to DB"); + + // Check plain storage state + let mut storage_cursor = provider + .tx_ref() + .cursor_dup_read::() + .expect("Could not open plain storage state cursor"); + + assert_eq!( + storage_cursor.seek_exact(address_a).unwrap(), + Some((address_a, StorageEntry { key: H256::zero(), value: U256::from(1) })), + "Slot 0 for account A should be 1" + ); + assert_eq!( + storage_cursor.next_dup().unwrap(), + Some(( + address_a, + StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(2) } + )), + "Slot 1 for account A should be 2" + ); + assert_eq!( + storage_cursor.next_dup().unwrap(), + None, + "Account A should only have 2 storage slots" + ); + + assert_eq!( + storage_cursor.seek_exact(address_b).unwrap(), + Some(( + address_b, + StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(2) } + )), + "Slot 1 for account B should be 2" + ); + assert_eq!( + storage_cursor.next_dup().unwrap(), + None, + "Account B should only have 1 storage slot" + ); + + // Check change set + let mut changeset_cursor = provider + .tx_ref() + .cursor_dup_read::() + .expect("Could not open storage changeset cursor"); + assert_eq!( + changeset_cursor.seek_exact(BlockNumberAddress((1, address_a))).unwrap(), + Some(( + BlockNumberAddress((1, address_a)), + StorageEntry { key: H256::zero(), value: U256::from(0) } + )), + "Slot 0 for account A should have changed from 0" + ); + assert_eq!( + changeset_cursor.next_dup().unwrap(), + Some(( + BlockNumberAddress((1, address_a)), + StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(0) } + )), + "Slot 1 for account A should have changed from 0" + ); + assert_eq!( + changeset_cursor.next_dup().unwrap(), + None, + "Account A should only be in the changeset 2 times" + ); + + assert_eq!( + changeset_cursor.seek_exact(BlockNumberAddress((1, address_b))).unwrap(), + Some(( + BlockNumberAddress((1, address_b)), + StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(1) } + )), + "Slot 1 for account B should have changed from 1" + ); + assert_eq!( + changeset_cursor.next_dup().unwrap(), + None, + "Account B should only be in the changeset 1 time" + ); + + // Delete account A + let mut cache_state = CacheState::new(true); + cache_state.insert_account(address_a, RevmAccountInfo::default()); + let mut state = + State::builder().with_cached_prestate(cache_state).with_bundle_update().build(); + + state.commit(HashMap::from([( + address_a, + Account { + status: AccountStatus::Touched | AccountStatus::SelfDestructed, + info: RevmAccountInfo::default(), + storage: HashMap::default(), + }, + )])); + + state.merge_transitions(BundleRetention::Reverts); + BundleStateWithReceipts::new(state.take_bundle(), Vec::new(), 2) + .write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes) + .expect("Could not write bundle state to DB"); + + assert_eq!( + storage_cursor.seek_exact(address_a).unwrap(), + None, + "Account A should have no storage slots after deletion" + ); + + assert_eq!( + changeset_cursor.seek_exact(BlockNumberAddress((2, address_a))).unwrap(), + Some(( + BlockNumberAddress((2, address_a)), + StorageEntry { key: H256::zero(), value: U256::from(1) } + )), + "Slot 0 for account A should have changed from 1 on deletion" + ); + assert_eq!( + changeset_cursor.next_dup().unwrap(), + Some(( + BlockNumberAddress((2, address_a)), + StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(2) } + )), + "Slot 1 for account A should have changed from 2 on deletion" + ); + assert_eq!( + changeset_cursor.next_dup().unwrap(), + None, + "Account A should only be in the changeset 2 times on deletion" + ); + } + + #[test] + fn write_to_db_multiple_selfdestructs() { + let db: Arc = create_test_rw_db(); + let factory = ProviderFactory::new(db, MAINNET.clone()); + let provider = factory.provider_rw().unwrap(); + + let address1 = Address::random(); + let account_info = RevmAccountInfo { nonce: 1, ..Default::default() }; + + // Block #0: initial state. + let mut cache_state = CacheState::new(true); + cache_state.insert_not_existing(address1); + let mut init_state = + State::builder().with_cached_prestate(cache_state).with_bundle_update().build(); + init_state.commit(HashMap::from([( + address1, + Account { + info: account_info.clone(), + status: AccountStatus::Touched | AccountStatus::Created, + // 0x00 => 0 => 1 + // 0x01 => 0 => 2 + storage: HashMap::from([ + ( + U256::ZERO, + StorageSlot { present_value: U256::from(1), ..Default::default() }, + ), + ( + U256::from(1), + StorageSlot { present_value: U256::from(2), ..Default::default() }, + ), + ]), + }, + )])); + init_state.merge_transitions(BundleRetention::Reverts); + BundleStateWithReceipts::new(init_state.take_bundle(), Vec::new(), 0) + .write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes) + .expect("Could not write init bundle state to DB"); + + let mut cache_state = CacheState::new(true); + cache_state.insert_account_with_storage( + address1, + account_info.clone(), + HashMap::from([(U256::ZERO, U256::from(1)), (U256::from(1), U256::from(2))]), + ); + let mut state = + State::builder().with_cached_prestate(cache_state).with_bundle_update().build(); + + // Block #1: change storage. + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched, + info: account_info.clone(), + // 0x00 => 1 => 2 + storage: HashMap::from([( + U256::ZERO, + StorageSlot { + previous_or_original_value: U256::from(1), + present_value: U256::from(2), + }, + )]), + }, + )])); + state.merge_transitions(BundleRetention::Reverts); + + // Block #2: destroy account. + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched | AccountStatus::SelfDestructed, + info: account_info.clone(), + storage: HashMap::default(), + }, + )])); + state.merge_transitions(BundleRetention::Reverts); + + // Block #3: re-create account and change storage. + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched | AccountStatus::Created, + info: account_info.clone(), + storage: HashMap::default(), + }, + )])); + state.merge_transitions(BundleRetention::Reverts); + + // Block #4: change storage. + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched, + info: account_info.clone(), + // 0x00 => 0 => 2 + // 0x02 => 0 => 4 + // 0x06 => 0 => 6 + storage: HashMap::from([ + ( + U256::ZERO, + StorageSlot { present_value: U256::from(2), ..Default::default() }, + ), + ( + U256::from(2), + StorageSlot { present_value: U256::from(4), ..Default::default() }, + ), + ( + U256::from(6), + StorageSlot { present_value: U256::from(6), ..Default::default() }, + ), + ]), + }, + )])); + state.merge_transitions(BundleRetention::Reverts); + + // Block #5: Destroy account again. + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched | AccountStatus::SelfDestructed, + info: account_info.clone(), + storage: HashMap::default(), + }, + )])); + state.merge_transitions(BundleRetention::Reverts); + + // Block #6: Create, change, destroy and re-create in the same block. + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched | AccountStatus::Created, + info: account_info.clone(), + storage: HashMap::default(), + }, + )])); + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched, + info: account_info.clone(), + // 0x00 => 0 => 2 + storage: HashMap::from([( + U256::ZERO, + StorageSlot { present_value: U256::from(2), ..Default::default() }, + )]), + }, + )])); + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched | AccountStatus::SelfDestructed, + info: account_info.clone(), + storage: HashMap::default(), + }, + )])); + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched | AccountStatus::Created, + info: account_info.clone(), + storage: HashMap::default(), + }, + )])); + state.merge_transitions(BundleRetention::Reverts); + + // Block #7: Change storage. + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched, + info: account_info.clone(), + // 0x00 => 0 => 9 + storage: HashMap::from([( + U256::ZERO, + StorageSlot { present_value: U256::from(9), ..Default::default() }, + )]), + }, + )])); + state.merge_transitions(BundleRetention::Reverts); + + let bundle = state.take_bundle(); + + BundleStateWithReceipts::new(bundle, Vec::new(), 1) + .write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes) + .expect("Could not write bundle state to DB"); + + let mut storage_changeset_cursor = provider + .tx_ref() + .cursor_dup_read::() + .expect("Could not open plain storage state cursor"); + let mut storage_changes = storage_changeset_cursor.walk_range(..).unwrap(); + + // Iterate through all storage changes + + // Block + // : + // ... + + // Block #0 + // 0x00: 0 + // 0x01: 0 + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((0, address1)), + StorageEntry { key: H256::from_low_u64_be(0), value: U256::ZERO } + ))) + ); + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((0, address1)), + StorageEntry { key: H256::from_low_u64_be(1), value: U256::ZERO } + ))) + ); + + // Block #1 + // 0x00: 1 + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((1, address1)), + StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(1) } + ))) + ); + + // Block #2 (destroyed) + // 0x00: 2 + // 0x01: 2 + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((2, address1)), + StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(2) } + ))) + ); + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((2, address1)), + StorageEntry { key: H256::from_low_u64_be(1), value: U256::from(2) } + ))) + ); + + // Block #3 + // no storage changes + + // Block #4 + // 0x00: 0 + // 0x02: 0 + // 0x06: 0 + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((4, address1)), + StorageEntry { key: H256::from_low_u64_be(0), value: U256::ZERO } + ))) + ); + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((4, address1)), + StorageEntry { key: H256::from_low_u64_be(2), value: U256::ZERO } + ))) + ); + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((4, address1)), + StorageEntry { key: H256::from_low_u64_be(6), value: U256::ZERO } + ))) + ); + + // Block #5 (destroyed) + // 0x00: 2 + // 0x02: 4 + // 0x06: 6 + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((5, address1)), + StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(2) } + ))) + ); + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((5, address1)), + StorageEntry { key: H256::from_low_u64_be(2), value: U256::from(4) } + ))) + ); + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((5, address1)), + StorageEntry { key: H256::from_low_u64_be(6), value: U256::from(6) } + ))) + ); + + // Block #6 + // no storage changes (only inter block changes) + + // Block #7 + // 0x00: 0 + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((7, address1)), + StorageEntry { key: H256::from_low_u64_be(0), value: U256::ZERO } + ))) + ); + assert_eq!(storage_changes.next(), None); + } + + #[test] + fn storage_change_after_selfdestruct_within_block() { + let db: Arc = create_test_rw_db(); + let factory = ProviderFactory::new(db, MAINNET.clone()); + let provider = factory.provider_rw().unwrap(); + + let address1 = Address::random(); + let account1 = RevmAccountInfo { nonce: 1, ..Default::default() }; + + // Block #0: initial state. + let mut cache_state = CacheState::new(true); + cache_state.insert_not_existing(address1); + let mut init_state = + State::builder().with_cached_prestate(cache_state).with_bundle_update().build(); + init_state.commit(HashMap::from([( + address1, + Account { + info: account1.clone(), + status: AccountStatus::Touched | AccountStatus::Created, + // 0x00 => 0 => 1 + // 0x01 => 0 => 2 + storage: HashMap::from([ + ( + U256::ZERO, + StorageSlot { present_value: U256::from(1), ..Default::default() }, + ), + ( + U256::from(1), + StorageSlot { present_value: U256::from(2), ..Default::default() }, + ), + ]), + }, + )])); + init_state.merge_transitions(BundleRetention::Reverts); + BundleStateWithReceipts::new(init_state.take_bundle(), Vec::new(), 0) + .write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes) + .expect("Could not write init bundle state to DB"); + + let mut cache_state = CacheState::new(true); + cache_state.insert_account_with_storage( + address1, + account1.clone(), + HashMap::from([(U256::ZERO, U256::from(1)), (U256::from(1), U256::from(2))]), + ); + let mut state = + State::builder().with_cached_prestate(cache_state).with_bundle_update().build(); + + // Block #1: Destroy, re-create, change storage. + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched | AccountStatus::SelfDestructed, + info: account1.clone(), + storage: HashMap::default(), + }, + )])); + + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched | AccountStatus::Created, + info: account1.clone(), + storage: HashMap::default(), + }, + )])); + + state.commit(HashMap::from([( + address1, + Account { + status: AccountStatus::Touched, + info: account1.clone(), + // 0x01 => 0 => 5 + storage: HashMap::from([( + U256::from(1), + StorageSlot { present_value: U256::from(5), ..Default::default() }, + )]), + }, + )])); + + // Commit block #1 changes to the database. + state.merge_transitions(BundleRetention::Reverts); + BundleStateWithReceipts::new(state.take_bundle(), Vec::new(), 1) + .write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes) + .expect("Could not write bundle state to DB"); + + let mut storage_changeset_cursor = provider + .tx_ref() + .cursor_dup_read::() + .expect("Could not open plain storage state cursor"); + let range = BlockNumberAddress::range(1..=1); + let mut storage_changes = storage_changeset_cursor.walk_range(range).unwrap(); + + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((1, address1)), + StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(1) } + ))) + ); + assert_eq!( + storage_changes.next(), + Some(Ok(( + BlockNumberAddress((1, address1)), + StorageEntry { key: H256::from_low_u64_be(1), value: U256::from(2) } + ))) + ); + assert_eq!(storage_changes.next(), None); + } + + #[test] + fn revert_to_indices() { + let base = BundleStateWithReceipts { + bundle: BundleState::default(), + receipts: vec![vec![Some(Receipt::default()); 2]; 7], + first_block: 10, + }; + + let mut this = base.clone(); + assert!(this.revert_to(10)); + assert_eq!(this.receipts.len(), 1); + + let mut this = base.clone(); + assert!(!this.revert_to(9)); + assert_eq!(this.receipts.len(), 7); + + let mut this = base.clone(); + assert!(this.revert_to(15)); + assert_eq!(this.receipts.len(), 6); + + let mut this = base.clone(); + assert!(this.revert_to(16)); + assert_eq!(this.receipts.len(), 7); + + let mut this = base.clone(); + assert!(!this.revert_to(17)); + assert_eq!(this.receipts.len(), 7); + } +} diff --git a/crates/storage/provider/src/bundle_state/mod.rs b/crates/storage/provider/src/bundle_state/mod.rs new file mode 100644 index 000000000..88b17ad56 --- /dev/null +++ b/crates/storage/provider/src/bundle_state/mod.rs @@ -0,0 +1,11 @@ +//! Bundle state module. +//! This module contains all the logic related to bundle state. +mod bundle_state_with_receipts; +mod state_changes; +mod state_reverts; + +pub use bundle_state_with_receipts::{ + AccountRevertInit, BundleStateInit, BundleStateWithReceipts, OriginalValuesKnown, RevertsInit, +}; +pub use state_changes::StateChanges; +pub use state_reverts::StateReverts; diff --git a/crates/storage/provider/src/bundle_state/state_changes.rs b/crates/storage/provider/src/bundle_state/state_changes.rs new file mode 100644 index 000000000..fddfbdaac --- /dev/null +++ b/crates/storage/provider/src/bundle_state/state_changes.rs @@ -0,0 +1,88 @@ +use rayon::slice::ParallelSliceMut; +use reth_db::{ + cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW}, + tables, + transaction::{DbTx, DbTxMut}, +}; +use reth_interfaces::db::DatabaseError; +use reth_primitives::{Bytecode, StorageEntry, U256}; +use reth_revm_primitives::{ + db::states::{PlainStorageChangeset, StateChangeset}, + into_reth_acc, +}; + +/// A change to the state of the world. +#[derive(Default)] +pub struct StateChanges(pub StateChangeset); + +impl From for StateChanges { + fn from(revm: StateChangeset) -> Self { + Self(revm) + } +} + +impl StateChanges { + /// Write the post state to the database. + pub fn write_to_db<'a, TX: DbTxMut<'a> + DbTx<'a>>( + mut self, + tx: &TX, + ) -> Result<(), DatabaseError> { + // sort all entries so they can be written to database in more performant way. + // and take smaller memory footprint. + self.0.accounts.par_sort_by_key(|a| a.0); + self.0.storage.par_sort_by_key(|a| a.address); + self.0.contracts.par_sort_by_key(|a| a.0); + + // Write new account state + tracing::trace!(target: "provider::post_state", len = self.0.accounts.len(), "Writing new account state"); + let mut accounts_cursor = tx.cursor_write::()?; + // write account to database. + for (address, account) in self.0.accounts.into_iter() { + if let Some(account) = account { + tracing::trace!(target: "provider::post_state", ?address, "Updating plain state account"); + accounts_cursor.upsert(address, into_reth_acc(account))?; + } else if accounts_cursor.seek_exact(address)?.is_some() { + tracing::trace!(target: "provider::post_state", ?address, "Deleting plain state account"); + accounts_cursor.delete_current()?; + } + } + + // Write bytecode + tracing::trace!(target: "provider::post_state", len = self.0.contracts.len(), "Writing bytecodes"); + let mut bytecodes_cursor = tx.cursor_write::()?; + for (hash, bytecode) in self.0.contracts.into_iter() { + bytecodes_cursor.upsert(hash, Bytecode(bytecode))?; + } + + // Write new storage state and wipe storage if needed. + tracing::trace!(target: "provider::post_state", len = self.0.storage.len(), "Writing new storage state"); + let mut storages_cursor = tx.cursor_dup_write::()?; + for PlainStorageChangeset { address, wipe_storage, storage } in self.0.storage.into_iter() { + // Wiping of storage. + if wipe_storage && storages_cursor.seek_exact(address)?.is_some() { + storages_cursor.delete_current_duplicates()?; + } + // cast storages to H256. + let mut storage = storage + .into_iter() + .map(|(k, value)| StorageEntry { key: k.into(), value }) + .collect::>(); + // sort storage slots by key. + storage.par_sort_unstable_by_key(|a| a.key); + + for entry in storage.into_iter() { + tracing::trace!(target: "provider::post_state", ?address, ?entry.key, "Updating plain state storage"); + if let Some(db_entry) = storages_cursor.seek_by_key_subkey(address, entry.key)? { + if db_entry.key == entry.key { + storages_cursor.delete_current()?; + } + } + + if entry.value != U256::ZERO { + storages_cursor.upsert(address, entry)?; + } + } + } + Ok(()) + } +} diff --git a/crates/storage/provider/src/bundle_state/state_reverts.rs b/crates/storage/provider/src/bundle_state/state_reverts.rs new file mode 100644 index 000000000..e3a178ca0 --- /dev/null +++ b/crates/storage/provider/src/bundle_state/state_reverts.rs @@ -0,0 +1,167 @@ +use rayon::slice::ParallelSliceMut; +use reth_db::{ + cursor::{DbCursorRO, DbDupCursorRO, DbDupCursorRW}, + models::{AccountBeforeTx, BlockNumberAddress}, + tables, + transaction::{DbTx, DbTxMut}, +}; +use reth_interfaces::db::DatabaseError; +use reth_primitives::{BlockNumber, StorageEntry, H256, U256}; +use reth_revm_primitives::{ + db::states::{PlainStateReverts, PlainStorageRevert, RevertToSlot}, + into_reth_acc, +}; +use std::iter::Peekable; + +/// Revert of the state. +#[derive(Default)] +pub struct StateReverts(pub PlainStateReverts); + +impl From for StateReverts { + fn from(revm: PlainStateReverts) -> Self { + Self(revm) + } +} + +impl StateReverts { + /// Write reverts to database. + /// + /// Note:: Reverts will delete all wiped storage from plain state. + pub fn write_to_db<'a, TX: DbTxMut<'a> + DbTx<'a>>( + self, + tx: &TX, + first_block: BlockNumber, + ) -> Result<(), DatabaseError> { + // Write storage changes + tracing::trace!(target: "provider::reverts", "Writing storage changes"); + let mut storages_cursor = tx.cursor_dup_write::()?; + let mut storage_changeset_cursor = tx.cursor_dup_write::()?; + for (block_index, mut storage_changes) in self.0.storage.into_iter().enumerate() { + let block_number = first_block + block_index as BlockNumber; + + tracing::trace!(target: "provider::reverts", block_number, "Writing block change"); + // sort changes by address. + storage_changes.par_sort_unstable_by_key(|a| a.address); + for PlainStorageRevert { address, wiped, storage_revert } in storage_changes.into_iter() + { + let storage_id = BlockNumberAddress((block_number, address)); + + let mut storage = storage_revert + .into_iter() + .map(|(k, v)| (H256(k.to_be_bytes()), v)) + .collect::>(); + // sort storage slots by key. + storage.par_sort_unstable_by_key(|a| a.0); + + // If we are writing the primary storage wipe transition, the pre-existing plain + // storage state has to be taken from the database and written to storage history. + // See [StorageWipe::Primary] for more details. + let mut wiped_storage = Vec::new(); + if wiped { + tracing::trace!(target: "provider::reverts", ?address, "Wiping storage"); + if let Some((_, entry)) = storages_cursor.seek_exact(address)? { + wiped_storage.push((entry.key, entry.value)); + while let Some(entry) = storages_cursor.next_dup_val()? { + wiped_storage.push((entry.key, entry.value)) + } + } + } + + tracing::trace!(target: "provider::reverts", ?address, ?storage, "Writing storage reverts"); + for (key, value) in StorageRevertsIter::new(storage, wiped_storage) { + storage_changeset_cursor.append_dup(storage_id, StorageEntry { key, value })?; + } + } + } + + // Write account changes + tracing::trace!(target: "provider::reverts", "Writing account changes"); + let mut account_changeset_cursor = tx.cursor_dup_write::()?; + for (block_index, mut account_block_reverts) in self.0.accounts.into_iter().enumerate() { + let block_number = first_block + block_index as BlockNumber; + // Sort accounts by address. + account_block_reverts.par_sort_by_key(|a| a.0); + for (address, info) in account_block_reverts { + account_changeset_cursor.append_dup( + block_number, + AccountBeforeTx { address, info: info.map(into_reth_acc) }, + )?; + } + } + + Ok(()) + } +} + +/// Iterator over storage reverts. +/// See [StorageRevertsIter::next] for more details. +struct StorageRevertsIter { + reverts: Peekable, + wiped: Peekable, +} + +impl StorageRevertsIter +where + R: Iterator, + W: Iterator, +{ + fn new( + reverts: impl IntoIterator, + wiped: impl IntoIterator, + ) -> Self { + Self { reverts: reverts.into_iter().peekable(), wiped: wiped.into_iter().peekable() } + } + + /// Consume next revert and return it. + fn next_revert(&mut self) -> Option<(H256, U256)> { + self.reverts.next().map(|(key, revert)| (key, revert.to_previous_value())) + } + + /// Consume next wiped storage and return it. + fn next_wiped(&mut self) -> Option<(H256, U256)> { + self.wiped.next() + } +} + +impl Iterator for StorageRevertsIter +where + R: Iterator, + W: Iterator, +{ + type Item = (H256, U256); + + /// Iterate over storage reverts and wiped entries and return items in the sorted order. + /// NOTE: The implementation assumes that inner iterators are already sorted. + fn next(&mut self) -> Option { + match (self.reverts.peek(), self.wiped.peek()) { + (Some(revert), Some(wiped)) => { + // Compare the keys and return the lesser. + use std::cmp::Ordering; + match revert.0.cmp(&wiped.0) { + Ordering::Less => self.next_revert(), + Ordering::Greater => self.next_wiped(), + Ordering::Equal => { + // Keys are the same, decide which one to return. + let (key, revert_to) = *revert; + + let value = match revert_to { + // If the slot is some, prefer the revert value. + RevertToSlot::Some(value) => value, + // If the slot was destroyed, prefer the database value. + RevertToSlot::Destroyed => wiped.1, + }; + + // Consume both values from inner iterators. + self.next_revert(); + self.next_wiped(); + + Some((key, value)) + } + } + } + (Some(_revert), None) => self.next_revert(), + (None, Some(_wiped)) => self.next_wiped(), + (None, None) => None, + } + } +} diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index 1633d360f..fc81b3b49 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -1,6 +1,6 @@ //! Contains [Chain], a chain of blocks and their final state. -use crate::PostState; +use crate::bundle_state::BundleStateWithReceipts; use reth_interfaces::{executor::BlockExecutionError, Error}; use reth_primitives::{ BlockHash, BlockNumHash, BlockNumber, ForkBlock, Receipt, SealedBlock, SealedBlockWithSenders, @@ -20,7 +20,7 @@ pub struct Chain { /// [Chain::first] to [Chain::tip], inclusive. /// /// This state also contains the individual changes that lead to the current state. - pub state: PostState, + pub state: BundleStateWithReceipts, /// All blocks in this chain. pub blocks: BTreeMap, } @@ -42,7 +42,7 @@ impl Chain { } /// Get post state of this chain - pub fn state(&self) -> &PostState { + pub fn state(&self) -> &BundleStateWithReceipts { &self.state } @@ -64,7 +64,7 @@ impl Chain { } /// Return post state of the block at the `block_number` or None if block is not known - pub fn state_at_block(&self, block_number: BlockNumber) -> Option { + pub fn state_at_block(&self, block_number: BlockNumber) -> Option { if self.tip().number == block_number { return Some(self.state.clone()) } @@ -79,13 +79,13 @@ impl Chain { /// Destructure the chain into its inner components, the blocks and the state at the tip of the /// chain. - pub fn into_inner(self) -> (ChainBlocks<'static>, PostState) { + pub fn into_inner(self) -> (ChainBlocks<'static>, BundleStateWithReceipts) { (ChainBlocks { blocks: Cow::Owned(self.blocks) }, self.state) } /// Destructure the chain into its inner components, the blocks and the state at the tip of the /// chain. - pub fn inner(&self) -> (ChainBlocks<'_>, &PostState) { + pub fn inner(&self) -> (ChainBlocks<'_>, &BundleStateWithReceipts) { (ChainBlocks { blocks: Cow::Borrowed(&self.blocks) }, &self.state) } @@ -125,15 +125,8 @@ impl Chain { } /// Create new chain with given blocks and post state. - pub fn new(blocks: Vec<(SealedBlockWithSenders, PostState)>) -> Self { - let mut state = PostState::default(); - let mut block_num_hash = BTreeMap::new(); - for (block, block_state) in blocks.into_iter() { - state.extend(block_state); - block_num_hash.insert(block.number, block); - } - - Self { state, blocks: block_num_hash } + pub fn new(blocks: Vec, state: BundleStateWithReceipts) -> Self { + Self { state, blocks: blocks.into_iter().map(|b| (b.number, b)).collect() } } /// Returns length of the chain. @@ -142,9 +135,9 @@ impl Chain { } /// Get all receipts for the given block. - pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option<&[Receipt]> { + pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option> { let num = self.block_number(block_hash)?; - Some(self.state.receipts(num)) + self.state.receipts_by_block(num).iter().map(Option::as_ref).collect() } /// Get all receipts with attachment. @@ -152,13 +145,14 @@ impl Chain { /// Attachment includes block number, block hash, transaction hash and transaction index. pub fn receipts_with_attachment(&self) -> Vec { let mut receipt_attch = Vec::new(); - for (block_num, block) in self.blocks().iter() { - let mut receipts = self.state.receipts(*block_num).iter(); + for ((block_num, block), receipts) in self.blocks().iter().zip(self.state.receipts().iter()) + { let mut tx_receipts = Vec::new(); - for tx in block.body.iter() { - if let Some(receipt) = receipts.next() { - tx_receipts.push((tx.hash(), receipt.clone())); - } + for (tx, receipt) in block.body.iter().zip(receipts.iter()) { + tx_receipts.push(( + tx.hash(), + receipt.as_ref().expect("receipts have not been pruned").clone(), + )); } let block_num_hash = BlockNumHash::new(*block_num, block.hash()); receipt_attch.push(BlockReceipts { block: block_num_hash, tx_receipts }); @@ -188,7 +182,7 @@ impl Chain { /// Split this chain at the given block. /// - /// The given block will be the first block in the first returned chain. + /// The given block will be the last block in the first returned chain. /// /// If the given block is not found, [`ChainSplit::NoSplitPending`] is returned. /// Split chain at the number or hash, block with given number will be included at first chain. @@ -196,7 +190,7 @@ impl Chain { /// /// # Note /// - /// The block number to transition ID mapping is only found in the second chain, making it + /// The plain state is only found in the second chain, making it /// impossible to perform any state reverts on the first chain. /// /// The second chain only contains the changes that were reverted on the first chain; however, @@ -229,13 +223,13 @@ impl Chain { let higher_number_blocks = self.blocks.split_off(&(block_number + 1)); - let mut canonical_state = std::mem::take(&mut self.state); - let new_state = canonical_state.split_at(block_number); - self.state = new_state; + let mut state = std::mem::take(&mut self.state); + let canonical_state = + state.split_at(block_number).expect("Detach block number to be in range"); ChainSplit::Split { canonical: Chain { state: canonical_state, blocks: self.blocks }, - pending: Chain { state: self.state, blocks: higher_number_blocks }, + pending: Chain { state, blocks: higher_number_blocks }, } } } @@ -365,7 +359,11 @@ pub enum ChainSplit { #[cfg(test)] mod tests { use super::*; - use reth_primitives::{Account, H160, H256}; + use reth_primitives::{H160, H256}; + use reth_revm_primitives::{ + db::BundleState, + primitives::{AccountInfo, HashMap}, + }; #[test] fn chain_append() { @@ -401,15 +399,25 @@ mod tests { #[test] fn test_number_split() { - let mut base_state = PostState::default(); - let account = Account { nonce: 10, ..Default::default() }; - base_state.create_account(1, H160([1; 20]), account); + let block_state1 = BundleStateWithReceipts::new( + BundleState::new( + vec![(H160([2; 20]), None, Some(AccountInfo::default()), HashMap::default())], + vec![vec![(H160([2; 20]), None, vec![])]], + vec![], + ), + vec![vec![]], + 1, + ); - let mut block_state1 = PostState::default(); - block_state1.create_account(2, H160([2; 20]), Account::default()); - - let mut block_state2 = PostState::default(); - block_state2.create_account(3, H160([3; 20]), Account::default()); + let block_state2 = BundleStateWithReceipts::new( + BundleState::new( + vec![(H160([3; 20]), None, Some(AccountInfo::default()), HashMap::default())], + vec![vec![(H160([3; 20]), None, vec![])]], + vec![], + ), + vec![vec![]], + 2, + ); let mut block1 = SealedBlockWithSenders::default(); let block1_hash = H256([15; 32]); @@ -423,13 +431,13 @@ mod tests { block2.hash = block2_hash; block2.senders.push(H160([4; 20])); - let chain = Chain::new(vec![ - (block1.clone(), block_state1.clone()), - (block2.clone(), block_state2.clone()), - ]); + let mut block_state_extended = block_state1.clone(); + block_state_extended.extend(block_state2.clone()); - let mut split1_state = chain.state.clone(); - let split2_state = split1_state.split_at(1); + let chain = Chain::new(vec![block1.clone(), block2.clone()], block_state_extended); + + let mut split2_state = chain.state.clone(); + let split1_state = split2_state.split_at(1).unwrap(); let chain_split1 = Chain { state: split1_state, blocks: BTreeMap::from([(1, block1.clone())]) }; diff --git a/crates/storage/provider/src/lib.rs b/crates/storage/provider/src/lib.rs index 4e7048cd4..310f663d1 100644 --- a/crates/storage/provider/src/lib.rs +++ b/crates/storage/provider/src/lib.rs @@ -21,12 +21,12 @@ /// Various provider traits. mod traits; pub use traits::{ - AccountExtReader, AccountReader, BlockExecutionWriter, BlockExecutor, BlockHashReader, - BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, BlockWriter, - BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotification, - CanonStateNotificationSender, CanonStateNotifications, CanonStateSubscriptions, - ChainSpecProvider, ChangeSetReader, EvmEnvProvider, ExecutorFactory, HashingWriter, - HeaderProvider, HistoryWriter, PostStateDataProvider, PruneCheckpointReader, + AccountExtReader, AccountReader, BlockExecutionWriter, BlockExecutor, BlockExecutorStats, + BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, + BlockWriter, BlockchainTreePendingStateProvider, BundleStateDataProvider, CanonChainTracker, + CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications, + CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, ExecutorFactory, + HashingWriter, HeaderProvider, HistoryWriter, PrunableBlockExecutor, PruneCheckpointReader, PruneCheckpointWriter, ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StageCheckpointWriter, StateProvider, StateProviderBox, StateProviderFactory, StateRootProvider, StorageReader, TransactionsProvider, WithdrawalsProvider, @@ -39,10 +39,6 @@ pub use providers::{ HistoricalStateProviderRef, LatestStateProvider, LatestStateProviderRef, ProviderFactory, }; -/// Execution result -pub mod post_state; -pub use post_state::PostState; - #[cfg(any(test, feature = "test-utils"))] /// Common test helpers for mocking the Provider. pub mod test_utils; @@ -52,3 +48,6 @@ pub use reth_interfaces::provider::ProviderError; pub mod chain; pub use chain::{Chain, DisplayBlocksChain}; + +pub mod bundle_state; +pub use bundle_state::{BundleStateWithReceipts, OriginalValuesKnown, StateChanges, StateReverts}; diff --git a/crates/storage/provider/src/post_state/account.rs b/crates/storage/provider/src/post_state/account.rs deleted file mode 100644 index 8451dc75a..000000000 --- a/crates/storage/provider/src/post_state/account.rs +++ /dev/null @@ -1,89 +0,0 @@ -use derive_more::Deref; -use reth_primitives::{Account, Address, BlockNumber}; -use std::collections::{btree_map::Entry, BTreeMap}; - -/// A mapping of `block -> address -> account` that represents what accounts were changed, and what -/// their state were prior to that change. -/// -/// If the prior state was `None`, then the account is new. -#[derive(Default, Clone, Eq, PartialEq, Debug, Deref)] -pub struct AccountChanges { - /// The inner mapping of block changes. - #[deref] - pub inner: BTreeMap>>, - /// Hand tracked change size. - pub size: usize, -} - -impl AccountChanges { - /// Insert account change at specified block number. The value is **not** updated if it already - /// exists. - pub fn insert( - &mut self, - block: BlockNumber, - address: Address, - old: Option, - new: Option, - ) { - match self.inner.entry(block).or_default().entry(address) { - Entry::Vacant(entry) => { - self.size += 1; - entry.insert(old); - } - Entry::Occupied(entry) => { - // If the account state is the same before and after this block, collapse the state - // changes. - if entry.get() == &new { - entry.remove(); - self.size -= 1; - } - } - } - } - - /// Insert account changes at specified block number. The values are **not** updated if they - /// already exist. - pub fn insert_for_block( - &mut self, - block: BlockNumber, - changes: BTreeMap>, - ) { - let block_entry = self.inner.entry(block).or_default(); - for (address, account) in changes { - if let Entry::Vacant(entry) = block_entry.entry(address) { - entry.insert(account); - self.size += 1; - } - } - } - - /// Drain and return any entries above the target block number. - pub fn drain_above( - &mut self, - target_block: BlockNumber, - ) -> BTreeMap>> { - let mut evicted = BTreeMap::new(); - self.inner.retain(|block_number, accounts| { - if *block_number > target_block { - self.size -= accounts.len(); - evicted.insert(*block_number, accounts.clone()); - false - } else { - true - } - }); - evicted - } - - /// Retain entries only above specified block number. - pub fn retain_above(&mut self, target_block: BlockNumber) { - self.inner.retain(|block_number, accounts| { - if *block_number > target_block { - true - } else { - self.size -= accounts.len(); - false - } - }); - } -} diff --git a/crates/storage/provider/src/post_state/mod.rs b/crates/storage/provider/src/post_state/mod.rs deleted file mode 100644 index 069ad602a..000000000 --- a/crates/storage/provider/src/post_state/mod.rs +++ /dev/null @@ -1,2076 +0,0 @@ -//! Output of execution. -use reth_db::{ - cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW}, - models::{AccountBeforeTx, BlockNumberAddress}, - tables, - transaction::{DbTx, DbTxMut}, - DatabaseError as DbError, -}; -use reth_interfaces::Error; -use reth_primitives::{ - bloom::logs_bloom, keccak256, proofs::calculate_receipt_root_ref, Account, Address, - BlockNumber, Bloom, Bytecode, Log, PruneMode, PruneModes, Receipt, StorageEntry, H256, - MINIMUM_PRUNING_DISTANCE, U256, -}; -use reth_trie::{ - hashed_cursor::{HashedPostState, HashedPostStateCursorFactory, HashedStorage}, - StateRoot, StateRootError, -}; -use std::collections::{BTreeMap, BTreeSet}; - -mod account; -pub use account::AccountChanges; - -mod storage; -pub use storage::{Storage, StorageChanges, StorageChangeset, StorageTransition, StorageWipe}; - -// todo: rewrite all the docs for this -/// The state of accounts after execution of one or more transactions, including receipts and new -/// bytecode. -/// -/// The latest state can be found in `accounts`, `storage`, and `bytecode`. The receipts for the -/// transactions that lead to these changes can be found in `receipts`, and each change leading to -/// this state can be found in `changes`. -/// -/// # Wiped Storage -/// -/// The [Storage] type has a field, `wiped` which denotes whether the pre-existing storage in the -/// database should be cleared or not. -/// -/// If `wiped` is true, then the account was selfdestructed at some point, and the values contained -/// in `storage` should be the only values written to the database. -/// -/// # Transitions -/// -/// The block level transition includes: -/// -/// - Block rewards -/// - Ommer rewards -/// - Withdrawals -/// - The irregular state change for the DAO hardfork -/// -/// For multi-block [PostState]s it is not possible to figure out what transition ID maps on to a -/// transaction or a block. -/// -/// # Shaving Allocations -/// -/// Since most [PostState]s in reth are for multiple blocks it is better to pre-allocate capacity -/// for receipts and changes, which [PostState::new] does, and thus it (or -/// [PostState::with_tx_capacity]) should be preferred to using the [Default] implementation. -#[derive(Debug, Clone, Default, Eq, PartialEq)] -pub struct PostState { - /// The state of all modified accounts after execution. - /// - /// If the value contained is `None`, then the account should be deleted. - accounts: BTreeMap>, - /// The state of all modified storage after execution - /// - /// If the contained [Storage] is marked as wiped, then all storage values should be cleared - /// from the database. - storage: BTreeMap, - /// The state of accounts before they were changed in the given block. - /// - /// If the value is `None`, then the account is new, otherwise it is a change. - account_changes: AccountChanges, - /// The state of account storage before it was changed in the given block. - /// - /// This map only contains old values for storage slots. - storage_changes: StorageChanges, - /// New code created during the execution - bytecode: BTreeMap, - /// The receipt(s) of the executed transaction(s). - receipts: BTreeMap>, - /// Pruning configuration. - prune_modes: PruneModes, -} - -impl PostState { - /// Create an empty [PostState]. - pub fn new() -> Self { - Self::default() - } - - /// Create an empty [PostState] with pre-allocated space for a certain amount of transactions. - pub fn with_tx_capacity(block: BlockNumber, txs: usize) -> Self { - Self { receipts: BTreeMap::from([(block, Vec::with_capacity(txs))]), ..Default::default() } - } - - /// Add a pruning configuration. - pub fn add_prune_modes(&mut self, prune_modes: PruneModes) { - self.prune_modes = prune_modes; - } - - /// Return the current size of the poststate. - /// - /// Size is the sum of individual changes to accounts, storage, bytecode and receipts. - pub fn size_hint(&self) -> usize { - // The amount of plain state account entries to update. - self.accounts.len() - // The approximate amount of plain state storage entries to update. - // NOTE: This can be improved by manually keeping track of the storage size for each account. - + self.storage.len() - // The amount of bytecodes to insert. - + self.bytecode.len() - // The approximate amount of receipts. - // NOTE: This can be improved by manually keeping track of the receipt size for each block number. - + self.receipts.len() - // The approximate amount of changsets to update. - + self.changeset_size_hint() - } - - /// Return the current size of history changes in the poststate. - pub fn changeset_size_hint(&self) -> usize { - // The amount of account changesets to insert. - self.account_changes.size - // The approximate amount of storage changes to insert. - // NOTE: This does not include the entries for primary storage wipes, - // which need to be read from plain state. - + self.storage_changes.size - } - - /// Get the latest state of all changed accounts. - pub fn accounts(&self) -> &BTreeMap> { - &self.accounts - } - - /// Get a reference to all the account changes - pub fn account_changes(&self) -> &AccountChanges { - &self.account_changes - } - - /// Get a reference to all the storage changes - pub fn storage_changes(&self) -> &StorageChanges { - &self.storage_changes - } - - /// Get the latest state for a specific account. - /// - /// # Returns - /// - /// - `None` if the account does not exist - /// - `Some(&None)` if the account existed, but has since been deleted. - /// - `Some(..)` if the account currently exists - pub fn account(&self, address: &Address) -> Option<&Option> { - self.accounts.get(address) - } - - /// Get the latest state of storage. - pub fn storage(&self) -> &BTreeMap { - &self.storage - } - - /// Get the storage for an account. - pub fn account_storage(&self, address: &Address) -> Option<&Storage> { - self.storage.get(address) - } - - /// Get the newly created bytecodes - pub fn bytecodes(&self) -> &BTreeMap { - &self.bytecode - } - - /// Get a bytecode in the post-state. - pub fn bytecode(&self, code_hash: &H256) -> Option<&Bytecode> { - self.bytecode.get(code_hash) - } - - /// Get the receipts for the transactions executed to form this [PostState]. - pub fn receipts(&self, block: BlockNumber) -> &[Receipt] { - self.receipts.get(&block).map(Vec::as_slice).unwrap_or(&[]) - } - - /// Returns an iterator over all logs in this [PostState]. - pub fn logs(&self, block: BlockNumber) -> impl Iterator { - self.receipts(block).iter().flat_map(|r| r.logs.iter()) - } - - /// Returns the logs bloom for all recorded logs. - pub fn logs_bloom(&self, block: BlockNumber) -> Bloom { - logs_bloom(self.logs(block)) - } - - /// Returns the receipt root for all recorded receipts. - /// TODO: This function hides an expensive operation (bloom). We should probably make it more - /// explicit. - pub fn receipts_root(&self, block: BlockNumber) -> H256 { - calculate_receipt_root_ref(self.receipts(block)) - } - - /// Hash all changed accounts and storage entries that are currently stored in the post state. - /// - /// # Returns - /// - /// The hashed post state. - pub fn hash_state_slow(&self) -> HashedPostState { - let mut hashed_post_state = HashedPostState::default(); - - // Insert accounts with hashed keys from account changes. - for (address, account) in self.accounts() { - let hashed_address = keccak256(address); - if let Some(account) = account { - hashed_post_state.insert_account(hashed_address, *account); - } else { - hashed_post_state.insert_cleared_account(hashed_address); - } - } - - // Insert accounts and storages with hashed keys from storage changes. - for (address, storage) in self.storage() { - let mut hashed_storage = HashedStorage::new(storage.wiped()); - for (slot, value) in &storage.storage { - let hashed_slot = keccak256(H256(slot.to_be_bytes())); - if *value == U256::ZERO { - hashed_storage.insert_zero_valued_slot(hashed_slot); - } else { - hashed_storage.insert_non_zero_valued_storage(hashed_slot, *value); - } - } - - hashed_post_state.insert_hashed_storage(keccak256(address), hashed_storage); - } - - hashed_post_state - } - - /// Calculate the state root for this [PostState]. - /// Internally, function calls [Self::hash_state_slow] to obtain the [HashedPostState]. - /// Afterwards, it retrieves the [PrefixSets](reth_trie::prefix_set::PrefixSet) of changed keys - /// from the [HashedPostState] and uses them to calculate the incremental state root. - /// - /// # Example - /// - /// ``` - /// use reth_primitives::{Address, Account}; - /// use reth_provider::PostState; - /// use reth_db::{test_utils::create_test_rw_db, database::Database}; - /// - /// // Initialize the database - /// let db = create_test_rw_db(); - /// - /// // Initialize the post state - /// let mut post_state = PostState::new(); - /// - /// // Create an account - /// let block_number = 1; - /// let address = Address::random(); - /// post_state.create_account(1, address, Account { nonce: 1, ..Default::default() }); - /// - /// // Calculate the state root - /// let tx = db.tx().expect("failed to create transaction"); - /// let state_root = post_state.state_root_slow(&tx); - /// ``` - /// - /// # Returns - /// - /// The state root for this [PostState]. - pub fn state_root_slow<'a, 'tx, TX: DbTx<'tx>>( - &self, - tx: &'a TX, - ) -> Result { - let hashed_post_state = self.hash_state_slow().sorted(); - let (account_prefix_set, storage_prefix_set) = hashed_post_state.construct_prefix_sets(); - let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, &hashed_post_state); - StateRoot::new(tx) - .with_hashed_cursor_factory(&hashed_cursor_factory) - .with_changed_account_prefixes(account_prefix_set) - .with_changed_storage_prefixes(storage_prefix_set) - .root() - } - - // todo: note overwrite behavior, i.e. changes in `other` take precedent - /// Extend this [PostState] with the changes in another [PostState]. - pub fn extend(&mut self, mut other: PostState) { - // Insert storage change sets - for (block_number, storage_changes) in std::mem::take(&mut other.storage_changes).inner { - for (address, their_storage_transition) in storage_changes { - let our_storage = self.storage.entry(address).or_default(); - let (wipe, storage) = if their_storage_transition.wipe.is_wiped() { - // Check existing storage change. - match self.storage_changes.get(&block_number).and_then(|ch| ch.get(&address)) { - Some(change) if change.wipe.is_wiped() => (), // already counted - _ => { - our_storage.times_wiped += 1; - } - }; - // Check if this is the first wipe. - let wipe = if our_storage.times_wiped == 1 { - StorageWipe::Primary - } else { - // Even if the wipe in other poststate was primary before, demote it to - // secondary. - StorageWipe::Secondary - }; - let mut wiped_storage = std::mem::take(&mut our_storage.storage); - wiped_storage.extend(their_storage_transition.storage); - (wipe, wiped_storage) - } else { - (StorageWipe::None, their_storage_transition.storage) - }; - self.storage_changes.insert_for_block_and_address( - block_number, - address, - wipe, - storage.into_iter(), - ); - } - } - - // Insert account change sets - for (block_number, account_changes) in std::mem::take(&mut other.account_changes).inner { - self.account_changes.insert_for_block(block_number, account_changes); - } - - // Update plain state - self.accounts.extend(other.accounts); - for (address, their_storage) in other.storage { - let our_storage = self.storage.entry(address).or_default(); - our_storage.storage.extend(their_storage.storage); - } - - self.receipts.extend(other.receipts); - - self.bytecode.extend(other.bytecode); - } - - /// Reverts each change up to the `target_block_number` (excluding). - /// - /// The reverted changes are removed from this post-state, and their effects are reverted. - pub fn revert_to(&mut self, target_block_number: BlockNumber) { - // Revert account state & changes - let removed_account_changes = self.account_changes.drain_above(target_block_number); - let changed_accounts = self - .account_changes - .iter() - .flat_map(|(_, account_changes)| account_changes.iter().map(|(address, _)| *address)) - .collect::>(); - let mut account_state: BTreeMap> = BTreeMap::default(); - for address in changed_accounts { - let info = removed_account_changes - .iter() - .find_map(|(_, changes)| { - changes.iter().find_map(|ch| (ch.0 == &address).then_some(*ch.1)) - }) - .unwrap_or(*self.accounts.get(&address).expect("exists")); - account_state.insert(address, info); - } - self.accounts = account_state; - - // Revert changes and recreate the storage state - let removed_storage_changes = self.storage_changes.drain_above(target_block_number); - let mut storage_state: BTreeMap = BTreeMap::default(); - for (_, storage_changes) in self.storage_changes.iter() { - for (address, storage_change) in storage_changes { - let entry = storage_state.entry(*address).or_default(); - if storage_change.wipe.is_wiped() { - entry.times_wiped += 1; - } - for (slot, _) in storage_change.storage.iter() { - let value = removed_storage_changes - .iter() - .find_map(|(_, changes)| { - changes.iter().find_map(|ch| { - if ch.0 == address { - match ch.1.storage.iter().find_map(|(changed_slot, value)| { - (slot == changed_slot).then_some(*value) - }) { - value @ Some(_) => Some(value), - None if ch.1.wipe.is_wiped() => Some(None), - None => None, - } - } else { - None - } - }) - }) - .unwrap_or_else(|| { - self.storage.get(address).and_then(|s| s.storage.get(slot).copied()) - }); - if let Some(value) = value { - entry.storage.insert(*slot, value); - } - } - } - } - self.storage = storage_state; - - // Revert receipts - self.receipts.retain(|block_number, _| *block_number <= target_block_number); - } - - /// Reverts each change up to and including any change that is part of `transition_id`. - /// - /// The reverted changes are removed from this post-state, and their effects are reverted. - /// - /// A new post-state containing the pre-revert state, as well as the reverted changes *only* is - /// returned. - /// - /// This effectively splits the post state in two: - /// - /// 1. This post-state has the changes reverted - /// 2. The returned post-state does *not* have the changes reverted, but only contains the - /// descriptions of the changes that were reverted in the first post-state. - pub fn split_at(&mut self, revert_to_block: BlockNumber) -> Self { - // Clone ourselves - let mut non_reverted_state = self.clone(); - - // Revert the desired changes - self.revert_to(revert_to_block); - - // Remove all changes in the returned post-state that were not reverted - non_reverted_state.account_changes.retain_above(revert_to_block); - let updated_times_wiped = non_reverted_state.storage_changes.retain_above(revert_to_block); - // Update or reset the number of times the account was wiped. - for (address, storage) in non_reverted_state.storage.iter_mut() { - storage.times_wiped = updated_times_wiped.get(address).cloned().unwrap_or_default(); - } - // Remove receipts - non_reverted_state.receipts.retain(|block_number, _| *block_number > revert_to_block); - - non_reverted_state - } - - /// Add a newly created account to the post-state. - pub fn create_account( - &mut self, - block_number: BlockNumber, - address: Address, - account: Account, - ) { - self.accounts.insert(address, Some(account)); - self.account_changes.insert(block_number, address, None, Some(account)); - } - - /// Add a changed account to the post-state. - /// - /// If the account also has changed storage values, [PostState::change_storage] should also be - /// called. - pub fn change_account( - &mut self, - block_number: BlockNumber, - address: Address, - old: Account, - new: Account, - ) { - self.accounts.insert(address, Some(new)); - self.account_changes.insert(block_number, address, Some(old), Some(new)); - } - - /// Mark an account as destroyed. - pub fn destroy_account( - &mut self, - block_number: BlockNumber, - address: Address, - account: Account, - ) { - self.accounts.insert(address, None); - self.account_changes.insert(block_number, address, Some(account), None); - - let storage = self.storage.entry(address).or_default(); - storage.times_wiped += 1; - let wipe = - if storage.times_wiped == 1 { StorageWipe::Primary } else { StorageWipe::Secondary }; - - let wiped_storage = std::mem::take(&mut storage.storage); - self.storage_changes.insert_for_block_and_address( - block_number, - address, - wipe, - wiped_storage.into_iter(), - ); - } - - /// Add changed storage values to the post-state. - pub fn change_storage( - &mut self, - block_number: BlockNumber, - address: Address, - changeset: StorageChangeset, - ) { - self.storage - .entry(address) - .or_default() - .storage - .extend(changeset.iter().map(|(slot, (_, new))| (*slot, *new))); - self.storage_changes.insert_for_block_and_address( - block_number, - address, - StorageWipe::None, - changeset.into_iter().map(|(slot, (old, _))| (slot, old)), - ); - } - - /// Add new bytecode to the post-state. - pub fn add_bytecode(&mut self, code_hash: H256, bytecode: Bytecode) { - // Assumption: `insert` will override the value if present, but since the code hash for a - // given bytecode will always be the same, we are overriding with the same value. - // - // In other words: if this entry already exists, replacing the bytecode will replace with - // the same value, which is wasteful. - self.bytecode.entry(code_hash).or_insert(bytecode); - } - - /// Add a transaction receipt to the post-state. - /// - /// Transactions should always include their receipts in the post-state. - pub fn add_receipt(&mut self, block: BlockNumber, receipt: Receipt) { - self.receipts.entry(block).or_default().push(receipt); - } - - /// Write changeset history to the database. - pub fn write_history_to_db<'a, TX: DbTxMut<'a> + DbTx<'a>>( - &mut self, - tx: &TX, - tip: BlockNumber, - ) -> Result<(), DbError> { - // Write storage changes - tracing::trace!(target: "provider::post_state", "Writing storage changes"); - let mut storages_cursor = tx.cursor_dup_write::()?; - let mut storage_changeset_cursor = tx.cursor_dup_write::()?; - for (block_number, storage_changes) in - std::mem::take(&mut self.storage_changes).inner.into_iter() - { - if self.prune_modes.should_prune_storage_history(block_number, tip) { - continue - } - - for (address, mut storage) in storage_changes.into_iter() { - let storage_id = BlockNumberAddress((block_number, address)); - - // If the account was created and wiped at the same block, skip all storage changes - if storage.wipe.is_wiped() && - self.account_changes - .get(&block_number) - .and_then(|changes| changes.get(&address).map(|info| info.is_none())) - // No account info available, fallback to `false` - .unwrap_or_default() - { - continue - } - - // If we are writing the primary storage wipe transition, the pre-existing plain - // storage state has to be taken from the database and written to storage history. - // See [StorageWipe::Primary] for more details. - if storage.wipe.is_primary() { - if let Some((_, entry)) = storages_cursor.seek_exact(address)? { - tracing::trace!(target: "provider::post_state", ?storage_id, key = ?entry.key, "Storage wiped"); - let key = U256::from_be_bytes(entry.key.to_fixed_bytes()); - if !storage.storage.contains_key(&key) { - storage.storage.insert(entry.key.into(), entry.value); - } - - while let Some(entry) = storages_cursor.next_dup_val()? { - let key = U256::from_be_bytes(entry.key.to_fixed_bytes()); - if !storage.storage.contains_key(&key) { - storage.storage.insert(entry.key.into(), entry.value); - } - } - } - } - - for (slot, old_value) in storage.storage { - tracing::trace!(target: "provider::post_state", ?storage_id, ?slot, ?old_value, "Storage changed"); - storage_changeset_cursor.append_dup( - storage_id, - StorageEntry { key: H256(slot.to_be_bytes()), value: old_value }, - )?; - } - } - } - - // Write account changes - tracing::trace!(target: "provider::post_state", "Writing account changes"); - let mut account_changeset_cursor = tx.cursor_dup_write::()?; - for (block_number, account_changes) in - std::mem::take(&mut self.account_changes).inner.into_iter() - { - if self.prune_modes.should_prune_account_history(block_number, tip) { - continue - } - - for (address, info) in account_changes.into_iter() { - tracing::trace!(target: "provider::post_state", block_number, ?address, old = ?info, "Account changed"); - account_changeset_cursor - .append_dup(block_number, AccountBeforeTx { address, info })?; - } - } - - Ok(()) - } - - /// Write the post state to the database. - pub fn write_to_db<'a, TX: DbTxMut<'a> + DbTx<'a>>( - mut self, - tx: &TX, - tip: BlockNumber, - ) -> Result<(), Error> { - self.write_history_to_db(tx, tip)?; - - // Write new storage state - tracing::trace!(target: "provider::post_state", len = self.storage.len(), "Writing new storage state"); - let mut storages_cursor = tx.cursor_dup_write::()?; - for (address, storage) in self.storage.into_iter() { - // If the storage was wiped at least once, remove all previous entries from the - // database. - if storage.wiped() { - tracing::trace!(target: "provider::post_state", ?address, "Wiping storage from plain state"); - if storages_cursor.seek_exact(address)?.is_some() { - storages_cursor.delete_current_duplicates()?; - } - } - - for (key, value) in storage.storage { - tracing::trace!(target: "provider::post_state", ?address, ?key, "Updating plain state storage"); - let key: H256 = key.into(); - if let Some(entry) = storages_cursor.seek_by_key_subkey(address, key)? { - if entry.key == key { - storages_cursor.delete_current()?; - } - } - - if value != U256::ZERO { - storages_cursor.upsert(address, StorageEntry { key, value })?; - } - } - } - - // Write new account state - tracing::trace!(target: "provider::post_state", len = self.accounts.len(), "Writing new account state"); - let mut accounts_cursor = tx.cursor_write::()?; - for (address, account) in self.accounts.into_iter() { - if let Some(account) = account { - tracing::trace!(target: "provider::post_state", ?address, "Updating plain state account"); - accounts_cursor.upsert(address, account)?; - } else if accounts_cursor.seek_exact(address)?.is_some() { - tracing::trace!(target: "provider::post_state", ?address, "Deleting plain state account"); - accounts_cursor.delete_current()?; - } - } - - // Write bytecode - tracing::trace!(target: "provider::post_state", len = self.bytecode.len(), "Writing bytecodes"); - let mut bytecodes_cursor = tx.cursor_write::()?; - for (hash, bytecode) in self.bytecode.into_iter() { - bytecodes_cursor.upsert(hash, bytecode)?; - } - - // Write the receipts of the transactions if not pruned - tracing::trace!(target: "provider::post_state", len = self.receipts.len(), "Writing receipts"); - if !self.receipts.is_empty() && self.prune_modes.receipts != Some(PruneMode::Full) { - let mut bodies_cursor = tx.cursor_read::()?; - let mut receipts_cursor = tx.cursor_write::()?; - - let contract_log_pruner = self - .prune_modes - .receipts_log_filter - .group_by_block(tip, None) - .map_err(|e| Error::Custom(e.to_string()))?; - - // Empty implies that there is going to be - // addresses to include in the filter in a future block. None means there isn't any kind - // of configuration. - let mut address_filter: Option<(u64, Vec<&Address>)> = None; - - for (block, receipts) in self.receipts { - // [`PrunePart::Receipts`] takes priority over [`PrunePart::ContractLogs`] - if receipts.is_empty() || self.prune_modes.should_prune_receipts(block, tip) { - continue - } - - // All receipts from the last 128 blocks are required for blockchain tree, even with - // [`PrunePart::ContractLogs`]. - let prunable_receipts = - PruneMode::Distance(MINIMUM_PRUNING_DISTANCE).should_prune(block, tip); - - if prunable_receipts && !contract_log_pruner.is_empty() { - if address_filter.is_none() { - address_filter = Some((0, vec![])); - } - - // Get all addresses higher than the previous checked block up to the current - // one - if let Some((prev_block, filter)) = &mut address_filter { - for (_, addresses) in contract_log_pruner.range(*prev_block..=block) { - filter.extend_from_slice(addresses.as_slice()) - } - - *prev_block = block; - } - } - - let (_, body_indices) = - bodies_cursor.seek_exact(block)?.expect("body indices exist"); - let tx_range = body_indices.tx_num_range(); - assert_eq!(receipts.len(), tx_range.clone().count(), "Receipt length mismatch"); - - for (tx_num, receipt) in tx_range.zip(receipts) { - if prunable_receipts { - // If there is an address_filter, and it does not contain any of the - // contract addresses, then skip writing this - // receipt. - if let Some((_, filter)) = &address_filter { - if !receipt.logs.iter().any(|log| filter.contains(&&log.address)) { - continue - } - } - } - receipts_cursor.append(tx_num, receipt)?; - } - } - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{AccountReader, ProviderFactory}; - use reth_db::{ - database::Database, test_utils::create_test_rw_db, transaction::DbTx, DatabaseEnv, - }; - use reth_primitives::{proofs::EMPTY_ROOT, MAINNET}; - use reth_trie::test_utils::state_root; - use std::sync::Arc; - - // Ensure that the transition id is not incremented if postate is extended by another empty - // poststate. - #[test] - fn extend_empty() { - let mut a = PostState::new(); - - // Extend empty poststate with another empty poststate - a.extend(PostState::new()); - - // Add single transition and extend with empty poststate - a.create_account(1, Address::zero(), Account::default()); - a.extend(PostState::new()); - assert_eq!(a.account_changes.iter().fold(0, |len, (_, changes)| len + changes.len()), 1); - } - - #[test] - fn extend() { - let mut a = PostState::new(); - a.create_account(1, Address::zero(), Account::default()); - a.destroy_account(1, Address::zero(), Account::default()); - - assert_eq!(a.account_changes.iter().fold(0, |len, (_, changes)| len + changes.len()), 0); - - let mut b = PostState::new(); - b.create_account(2, Address::repeat_byte(0xff), Account::default()); - - assert_eq!(b.account_changes.iter().fold(0, |len, (_, changes)| len + changes.len()), 1); - - let mut c = a.clone(); - c.extend(b.clone()); - - assert_eq!(c.account_changes.iter().fold(0, |len, (_, changes)| len + changes.len()), 1); - - let mut d = PostState::new(); - d.create_account(3, Address::zero(), Account::default()); - d.destroy_account(3, Address::zero(), Account::default()); - c.extend(d); - assert_eq!(c.account_storage(&Address::zero()).unwrap().times_wiped, 2); - // Primary wipe occurred at block #1. - assert_eq!( - c.storage_changes.get(&1).unwrap().get(&Address::zero()).unwrap().wipe, - StorageWipe::Primary - ); - // Primary wipe occurred at block #3. - assert_eq!( - c.storage_changes.get(&3).unwrap().get(&Address::zero()).unwrap().wipe, - StorageWipe::Secondary - ); - } - - #[test] - fn revert_to() { - let mut state = PostState::new(); - let address1 = Address::repeat_byte(0); - let account1 = Account { nonce: 1, balance: U256::from(1), bytecode_hash: None }; - state.create_account(1, address1, account1); - state.create_account( - 2, - Address::repeat_byte(0xff), - Account { nonce: 2, balance: U256::from(2), bytecode_hash: None }, - ); - assert_eq!( - state.account_changes.iter().fold(0, |len, (_, changes)| len + changes.len()), - 2 - ); - - let revert_to = 1; - state.revert_to(revert_to); - assert_eq!(state.accounts, BTreeMap::from([(address1, Some(account1))])); - assert_eq!( - state.account_changes.iter().fold(0, |len, (_, changes)| len + changes.len()), - 1 - ); - } - - #[test] - fn wiped_revert() { - let address = Address::random(); - - let init_block_number = 0; - let init_account = Account { balance: U256::from(3), ..Default::default() }; - let init_slot = U256::from(1); - - // Create init state for demonstration purposes - // Block 0 - // Account: exists - // Storage: 0x01: 1 - let mut init_state = PostState::new(); - init_state.create_account(init_block_number, address, init_account); - init_state.change_storage( - init_block_number, - address, - BTreeMap::from([(init_slot, (U256::ZERO, U256::from(1)))]), - ); - assert_eq!( - init_state.storage.get(&address), - Some(&Storage { - storage: BTreeMap::from([(init_slot, U256::from(1))]), - times_wiped: 0 - }) - ); - - let mut post_state = PostState::new(); - // Block 1 - // - - // Block 2 - // Account: destroyed - // Storage: wiped - post_state.destroy_account(2, address, init_account); - assert!(post_state.storage.get(&address).unwrap().wiped()); - - // Block 3 - // Account: recreated - // Storage: wiped, then 0x01: 2 - let recreated_account = Account { balance: U256::from(4), ..Default::default() }; - post_state.create_account(3, address, recreated_account); - post_state.change_storage( - 3, - address, - BTreeMap::from([(init_slot, (U256::ZERO, U256::from(2)))]), - ); - assert!(post_state.storage.get(&address).unwrap().wiped()); - - // Revert to block 2 - post_state.revert_to(2); - assert!(post_state.storage.get(&address).unwrap().wiped()); - assert_eq!( - post_state.storage.get(&address).unwrap(), - &Storage { times_wiped: 1, storage: BTreeMap::default() } - ); - - // Revert to block 1 - post_state.revert_to(1); - assert_eq!(post_state.storage.get(&address), None); - } - - #[test] - fn split_at() { - let address1 = Address::random(); - let address2 = Address::random(); - let slot1 = U256::from(1); - let slot2 = U256::from(2); - - let mut state = PostState::new(); - // Block #1 - // Create account 1 and change its storage - // Assume account 2 already exists in the database and change storage for it - state.create_account(1, address1, Account::default()); - state.change_storage(1, address1, BTreeMap::from([(slot1, (U256::ZERO, U256::from(1)))])); - state.change_storage(1, address1, BTreeMap::from([(slot2, (U256::ZERO, U256::from(1)))])); - state.change_storage(1, address2, BTreeMap::from([(slot2, (U256::ZERO, U256::from(2)))])); - let block1_account_changes = (1, BTreeMap::from([(address1, None)])); - let block1_storage_changes = ( - 1, - BTreeMap::from([ - ( - address1, - StorageTransition { - storage: BTreeMap::from([(slot1, U256::ZERO), (slot2, U256::ZERO)]), - wipe: StorageWipe::None, - }, - ), - ( - address2, - StorageTransition { - storage: BTreeMap::from([(slot2, U256::ZERO)]), - wipe: StorageWipe::None, - }, - ), - ]), - ); - assert_eq!( - state.account_changes, - AccountChanges { inner: BTreeMap::from([block1_account_changes.clone()]), size: 1 } - ); - assert_eq!( - state.storage_changes, - StorageChanges { inner: BTreeMap::from([block1_storage_changes.clone()]), size: 3 } - ); - - // Block #2 - // Destroy account 1 - // Change storage for account 2 - state.destroy_account(2, address1, Account::default()); - state.change_storage( - 2, - address2, - BTreeMap::from([(slot2, (U256::from(2), U256::from(4)))]), - ); - let account_state_after_block_2 = state.accounts.clone(); - let storage_state_after_block_2 = state.storage.clone(); - let block2_account_changes = (2, BTreeMap::from([(address1, Some(Account::default()))])); - let block2_storage_changes = ( - 2, - BTreeMap::from([ - ( - address1, - StorageTransition { - storage: BTreeMap::from([(slot1, U256::from(1)), (slot2, U256::from(1))]), - wipe: StorageWipe::Primary, - }, - ), - ( - address2, - StorageTransition { - storage: BTreeMap::from([(slot2, U256::from(2))]), - wipe: StorageWipe::None, - }, - ), - ]), - ); - assert_eq!( - state.account_changes, - AccountChanges { - inner: BTreeMap::from([ - block1_account_changes.clone(), - block2_account_changes.clone() - ]), - size: 2 - } - ); - assert_eq!( - state.storage_changes, - StorageChanges { - inner: BTreeMap::from([ - block1_storage_changes.clone(), - block2_storage_changes.clone() - ]), - size: 6, - } - ); - - // Block #3 - // Recreate account 1 - // Destroy account 2 - state.create_account(3, address1, Account::default()); - state.change_storage( - 3, - address2, - BTreeMap::from([(slot2, (U256::from(4), U256::from(1)))]), - ); - state.destroy_account(3, address2, Account::default()); - let block3_account_changes = - (3, BTreeMap::from([(address1, None), (address2, Some(Account::default()))])); - let block3_storage_changes = ( - 3, - BTreeMap::from([( - address2, - StorageTransition { - storage: BTreeMap::from([(slot2, U256::from(4))]), - wipe: StorageWipe::Primary, - }, - )]), - ); - assert_eq!( - state.account_changes, - AccountChanges { - inner: BTreeMap::from([ - block1_account_changes.clone(), - block2_account_changes.clone(), - block3_account_changes.clone() - ]), - size: 4 - } - ); - assert_eq!( - state.storage_changes, - StorageChanges { - inner: BTreeMap::from([ - block1_storage_changes.clone(), - block2_storage_changes.clone(), - block3_storage_changes.clone() - ]), - size: 7, - } - ); - - // Block #4 - // Destroy account 1 again - state.destroy_account(4, address1, Account::default()); - let account_state_after_block_4 = state.accounts.clone(); - let storage_state_after_block_4 = state.storage.clone(); - let block4_account_changes = (4, BTreeMap::from([(address1, Some(Account::default()))])); - let block4_storage_changes = ( - 4, - BTreeMap::from([( - address1, - StorageTransition { storage: BTreeMap::default(), wipe: StorageWipe::Secondary }, - )]), - ); - - // Blocks #1-4 - // Account 1. Info: . Storage: . Times Wiped: 2. - // Account 2. Info: . Storage: . Times Wiped: 1. - assert_eq!(state.accounts, BTreeMap::from([(address1, None), (address2, None)])); - assert_eq!( - state.storage, - BTreeMap::from([ - (address1, Storage { times_wiped: 2, storage: BTreeMap::default() }), - (address2, Storage { times_wiped: 1, storage: BTreeMap::default() }) - ]) - ); - assert_eq!( - state.account_changes, - AccountChanges { - inner: BTreeMap::from([ - block1_account_changes.clone(), - block2_account_changes.clone(), - block3_account_changes.clone(), - block4_account_changes.clone(), - ]), - size: 5 - } - ); - assert_eq!( - state.storage_changes, - StorageChanges { - inner: BTreeMap::from([ - block1_storage_changes.clone(), - block2_storage_changes.clone(), - block3_storage_changes.clone(), - block4_storage_changes, - ]), - size: 7, - } - ); - - // Split state at block #2 - let mut state_1_2 = state.clone(); - let state_3_4 = state_1_2.split_at(2); - - // Blocks #1-2 - // Account 1. Info: . Storage: . - // Account 2. Info: exists. Storage: slot2 - 4. - assert_eq!(state_1_2.accounts, account_state_after_block_2); - assert_eq!(state_1_2.storage, storage_state_after_block_2); - assert_eq!( - state_1_2.account_changes, - AccountChanges { - inner: BTreeMap::from([block1_account_changes, block2_account_changes]), - size: 2 - } - ); - assert_eq!( - state_1_2.storage_changes, - StorageChanges { - inner: BTreeMap::from([block1_storage_changes, block2_storage_changes]), - size: 6, - } - ); - - // Plain state for blocks #3-4 should match plain state from blocks #1-4 - // Account 1. Info: . Storage: . - // Account 2. Info: exists. Storage: slot2 - 4. - assert_eq!(state_3_4.accounts, account_state_after_block_4); - // Not equal because the `times_wiped` value is different. - assert_ne!(state_3_4.storage, storage_state_after_block_4); - assert_eq!( - state_3_4.storage, - BTreeMap::from([ - (address1, Storage { times_wiped: 1, storage: BTreeMap::default() }), - (address2, Storage { times_wiped: 1, storage: BTreeMap::default() }) - ]) - ); - - // Account changes should match - assert_eq!( - state_3_4.account_changes, - AccountChanges { - inner: BTreeMap::from([block3_account_changes, block4_account_changes,]), - size: 3 - } - ); - // Storage changes should match except for the wipe flag being promoted to primary - assert_eq!( - state_3_4.storage_changes, - StorageChanges { - inner: BTreeMap::from([ - block3_storage_changes, - // Block #4. Wipe flag must be promoted to primary - ( - 4, - BTreeMap::from([( - address1, - StorageTransition { - storage: BTreeMap::default(), - wipe: StorageWipe::Primary - }, - )]), - ), - ]), - size: 1, - } - ) - } - - #[test] - fn receipts_split_at() { - let mut state = PostState::new(); - (1..=4).for_each(|block| { - state.add_receipt(block, Receipt::default()); - }); - let state2 = state.split_at(2); - assert_eq!( - state.receipts, - BTreeMap::from([(1, vec![Receipt::default()]), (2, vec![Receipt::default()])]) - ); - assert_eq!( - state2.receipts, - BTreeMap::from([(3, vec![Receipt::default()]), (4, vec![Receipt::default()])]) - ); - } - - #[test] - fn write_to_db_account_info() { - let db: Arc = create_test_rw_db(); - let factory = ProviderFactory::new(db, MAINNET.clone()); - let provider = factory.provider_rw().unwrap(); - - let mut post_state = PostState::new(); - - let address_a = Address::zero(); - let address_b = Address::repeat_byte(0xff); - - let account_a = Account { balance: U256::from(1), nonce: 1, bytecode_hash: None }; - let account_b = Account { balance: U256::from(2), nonce: 2, bytecode_hash: None }; - let account_b_changed = Account { balance: U256::from(3), nonce: 3, bytecode_hash: None }; - - // 0x00.. is created - post_state.create_account(1, address_a, account_a); - // 0x11.. is changed (balance + 1, nonce + 1) - post_state.change_account(1, address_b, account_b, account_b_changed); - post_state.write_to_db(provider.tx_ref(), 0).expect("Could not write post state to DB"); - - // Check plain state - assert_eq!( - provider.basic_account(address_a).expect("Could not read account state"), - Some(account_a), - "Account A state is wrong" - ); - assert_eq!( - provider.basic_account(address_b).expect("Could not read account state"), - Some(account_b_changed), - "Account B state is wrong" - ); - - // Check change set - let mut changeset_cursor = provider - .tx_ref() - .cursor_dup_read::() - .expect("Could not open changeset cursor"); - assert_eq!( - changeset_cursor.seek_exact(1).expect("Could not read account change set"), - Some((1, AccountBeforeTx { address: address_a, info: None })), - "Account A changeset is wrong" - ); - assert_eq!( - changeset_cursor.next_dup().expect("Changeset table is malformed"), - Some((1, AccountBeforeTx { address: address_b, info: Some(account_b) })), - "Account B changeset is wrong" - ); - - let mut post_state = PostState::new(); - // 0x11.. is destroyed - post_state.destroy_account(2, address_b, account_b_changed); - post_state - .write_to_db(provider.tx_ref(), 0) - .expect("Could not write second post state to DB"); - - // Check new plain state for account B - assert_eq!( - provider.basic_account(address_b).expect("Could not read account state"), - None, - "Account B should be deleted" - ); - - // Check change set - assert_eq!( - changeset_cursor.seek_exact(2).expect("Could not read account change set"), - Some((2, AccountBeforeTx { address: address_b, info: Some(account_b_changed) })), - "Account B changeset is wrong after deletion" - ); - } - - #[test] - fn write_to_db_storage() { - let db: Arc = create_test_rw_db(); - let tx = db.tx_mut().expect("Could not get database tx"); - - let mut post_state = PostState::new(); - - let address_a = Address::zero(); - let address_b = Address::repeat_byte(0xff); - - // 0x00 => 0 => 1 - // 0x01 => 0 => 2 - let storage_a_changeset = BTreeMap::from([ - (U256::from(0), (U256::from(0), U256::from(1))), - (U256::from(1), (U256::from(0), U256::from(2))), - ]); - - // 0x01 => 1 => 2 - let storage_b_changeset = BTreeMap::from([(U256::from(1), (U256::from(1), U256::from(2)))]); - - post_state.change_storage(1, address_a, storage_a_changeset); - post_state.change_storage(1, address_b, storage_b_changeset); - post_state.write_to_db(&tx, 0).expect("Could not write post state to DB"); - - // Check plain storage state - let mut storage_cursor = tx - .cursor_dup_read::() - .expect("Could not open plain storage state cursor"); - - assert_eq!( - storage_cursor.seek_exact(address_a).unwrap(), - Some((address_a, StorageEntry { key: H256::zero(), value: U256::from(1) })), - "Slot 0 for account A should be 1" - ); - assert_eq!( - storage_cursor.next_dup().unwrap(), - Some(( - address_a, - StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(2) } - )), - "Slot 1 for account A should be 2" - ); - assert_eq!( - storage_cursor.next_dup().unwrap(), - None, - "Account A should only have 2 storage slots" - ); - - assert_eq!( - storage_cursor.seek_exact(address_b).unwrap(), - Some(( - address_b, - StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(2) } - )), - "Slot 1 for account B should be 2" - ); - assert_eq!( - storage_cursor.next_dup().unwrap(), - None, - "Account B should only have 1 storage slot" - ); - - // Check change set - let mut changeset_cursor = tx - .cursor_dup_read::() - .expect("Could not open storage changeset cursor"); - assert_eq!( - changeset_cursor.seek_exact(BlockNumberAddress((1, address_a))).unwrap(), - Some(( - BlockNumberAddress((1, address_a)), - StorageEntry { key: H256::zero(), value: U256::from(0) } - )), - "Slot 0 for account A should have changed from 0" - ); - assert_eq!( - changeset_cursor.next_dup().unwrap(), - Some(( - BlockNumberAddress((1, address_a)), - StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(0) } - )), - "Slot 1 for account A should have changed from 0" - ); - assert_eq!( - changeset_cursor.next_dup().unwrap(), - None, - "Account A should only be in the changeset 2 times" - ); - - assert_eq!( - changeset_cursor.seek_exact(BlockNumberAddress((1, address_b))).unwrap(), - Some(( - BlockNumberAddress((1, address_b)), - StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(1) } - )), - "Slot 1 for account B should have changed from 1" - ); - assert_eq!( - changeset_cursor.next_dup().unwrap(), - None, - "Account B should only be in the changeset 1 time" - ); - - // Delete account A - let mut post_state = PostState::new(); - post_state.destroy_account(2, address_a, Account::default()); - post_state.write_to_db(&tx, 0).expect("Could not write post state to DB"); - - assert_eq!( - storage_cursor.seek_exact(address_a).unwrap(), - None, - "Account A should have no storage slots after deletion" - ); - - assert_eq!( - changeset_cursor.seek_exact(BlockNumberAddress((2, address_a))).unwrap(), - Some(( - BlockNumberAddress((2, address_a)), - StorageEntry { key: H256::zero(), value: U256::from(1) } - )), - "Slot 0 for account A should have changed from 1 on deletion" - ); - assert_eq!( - changeset_cursor.next_dup().unwrap(), - Some(( - BlockNumberAddress((2, address_a)), - StorageEntry { key: H256::from(U256::from(1).to_be_bytes()), value: U256::from(2) } - )), - "Slot 1 for account A should have changed from 2 on deletion" - ); - assert_eq!( - changeset_cursor.next_dup().unwrap(), - None, - "Account A should only be in the changeset 2 times on deletion" - ); - } - - #[test] - fn write_to_db_multiple_selfdestructs() { - let db: Arc = create_test_rw_db(); - let tx = db.tx_mut().expect("Could not get database tx"); - - let address1 = Address::random(); - - let mut init_state = PostState::new(); - init_state.create_account(0, address1, Account::default()); - init_state.change_storage( - 0, - address1, - // 0x00 => 0 => 1 - // 0x01 => 0 => 2 - BTreeMap::from([ - (U256::from(0), (U256::ZERO, U256::from(1))), - (U256::from(1), (U256::ZERO, U256::from(2))), - ]), - ); - init_state.write_to_db(&tx, 0).expect("Could not write init state to DB"); - - let mut post_state = PostState::new(); - post_state.change_storage( - 1, - address1, - // 0x00 => 1 => 2 - BTreeMap::from([(U256::from(0), (U256::from(1), U256::from(2)))]), - ); - post_state.destroy_account(2, address1, Account::default()); - post_state.create_account(3, address1, Account::default()); - post_state.change_storage( - 4, - address1, - // 0x00 => 0 => 2 - // 0x02 => 0 => 4 - // 0x06 => 0 => 6 - BTreeMap::from([ - (U256::from(0), (U256::ZERO, U256::from(2))), - (U256::from(2), (U256::ZERO, U256::from(4))), - (U256::from(6), (U256::ZERO, U256::from(6))), - ]), - ); - post_state.destroy_account(5, address1, Account::default()); - - // Create, change, destroy and recreate in the same block. - post_state.create_account(6, address1, Account::default()); - post_state.change_storage( - 6, - address1, - // 0x00 => 0 => 2 - BTreeMap::from([(U256::from(0), (U256::ZERO, U256::from(2)))]), - ); - post_state.destroy_account(6, address1, Account::default()); - post_state.create_account(6, address1, Account::default()); - - post_state.change_storage( - 7, - address1, - // 0x00 => 0 => 9 - BTreeMap::from([(U256::from(0), (U256::ZERO, U256::from(9)))]), - ); - - post_state.write_to_db(&tx, 0).expect("Could not write post state to DB"); - - let mut storage_changeset_cursor = tx - .cursor_dup_read::() - .expect("Could not open plain storage state cursor"); - let mut storage_changes = storage_changeset_cursor.walk_range(..).unwrap(); - - // Iterate through all storage changes - - // Block - // : - // ... - - // Block #0 - // 0x00: 0 - // 0x01: 0 - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((0, address1)), - StorageEntry { key: H256::from_low_u64_be(0), value: U256::ZERO } - ))) - ); - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((0, address1)), - StorageEntry { key: H256::from_low_u64_be(1), value: U256::ZERO } - ))) - ); - - // Block #1 - // 0x00: 1 - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((1, address1)), - StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(1) } - ))) - ); - - // Block #2 (destroyed) - // 0x00: 2 - // 0x01: 2 - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((2, address1)), - StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(2) } - ))) - ); - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((2, address1)), - StorageEntry { key: H256::from_low_u64_be(1), value: U256::from(2) } - ))) - ); - - // Block #3 - // no storage changes - - // Block #4 - // 0x00: 0 - // 0x02: 0 - // 0x06: 0 - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((4, address1)), - StorageEntry { key: H256::from_low_u64_be(0), value: U256::ZERO } - ))) - ); - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((4, address1)), - StorageEntry { key: H256::from_low_u64_be(2), value: U256::ZERO } - ))) - ); - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((4, address1)), - StorageEntry { key: H256::from_low_u64_be(6), value: U256::ZERO } - ))) - ); - - // Block #5 (destroyed) - // 0x00: 2 - // 0x02: 4 - // 0x06: 6 - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((5, address1)), - StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(2) } - ))) - ); - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((5, address1)), - StorageEntry { key: H256::from_low_u64_be(2), value: U256::from(4) } - ))) - ); - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((5, address1)), - StorageEntry { key: H256::from_low_u64_be(6), value: U256::from(6) } - ))) - ); - - // Block #6 - // no storage changes (only inter block changes) - - // Block #7 - // 0x00: 0 - assert_eq!( - storage_changes.next(), - Some(Ok(( - BlockNumberAddress((7, address1)), - StorageEntry { key: H256::from_low_u64_be(0), value: U256::ZERO } - ))) - ); - assert_eq!(storage_changes.next(), None); - } - - #[test] - fn reuse_selfdestructed_account() { - let address_a = Address::zero(); - - // 0x00 => 0 => 1 - // 0x01 => 0 => 2 - // 0x03 => 0 => 3 - let storage_changeset_one = BTreeMap::from([ - (U256::from(0), (U256::from(0), U256::from(1))), - (U256::from(1), (U256::from(0), U256::from(2))), - (U256::from(3), (U256::from(0), U256::from(3))), - ]); - // 0x00 => 0 => 3 - // 0x01 => 0 => 4 - let storage_changeset_two = BTreeMap::from([ - (U256::from(0), (U256::from(0), U256::from(3))), - (U256::from(2), (U256::from(0), U256::from(4))), - ]); - - let mut state = PostState::new(); - - // Create some storage for account A (simulates a contract deployment) - state.change_storage(1, address_a, storage_changeset_one); - // Next transition destroys the account (selfdestruct) - state.destroy_account(2, address_a, Account::default()); - // Next transition recreates account A with some storage (simulates a contract deployment) - state.change_storage(3, address_a, storage_changeset_two); - - // All the storage of account A has to be deleted in the database (wiped) - assert!( - state.account_storage(&address_a).expect("Account A should have some storage").wiped(), - "The wiped flag should be set to discard all pre-existing storage from the database" - ); - // Then, we must ensure that *only* the storage from the last transition will be written - assert_eq!( - state.account_storage(&address_a).expect("Account A should have some storage").storage, - BTreeMap::from([(U256::from(0), U256::from(3)), (U256::from(2), U256::from(4))]), - "Account A's storage should only have slots 0 and 2, and they should have values 3 and 4, respectively." - ); - } - - /// Checks that if an account is touched multiple times in the same block, - /// then the old value from the first change is kept and not overwritten. - /// - /// This is important because post states from different transactions in the same block may see - /// different states of the same account as the old value, but the changeset should reflect the - /// state of the account before the block. - #[test] - fn account_changesets_keep_old_values() { - let mut state = PostState::new(); - let block = 1; - let address = Address::repeat_byte(0); - - // A transaction in block 1 creates the account - state.create_account( - block, - address, - Account { nonce: 1, balance: U256::from(1), bytecode_hash: None }, - ); - - // A transaction in block 1 then changes the same account - state.change_account( - block, - address, - Account { nonce: 1, balance: U256::from(1), bytecode_hash: None }, - Account { nonce: 1, balance: U256::from(2), bytecode_hash: None }, - ); - - // The value in the changeset for the account should be `None` since this was an account - // creation - assert_eq!( - state.account_changes().inner, - BTreeMap::from([(block, BTreeMap::from([(address, None)]))]), - "The changeset for the account is incorrect" - ); - - // The latest state of the account should be: nonce = 1, balance = 2, bytecode hash = None - assert_eq!( - state.accounts.get(&address).unwrap(), - &Some(Account { nonce: 1, balance: U256::from(2), bytecode_hash: None }), - "The latest state of the account is incorrect" - ); - - // Another transaction in block 1 then changes the account yet again - state.change_account( - block, - address, - Account { nonce: 1, balance: U256::from(2), bytecode_hash: None }, - Account { nonce: 2, balance: U256::from(1), bytecode_hash: None }, - ); - - // The value in the changeset for the account should still be `None` - assert_eq!( - state.account_changes().inner, - BTreeMap::from([(block, BTreeMap::from([(address, None)]))]), - "The changeset for the account is incorrect" - ); - - // The latest state of the account should be: nonce = 2, balance = 1, bytecode hash = None - assert_eq!( - state.accounts.get(&address).unwrap(), - &Some(Account { nonce: 2, balance: U256::from(1), bytecode_hash: None }), - "The latest state of the account is incorrect" - ); - } - - /// Checks that if a storage slot is touched multiple times in the same block, - /// then the old value from the first change is kept and not overwritten. - /// - /// This is important because post states from different transactions in the same block may see - /// different states of the same account as the old value, but the changeset should reflect the - /// state of the account before the block. - #[test] - fn storage_changesets_keep_old_values() { - let mut state = PostState::new(); - let block = 1; - let address = Address::repeat_byte(0); - - // A transaction in block 1 changes: - // - // Slot 0: 0 -> 1 - // Slot 1: 3 -> 4 - state.change_storage( - block, - address, - BTreeMap::from([ - (U256::from(0), (U256::from(0), U256::from(1))), - (U256::from(1), (U256::from(3), U256::from(4))), - ]), - ); - - // A transaction in block 1 changes: - // - // Slot 0: 1 -> 2 - // Slot 1: 4 -> 5 - state.change_storage( - block, - address, - BTreeMap::from([ - (U256::from(0), (U256::from(1), U256::from(2))), - (U256::from(1), (U256::from(4), U256::from(5))), - ]), - ); - - // The storage changeset for the account in block 1 should now be: - // - // Slot 0: 0 (the value before the first tx in the block) - // Slot 1: 3 - assert_eq!( - state.storage_changes().inner, - BTreeMap::from([( - block, - BTreeMap::from([( - address, - StorageTransition { - storage: BTreeMap::from([ - (U256::from(0), U256::from(0)), - (U256::from(1), U256::from(3)) - ]), - wipe: StorageWipe::None, - } - )]) - )]), - "The changeset for the storage is incorrect" - ); - - // The latest state of the storage should be: - // - // Slot 0: 2 - // Slot 1: 5 - assert_eq!( - state.storage(), - &BTreeMap::from([( - address, - Storage { - storage: BTreeMap::from([ - (U256::from(0), U256::from(2)), - (U256::from(1), U256::from(5)) - ]), - times_wiped: 0, - } - )]), - "The latest state of the storage is incorrect" - ); - } - - /// Tests that the oldest value for changesets is kept when extending a post state from another - /// post state. - /// - /// In other words, this tests the same cases as `account_changesets_keep_old_values` and - /// `storage_changesets_keep_old_values`, but in the case where accounts/slots are changed in - /// different post states that are then merged. - #[test] - fn extending_preserves_changesets() { - let mut a = PostState::new(); - let mut b = PostState::new(); - let block = 1; - let address = Address::repeat_byte(0); - - // The first state (a) represents a transaction that creates an account with some storage - // slots - // - // Expected changeset state: - // - Account: None - // - Storage: Slot 0: 0 - a.create_account( - block, - address, - Account { nonce: 1, balance: U256::from(1), bytecode_hash: None }, - ); - a.change_storage( - block, - address, - BTreeMap::from([(U256::from(0), (U256::from(0), U256::from(1)))]), - ); - assert_eq!( - a.account_changes().inner, - BTreeMap::from([(block, BTreeMap::from([(address, None)]))]), - "The changeset for the account is incorrect in state A" - ); - assert_eq!( - a.storage_changes().inner, - BTreeMap::from([( - block, - BTreeMap::from([( - address, - StorageTransition { - storage: BTreeMap::from([(U256::from(0), U256::from(0)),]), - wipe: StorageWipe::None, - } - )]) - )]), - "The changeset for the storage is incorrect in state A" - ); - - // The second state (b) represents a transaction that changes some slots and account info - // for the same account - // - // Expected changeset state is the same, i.e.: - // - Account: None - // - Storage: Slot 0: 0 - b.change_account( - block, - address, - Account { nonce: 1, balance: U256::from(1), bytecode_hash: None }, - Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }, - ); - b.change_storage( - block, - address, - BTreeMap::from([(U256::from(0), (U256::from(1), U256::from(2)))]), - ); - assert_eq!( - b.account_changes().inner, - BTreeMap::from([( - block, - BTreeMap::from([( - address, - Some(Account { nonce: 1, balance: U256::from(1), bytecode_hash: None }) - )]) - )]), - "The changeset for the account is incorrect in state B" - ); - assert_eq!( - b.storage_changes().inner, - BTreeMap::from([( - block, - BTreeMap::from([( - address, - StorageTransition { - storage: BTreeMap::from([(U256::from(0), U256::from(1)),]), - wipe: StorageWipe::None, - } - )]) - )]), - "The changeset for the storage is incorrect in state B" - ); - - // Now we merge the states - a.extend(b); - - // The expected state is: - // - // Changesets: - // - Account: None - // - Storage: Slot 0: 0 - // - // Accounts: - // - Nonce 1, balance 10, bytecode hash None - // - // Storage: - // - Slot 0: 2 - assert_eq!( - a.account_changes().inner, - BTreeMap::from([(block, BTreeMap::from([(address, None)]))]), - "The changeset for the account is incorrect in the merged state" - ); - assert_eq!( - a.storage_changes().inner, - BTreeMap::from([( - block, - BTreeMap::from([( - address, - StorageTransition { - storage: BTreeMap::from([(U256::from(0), U256::from(0)),]), - wipe: StorageWipe::None, - } - )]) - )]), - "The changeset for the storage is incorrect in the merged state" - ); - assert_eq!( - a.accounts(), - &BTreeMap::from([( - address, - Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }) - )]), - "The state of accounts in the merged state is incorrect" - ); - assert_eq!( - a.storage(), - &BTreeMap::from([( - address, - Storage { - storage: BTreeMap::from([(U256::from(0), U256::from(2)),]), - times_wiped: 0, - } - )]), - "The latest state of the storage is incorrect in the merged state" - ); - } - - #[test] - fn collapsible_account_changes() { - let address = Address::random(); - let mut post_state = PostState::default(); - - // Create account on block #1 - let account_at_block_1 = Account { nonce: 1, ..Default::default() }; - post_state.create_account(1, address, account_at_block_1); - - // Modify account on block #2 and return it to original state. - post_state.change_account( - 2, - address, - Account { nonce: 1, ..Default::default() }, - Account { nonce: 1, balance: U256::from(1), ..Default::default() }, - ); - post_state.change_account( - 2, - address, - Account { nonce: 1, balance: U256::from(1), ..Default::default() }, - Account { nonce: 1, ..Default::default() }, - ); - - assert_eq!(post_state.account_changes().get(&2).and_then(|ch| ch.get(&address)), None); - } - - #[test] - fn empty_post_state_state_root() { - let db: Arc = create_test_rw_db(); - let tx = db.tx().unwrap(); - - let post_state = PostState::new(); - let state_root = post_state.state_root_slow(&tx).expect("Could not get state root"); - assert_eq!(state_root, EMPTY_ROOT); - } - - #[test] - fn post_state_state_root() { - let mut state: BTreeMap)> = (0..10) - .map(|key| { - let account = Account { nonce: 1, balance: U256::from(key), bytecode_hash: None }; - let storage = - (1..11).map(|key| (H256::from_low_u64_be(key), U256::from(key))).collect(); - (Address::from_low_u64_be(key), (account, storage)) - }) - .collect(); - - let db: Arc = create_test_rw_db(); - - // insert initial state to the database - db.update(|tx| { - for (address, (account, storage)) in state.iter() { - let hashed_address = keccak256(address); - tx.put::(hashed_address, *account).unwrap(); - for (slot, value) in storage { - tx.put::( - hashed_address, - StorageEntry { key: keccak256(slot), value: *value }, - ) - .unwrap(); - } - } - - let (_, updates) = StateRoot::new(tx).root_with_updates().unwrap(); - updates.flush(tx).unwrap(); - }) - .unwrap(); - - let block_number = 1; - let tx = db.tx().unwrap(); - let mut post_state = PostState::new(); - - // database only state root is correct - assert_eq!( - post_state.state_root_slow(&tx).unwrap(), - state_root( - state - .clone() - .into_iter() - .map(|(address, (account, storage))| (address, (account, storage.into_iter()))) - ) - ); - - // destroy account 1 - let address_1 = Address::from_low_u64_be(1); - let account_1_old = state.remove(&address_1).unwrap(); - post_state.destroy_account(block_number, address_1, account_1_old.0); - assert_eq!( - post_state.state_root_slow(&tx).unwrap(), - state_root( - state - .clone() - .into_iter() - .map(|(address, (account, storage))| (address, (account, storage.into_iter()))) - ) - ); - - // change slot 2 in account 2 - let address_2 = Address::from_low_u64_be(2); - let slot_2 = U256::from(2); - let slot_2_key = H256(slot_2.to_be_bytes()); - let address_2_slot_2_old_value = - *state.get(&address_2).unwrap().1.get(&slot_2_key).unwrap(); - let address_2_slot_2_new_value = U256::from(100); - state.get_mut(&address_2).unwrap().1.insert(slot_2_key, address_2_slot_2_new_value); - post_state.change_storage( - block_number, - address_2, - BTreeMap::from([(slot_2, (address_2_slot_2_old_value, address_2_slot_2_new_value))]), - ); - assert_eq!( - post_state.state_root_slow(&tx).unwrap(), - state_root( - state - .clone() - .into_iter() - .map(|(address, (account, storage))| (address, (account, storage.into_iter()))) - ) - ); - - // change balance of account 3 - let address_3 = Address::from_low_u64_be(3); - let address_3_account_old = state.get(&address_3).unwrap().0; - let address_3_account_new = Account { balance: U256::from(24), ..address_3_account_old }; - state.get_mut(&address_3).unwrap().0.balance = address_3_account_new.balance; - post_state.change_account( - block_number, - address_3, - address_3_account_old, - address_3_account_new, - ); - assert_eq!( - post_state.state_root_slow(&tx).unwrap(), - state_root( - state - .clone() - .into_iter() - .map(|(address, (account, storage))| (address, (account, storage.into_iter()))) - ) - ); - - // change nonce of account 4 - let address_4 = Address::from_low_u64_be(4); - let address_4_account_old = state.get(&address_4).unwrap().0; - let address_4_account_new = Account { nonce: 128, ..address_4_account_old }; - state.get_mut(&address_4).unwrap().0.nonce = address_4_account_new.nonce; - post_state.change_account( - block_number, - address_4, - address_4_account_old, - address_4_account_new, - ); - assert_eq!( - post_state.state_root_slow(&tx).unwrap(), - state_root( - state - .clone() - .into_iter() - .map(|(address, (account, storage))| (address, (account, storage.into_iter()))) - ) - ); - - // recreate account 1 - let account_1_new = - Account { nonce: 56, balance: U256::from(123), bytecode_hash: Some(H256::random()) }; - state.insert(address_1, (account_1_new, BTreeMap::default())); - post_state.create_account(block_number, address_1, account_1_new); - assert_eq!( - post_state.state_root_slow(&tx).unwrap(), - state_root( - state - .clone() - .into_iter() - .map(|(address, (account, storage))| (address, (account, storage.into_iter()))) - ) - ); - - // update storage for account 1 - let slot_20 = U256::from(20); - let slot_20_key = H256(slot_20.to_be_bytes()); - let account_1_slot_20_value = U256::from(12345); - state.get_mut(&address_1).unwrap().1.insert(slot_20_key, account_1_slot_20_value); - post_state.change_storage( - block_number, - address_1, - BTreeMap::from([(slot_20, (U256::from(0), account_1_slot_20_value))]), - ); - assert_eq!( - post_state.state_root_slow(&tx).unwrap(), - state_root( - state - .clone() - .into_iter() - .map(|(address, (account, storage))| (address, (account, storage.into_iter()))) - ) - ); - } -} diff --git a/crates/storage/provider/src/post_state/storage.rs b/crates/storage/provider/src/post_state/storage.rs deleted file mode 100644 index 08e5629b2..000000000 --- a/crates/storage/provider/src/post_state/storage.rs +++ /dev/null @@ -1,156 +0,0 @@ -use derive_more::Deref; -use reth_primitives::{Address, BlockNumber, U256}; -use std::collections::{btree_map::Entry, BTreeMap}; - -/// Storage for an account with the old and new values for each slot: (slot -> (old, new)). -pub type StorageChangeset = BTreeMap; - -/// The storage state of the account before the state transition. -#[derive(Debug, Default, Clone, Eq, PartialEq)] -pub struct StorageTransition { - /// The indicator of the storage wipe. - pub wipe: StorageWipe, - /// The storage slots. - pub storage: BTreeMap, -} - -/// The indicator of the storage wipe. -#[derive(Debug, Default, Clone, Eq, PartialEq)] -pub enum StorageWipe { - /// The storage was not wiped at this change. - #[default] - None, - /// The storage was wiped for the first time in the current in-memory state. - /// - /// When writing history to the database, on the primary storage wipe the pre-existing storage - /// will be inserted as the storage state before this transition. - Primary, - /// The storage had been already wiped before. - Secondary, -} - -impl StorageWipe { - /// Returns `true` if the wipe occurred at this transition. - pub fn is_wiped(&self) -> bool { - matches!(self, Self::Primary | Self::Secondary) - } - - /// Returns `true` if the primary wiped occurred at this transition. - /// See [StorageWipe::Primary] for more details. - pub fn is_primary(&self) -> bool { - matches!(self, Self::Primary) - } -} - -/// Latest storage state for the account. -/// -/// # Wiped Storage -/// -/// The `times_wiped` field indicates the number of times the storage was wiped in this poststate. -/// -/// If `times_wiped` is greater than 0, then the account was selfdestructed at some point, and the -/// values contained in `storage` should be the only values written to the database. -#[derive(Debug, Default, Clone, Eq, PartialEq)] -pub struct Storage { - /// The number of times the storage was wiped. - pub times_wiped: u64, - /// The storage slots. - pub storage: BTreeMap, -} - -impl Storage { - /// Returns `true` if the storage was wiped at any point. - pub fn wiped(&self) -> bool { - self.times_wiped > 0 - } -} - -/// A mapping of `block -> account -> slot -> old value` that represents what slots were changed, -/// and what their values were prior to that change. -#[derive(Default, Clone, Eq, PartialEq, Debug, Deref)] -pub struct StorageChanges { - /// The inner mapping of block changes. - #[deref] - pub inner: BTreeMap>, - /// Hand tracked change size. - pub size: usize, -} - -impl StorageChanges { - /// Insert storage entries for specified block number and address. - pub fn insert_for_block_and_address( - &mut self, - block: BlockNumber, - address: Address, - wipe: StorageWipe, - storage: I, - ) where - I: Iterator, - { - let block_entry = self.inner.entry(block).or_default(); - let storage_entry = block_entry.entry(address).or_default(); - if wipe.is_wiped() { - storage_entry.wipe = wipe; - } - for (slot, value) in storage { - if let Entry::Vacant(entry) = storage_entry.storage.entry(slot) { - entry.insert(value); - self.size += 1; - } - } - } - - /// Drain and return any entries above the target block number. - pub fn drain_above( - &mut self, - target_block: BlockNumber, - ) -> BTreeMap> { - let mut evicted = BTreeMap::new(); - self.inner.retain(|block_number, storages| { - if *block_number > target_block { - // This is fine, because it's called only on post state splits - self.size -= - storages.iter().fold(0, |acc, (_, storage)| acc + storage.storage.len()); - evicted.insert(*block_number, storages.clone()); - false - } else { - true - } - }); - evicted - } - - /// Retain entries only above specified block number. - /// - /// # Returns - /// - /// The update mapping of address to the number of times it was wiped. - pub fn retain_above(&mut self, target_block: BlockNumber) -> BTreeMap { - let mut updated_times_wiped: BTreeMap = BTreeMap::default(); - self.inner.retain(|block_number, storages| { - if *block_number > target_block { - for (address, storage) in storages.iter_mut() { - if storage.wipe.is_wiped() { - let times_wiped_entry = updated_times_wiped.entry(*address).or_default(); - storage.wipe = if *times_wiped_entry == 0 { - // No wipe was observed, promote the wipe to primary even if it was - // secondary before. - StorageWipe::Primary - } else { - // We already observed the storage wipe for this address - StorageWipe::Secondary - }; - *times_wiped_entry += 1; - } - } - true - } else { - // This is fine, because it's called only on post state splits - self.size -= - storages.iter().fold(0, |acc, (_, storage)| acc + storage.storage.len()); - false - } - }); - updated_times_wiped - } -} diff --git a/crates/storage/provider/src/providers/post_state_provider.rs b/crates/storage/provider/src/providers/bundle_state_provider.rs similarity index 59% rename from crates/storage/provider/src/providers/post_state_provider.rs rename to crates/storage/provider/src/providers/bundle_state_provider.rs index 517ff50af..2fa536742 100644 --- a/crates/storage/provider/src/providers/post_state_provider.rs +++ b/crates/storage/provider/src/providers/bundle_state_provider.rs @@ -1,30 +1,30 @@ use crate::{ - AccountReader, BlockHashReader, PostState, PostStateDataProvider, StateProvider, - StateRootProvider, + bundle_state::BundleStateWithReceipts, AccountReader, BlockHashReader, BundleStateDataProvider, + StateProvider, StateRootProvider, }; use reth_interfaces::{provider::ProviderError, Result}; -use reth_primitives::{Account, Address, BlockNumber, Bytecode, Bytes, H256, U256}; +use reth_primitives::{Account, Address, BlockNumber, Bytecode, Bytes, H256}; -/// A state provider that either resolves to data in a wrapped [`crate::PostState`], or an -/// underlying state provider. -pub struct PostStateProvider { +/// A state provider that either resolves to data in a wrapped [`crate::BundleStateWithReceipts`], +/// or an underlying state provider. +pub struct BundleStateProvider { /// The inner state provider. pub(crate) state_provider: SP, /// Post state data, - pub(crate) post_state_data_provider: PSDP, + pub(crate) post_state_data_provider: BSDP, } -impl PostStateProvider { +impl BundleStateProvider { /// Create new post-state provider - pub fn new(state_provider: SP, post_state_data_provider: PSDP) -> Self { + pub fn new(state_provider: SP, post_state_data_provider: BSDP) -> Self { Self { state_provider, post_state_data_provider } } } /* Implement StateProvider traits */ -impl BlockHashReader - for PostStateProvider +impl BlockHashReader + for BundleStateProvider { fn block_hash(&self, block_number: BlockNumber) -> Result> { let block_hash = self.post_state_data_provider.block_hash(block_number); @@ -39,48 +39,48 @@ impl BlockHashReader } } -impl AccountReader for PostStateProvider { +impl AccountReader + for BundleStateProvider +{ fn basic_account(&self, address: Address) -> Result> { if let Some(account) = self.post_state_data_provider.state().account(&address) { - Ok(*account) + Ok(account) } else { self.state_provider.basic_account(address) } } } -impl StateRootProvider - for PostStateProvider +impl StateRootProvider + for BundleStateProvider { - fn state_root(&self, post_state: PostState) -> Result { + fn state_root(&self, post_state: BundleStateWithReceipts) -> Result { let mut state = self.post_state_data_provider.state().clone(); state.extend(post_state); self.state_provider.state_root(state) } } -impl StateProvider for PostStateProvider { +impl StateProvider + for BundleStateProvider +{ fn storage( &self, account: Address, storage_key: reth_primitives::StorageKey, ) -> Result> { - if let Some(storage) = self.post_state_data_provider.state().account_storage(&account) { - if let Some(value) = - storage.storage.get(&U256::from_be_bytes(storage_key.to_fixed_bytes())) - { - return Ok(Some(*value)) - } else if storage.wiped() { - return Ok(Some(U256::ZERO)) - } + let u256_storage_key = storage_key.into(); + if let Some(value) = + self.post_state_data_provider.state().storage(&account, u256_storage_key) + { + return Ok(Some(value)) } self.state_provider.storage(account, storage_key) } fn bytecode_by_hash(&self, code_hash: H256) -> Result> { - if let Some(bytecode) = self.post_state_data_provider.state().bytecode(&code_hash).cloned() - { + if let Some(bytecode) = self.post_state_data_provider.state().bytecode(&code_hash) { return Ok(Some(bytecode)) } diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index a599ee47f..fb1632cee 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1,12 +1,12 @@ use crate::{ - post_state::StorageChangeset, + bundle_state::{BundleStateInit, BundleStateWithReceipts, RevertsInit}, traits::{ AccountExtReader, BlockSource, ChangeSetReader, ReceiptProvider, StageCheckpointWriter, }, AccountReader, BlockExecutionWriter, BlockHashReader, BlockNumReader, BlockReader, BlockWriter, - EvmEnvProvider, HashingWriter, HeaderProvider, HistoryWriter, PostState, ProviderError, - PruneCheckpointReader, PruneCheckpointWriter, StageCheckpointReader, StorageReader, - TransactionsProvider, WithdrawalsProvider, + Chain, EvmEnvProvider, HashingWriter, HeaderProvider, HistoryWriter, OriginalValuesKnown, + ProviderError, PruneCheckpointReader, PruneCheckpointWriter, StageCheckpointReader, + StorageReader, TransactionsProvider, WithdrawalsProvider, }; use itertools::{izip, Itertools}; use reth_db::{ @@ -43,7 +43,7 @@ use reth_revm_primitives::{ }; use reth_trie::{prefix_set::PrefixSetMut, StateRoot}; use std::{ - collections::{btree_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, + collections::{hash_map, BTreeMap, BTreeSet, HashMap, HashSet}, fmt::Debug, ops::{Deref, DerefMut, Range, RangeBounds, RangeInclusive}, sync::Arc, @@ -197,8 +197,12 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> DatabaseProvider<'this, TX> { // TODO(joshie) TEMPORARY should be moved to trait providers - /// Traverse over changesets and plain state and recreate the [`PostState`]s for the given range - /// of blocks. + /// Unwind or peek at last N blocks of state recreating the [`BundleStateWithReceipts`]. + /// + /// If UNWIND it set to true tip and latest state will be unwind + /// and returned back with all the blocks + /// + /// If UNWIND is false we will just read the state/blocks and return them. /// /// 1. Iterate over the [BlockBodyIndices][tables::BlockBodyIndices] table to get all /// the transaction ids. @@ -217,16 +221,14 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> DatabaseProvider<'this, TX> { /// 1. Take the old value from the changeset /// 2. Take the new value from the local state /// 3. Set the local state to the value in the changeset - /// - /// If `TAKE` is `true`, the local state will be written to the plain state tables. - /// 5. Get all receipts from table - fn get_take_block_execution_result_range( + fn unwind_or_peek_state( &self, range: RangeInclusive, - ) -> Result> { + ) -> Result { if range.is_empty() { - return Ok(Vec::new()) + return Ok(BundleStateWithReceipts::default()) } + let start_block_number = *range.start(); // We are not removing block meta as it is used to get block changesets. let block_bodies = self.get_or_take::(range.clone())?; @@ -236,146 +238,139 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> DatabaseProvider<'this, TX> { block_bodies.first().expect("already checked if there are blocks").1.first_tx_num(); let to_transaction_num = block_bodies.last().expect("already checked if there are blocks").1.last_tx_num(); - let receipts = - self.get_or_take::(from_transaction_num..=to_transaction_num)?; let storage_range = BlockNumberAddress::range(range.clone()); let storage_changeset = - self.get_or_take::(storage_range)?; - let account_changeset = self.get_or_take::(range)?; + self.get_or_take::(storage_range)?; + let account_changeset = self.get_or_take::(range)?; // iterate previous value and get plain state value to create changeset // Double option around Account represent if Account state is know (first option) and // account is removed (Second Option) - type LocalPlainState = BTreeMap>, BTreeMap)>; - let mut local_plain_state: LocalPlainState = BTreeMap::new(); - - // iterate in reverse and get plain state. - - // Bundle execution changeset to its particular transaction and block - let mut block_states = - BTreeMap::from_iter(block_bodies.iter().map(|(num, _)| (*num, PostState::default()))); + let mut state: BundleStateInit = HashMap::new(); + // This is not working for blocks that are not at tip. as plain state is not the last + // state of end range. We should rename the functions or add support to access + // History state. Accessing history state can be tricky but we are not gaining + // anything. let mut plain_accounts_cursor = self.tx.cursor_write::()?; let mut plain_storage_cursor = self.tx.cursor_dup_write::()?; + let mut reverts: RevertsInit = HashMap::new(); + // add account changeset changes for (block_number, account_before) in account_changeset.into_iter().rev() { let AccountBeforeTx { info: old_info, address } = account_before; - let new_info = match local_plain_state.entry(address) { - Entry::Vacant(entry) => { - let new_account = plain_accounts_cursor.seek_exact(address)?.map(|kv| kv.1); - entry.insert((Some(old_info), BTreeMap::new())); - new_account + match state.entry(address) { + hash_map::Entry::Vacant(entry) => { + let new_info = plain_accounts_cursor.seek_exact(address)?.map(|kv| kv.1); + entry.insert((old_info, new_info, HashMap::new())); } - Entry::Occupied(mut entry) => { - let new_account = std::mem::replace(&mut entry.get_mut().0, Some(old_info)); - new_account.expect("As we are stacking account first, account would always be Some(Some) or Some(None)") + hash_map::Entry::Occupied(mut entry) => { + // overwrite old account state. + entry.get_mut().0 = old_info; } - }; - - let post_state = block_states.entry(block_number).or_default(); - match (old_info, new_info) { - (Some(old), Some(new)) => { - if new != old { - post_state.change_account(block_number, address, old, new); - } else { - unreachable!("Junk data in database: an account changeset did not represent any change"); - } - } - (None, Some(account)) => post_state.create_account(block_number, address, account), - (Some(old), None) => - post_state.destroy_account(block_number, address, old), - (None, None) => unreachable!("Junk data in database: an account changeset transitioned from no account to no account"), - }; + } + // insert old info into reverts. + reverts.entry(block_number).or_default().entry(address).or_default().0 = Some(old_info); } // add storage changeset changes - let mut storage_changes: BTreeMap = BTreeMap::new(); - for (block_and_address, storage_entry) in storage_changeset.into_iter().rev() { - let BlockNumberAddress((_, address)) = block_and_address; - let new_storage = - match local_plain_state.entry(address).or_default().1.entry(storage_entry.key) { - Entry::Vacant(entry) => { - let new_storage = plain_storage_cursor - .seek_by_key_subkey(address, storage_entry.key)? - .filter(|storage| storage.key == storage_entry.key) - .unwrap_or_default(); - entry.insert(storage_entry.value); - new_storage.value - } - Entry::Occupied(mut entry) => { - std::mem::replace(entry.get_mut(), storage_entry.value) - } - }; - storage_changes.entry(block_and_address).or_default().insert( - U256::from_be_bytes(storage_entry.key.0), - (storage_entry.value, new_storage), - ); + for (block_and_address, old_storage) in storage_changeset.into_iter().rev() { + let BlockNumberAddress((block_number, address)) = block_and_address; + // get account state or insert from plain state. + let account_state = match state.entry(address) { + hash_map::Entry::Vacant(entry) => { + let present_info = plain_accounts_cursor.seek_exact(address)?.map(|kv| kv.1); + entry.insert((present_info, present_info, HashMap::new())) + } + hash_map::Entry::Occupied(entry) => entry.into_mut(), + }; + + // match storage. + match account_state.2.entry(old_storage.key) { + hash_map::Entry::Vacant(entry) => { + let new_storage = plain_storage_cursor + .seek_by_key_subkey(address, old_storage.key)? + .filter(|storage| storage.key == old_storage.key) + .unwrap_or_default(); + entry.insert((old_storage.value, new_storage.value)); + } + hash_map::Entry::Occupied(mut entry) => { + entry.get_mut().0 = old_storage.value; + } + }; + + reverts + .entry(block_number) + .or_default() + .entry(address) + .or_default() + .1 + .push(old_storage); } - for (BlockNumberAddress((block_number, address)), storage_changeset) in - storage_changes.into_iter() - { - block_states.entry(block_number).or_default().change_storage( - block_number, - address, - storage_changeset, - ); - } - - if TAKE { + if UNWIND { // iterate over local plain state remove all account and all storages. - for (address, (account, storage)) in local_plain_state.into_iter() { - // revert account - if let Some(account) = account { - let existing_entry = plain_accounts_cursor.seek_exact(address)?; - if let Some(account) = account { - plain_accounts_cursor.upsert(address, account)?; + for (address, (old_account, new_account, storage)) in state.iter() { + // revert account if needed. + if old_account != new_account { + let existing_entry = plain_accounts_cursor.seek_exact(*address)?; + if let Some(account) = old_account { + plain_accounts_cursor.upsert(*address, *account)?; } else if existing_entry.is_some() { plain_accounts_cursor.delete_current()?; } } // revert storages - for (storage_key, storage_value) in storage.into_iter() { - let storage_entry = StorageEntry { key: storage_key, value: storage_value }; + for (storage_key, (old_storage_value, _new_storage_value)) in storage { + let storage_entry = + StorageEntry { key: *storage_key, value: *old_storage_value }; // delete previous value // TODO: This does not use dupsort features if plain_storage_cursor - .seek_by_key_subkey(address, storage_key)? - .filter(|s| s.key == storage_key) + .seek_by_key_subkey(*address, *storage_key)? + .filter(|s| s.key == *storage_key) .is_some() { plain_storage_cursor.delete_current()? } - // TODO: This does not use dupsort features // insert value if needed - if storage_value != U256::ZERO { - plain_storage_cursor.upsert(address, storage_entry)?; + if *old_storage_value != U256::ZERO { + plain_storage_cursor.upsert(*address, storage_entry)?; } } } } // iterate over block body and create ExecutionResult - let mut receipt_iter = receipts.into_iter(); + let mut receipt_iter = self + .get_or_take::(from_transaction_num..=to_transaction_num)? + .into_iter(); + let mut receipts = Vec::new(); // loop break if we are at the end of the blocks. - for (block_number, block_body) in block_bodies.into_iter() { + for (_, block_body) in block_bodies.into_iter() { + let mut block_receipts = Vec::with_capacity(block_body.tx_count as usize); for _ in block_body.tx_num_range() { if let Some((_, receipt)) = receipt_iter.next() { - block_states - .entry(block_number) - .or_default() - .add_receipt(block_number, receipt); + block_receipts.push(Some(receipt)); } } + receipts.push(block_receipts); } - Ok(block_states.into_values().collect()) + + Ok(BundleStateWithReceipts::new_init( + state, + reverts, + Vec::new(), + receipts, + start_block_number, + )) } /// Return list of entries from table @@ -1826,11 +1821,12 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> HistoryWriter for DatabaseProvider } impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockExecutionWriter for DatabaseProvider<'this, TX> { + /// Return range of blocks and its execution result fn get_or_take_block_and_execution_range( &self, chain_spec: &ChainSpec, range: RangeInclusive, - ) -> Result> { + ) -> Result { if TAKE { let storage_range = BlockNumberAddress::range(range.clone()); @@ -1905,9 +1901,7 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockExecutionWriter for DatabaseP let blocks = self.get_take_block_range::(chain_spec, range.clone())?; let unwind_to = blocks.first().map(|b| b.number.saturating_sub(1)); // get execution res - let execution_res = self.get_take_block_execution_result_range::(range.clone())?; - // combine them - let blocks_with_exec_result: Vec<_> = blocks.into_iter().zip(execution_res).collect(); + let execution_state = self.unwind_or_peek_state::(range.clone())?; // remove block bodies it is needed for both get block range and get block execution results // that is why it is deleted afterwards. @@ -1921,8 +1915,7 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockExecutionWriter for DatabaseP } } - // return them - Ok(blocks_with_exec_result) + Ok(Chain::new(blocks, execution_state)) } } @@ -2021,10 +2014,10 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockWriter for DatabaseProvider<' Ok(block_indices) } - fn append_blocks_with_post_state( + fn append_blocks_with_bundle_state( &self, blocks: Vec, - state: PostState, + state: BundleStateWithReceipts, prune_modes: Option<&PruneModes>, ) -> Result<()> { if blocks.is_empty() { @@ -2048,7 +2041,7 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockWriter for DatabaseProvider<' // Write state and changesets to the database. // Must be written after blocks because of the receipt lookup. - state.write_to_db(self.tx_ref(), new_tip_number)?; + state.write_to_db(self.tx_ref(), OriginalValuesKnown::No)?; self.insert_hashes(first_number..=last_block_number, last_block_hash, expected_state_root)?; diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 812606a5d..9b46aa2d6 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -1,8 +1,8 @@ use crate::{ BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, - BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotifications, - CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider, - PostStateDataProvider, ProviderError, PruneCheckpointReader, ReceiptProvider, + BlockchainTreePendingStateProvider, BundleStateDataProvider, CanonChainTracker, + CanonStateNotifications, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, + EvmEnvProvider, HeaderProvider, ProviderError, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StateProviderBox, StateProviderFactory, TransactionsProvider, WithdrawalsProvider, }; @@ -32,13 +32,13 @@ use std::{ }; use tracing::trace; +mod bundle_state_provider; mod chain_info; mod database; -mod post_state_provider; mod state; use crate::{providers::chain_info::ChainInfoTracker, traits::BlockSource}; +pub use bundle_state_provider::BundleStateProvider; pub use database::*; -pub use post_state_provider::PostStateProvider; use reth_db::models::AccountBeforeTx; use reth_interfaces::blockchain_tree::{ error::InsertBlockError, CanonicalOutcome, InsertPayloadOk, @@ -515,13 +515,13 @@ where fn pending_with_provider( &self, - post_state_data: Box, + post_state_data: Box, ) -> Result> { let canonical_fork = post_state_data.canonical_fork(); trace!(target: "providers::blockchain", ?canonical_fork, "Returning post state provider"); let state_provider = self.history_by_block_hash(canonical_fork.hash)?; - let post_state_provider = PostStateProvider::new(state_provider, post_state_data); + let post_state_provider = BundleStateProvider::new(state_provider, post_state_data); Ok(Box::new(post_state_provider)) } } @@ -754,7 +754,7 @@ where fn find_pending_state_provider( &self, block_hash: BlockHash, - ) -> Option> { + ) -> Option> { self.tree.find_pending_state_provider(block_hash) } } diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index 01c9ea8b6..dc53d15f8 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -1,6 +1,6 @@ use crate::{ - providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader, PostState, - ProviderError, StateProvider, StateRootProvider, + providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader, + BundleStateWithReceipts, ProviderError, StateProvider, StateRootProvider, }; use reth_db::{ cursor::{DbCursorRO, DbDupCursorRO}, @@ -201,7 +201,7 @@ impl<'a, 'b, TX: DbTx<'a>> BlockHashReader for HistoricalStateProviderRef<'a, 'b } impl<'a, 'b, TX: DbTx<'a>> StateRootProvider for HistoricalStateProviderRef<'a, 'b, TX> { - fn state_root(&self, _post_state: PostState) -> Result { + fn state_root(&self, _post_state: BundleStateWithReceipts) -> Result { Err(ProviderError::StateRootNotAvailableForHistoricalBlock.into()) } } diff --git a/crates/storage/provider/src/providers/state/latest.rs b/crates/storage/provider/src/providers/state/latest.rs index 77a8692b5..7e4cfeea8 100644 --- a/crates/storage/provider/src/providers/state/latest.rs +++ b/crates/storage/provider/src/providers/state/latest.rs @@ -1,6 +1,6 @@ use crate::{ - providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader, PostState, - StateProvider, StateRootProvider, + providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader, + BundleStateWithReceipts, StateProvider, StateRootProvider, }; use reth_db::{ cursor::{DbCursorRO, DbDupCursorRO}, @@ -56,8 +56,8 @@ impl<'a, 'b, TX: DbTx<'a>> BlockHashReader for LatestStateProviderRef<'a, 'b, TX } impl<'a, 'b, TX: DbTx<'a>> StateRootProvider for LatestStateProviderRef<'a, 'b, TX> { - fn state_root(&self, post_state: PostState) -> Result { - post_state + fn state_root(&self, bundle_state: BundleStateWithReceipts) -> Result { + bundle_state .state_root_slow(self.db) .map_err(|err| reth_interfaces::Error::Database(err.into())) } diff --git a/crates/storage/provider/src/providers/state/macros.rs b/crates/storage/provider/src/providers/state/macros.rs index 8c8c5f709..301719e31 100644 --- a/crates/storage/provider/src/providers/state/macros.rs +++ b/crates/storage/provider/src/providers/state/macros.rs @@ -31,7 +31,7 @@ macro_rules! delegate_provider_impls { $crate::providers::state::macros::delegate_impls_to_as_ref!( for $target => StateRootProvider $(where [$($generics)*])? { - fn state_root(&self, state: crate::PostState) -> reth_interfaces::Result; + fn state_root(&self, state: crate::BundleStateWithReceipts) -> reth_interfaces::Result; } AccountReader $(where [$($generics)*])? { fn basic_account(&self, address: reth_primitives::Address) -> reth_interfaces::Result>; diff --git a/crates/storage/provider/src/test_utils/blocks.rs b/crates/storage/provider/src/test_utils/blocks.rs index 7df40d45e..55982b010 100644 --- a/crates/storage/provider/src/test_utils/blocks.rs +++ b/crates/storage/provider/src/test_utils/blocks.rs @@ -1,13 +1,13 @@ //! Dummy blocks and data for tests -use crate::{post_state::PostState, DatabaseProviderRW}; +use crate::{BundleStateWithReceipts, DatabaseProviderRW}; use reth_db::{database::Database, models::StoredBlockBodyIndices, tables}; use reth_primitives::{ hex_literal::hex, Account, BlockNumber, Bytes, Header, Log, Receipt, SealedBlock, - SealedBlockWithSenders, TxType, Withdrawal, H160, H256, U256, + SealedBlockWithSenders, StorageEntry, TxType, Withdrawal, H160, H256, U256, }; use reth_rlp::Decodable; -use std::collections::BTreeMap; +use std::collections::HashMap; /// Assert genesis block pub fn assert_genesis_block(provider: &DatabaseProviderRW<'_, DB>, g: SealedBlock) { @@ -53,7 +53,7 @@ pub struct BlockChainTestData { /// Genesis pub genesis: SealedBlock, /// Blocks with its execution result - pub blocks: Vec<(SealedBlockWithSenders, PostState)>, + pub blocks: Vec<(SealedBlockWithSenders, BundleStateWithReceipts)>, } impl BlockChainTestData { @@ -85,7 +85,7 @@ pub fn genesis() -> SealedBlock { } /// Block one that points to genesis -fn block1(number: BlockNumber) -> (SealedBlockWithSenders, PostState) { +fn block1(number: BlockNumber) -> (SealedBlockWithSenders, BundleStateWithReceipts) { let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice(); let mut block = SealedBlock::decode(&mut block_rlp).unwrap(); block.withdrawals = Some(vec![Withdrawal::default()]); @@ -96,27 +96,39 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, PostState) { header.parent_hash = H256::zero(); block.header = header.seal_slow(); - let mut post_state = PostState::default(); - // Transaction changes - post_state.create_account( - number, - H160([0x61; 20]), - Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }, - ); - post_state.create_account( - number, - H160([0x60; 20]), - Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }, - ); - post_state.change_storage( - number, - H160([0x60; 20]), - BTreeMap::from([(U256::from(5), (U256::ZERO, U256::from(10)))]), - ); + // block changes + let account1: H160 = [0x60; 20].into(); + let account2: H160 = [0x61; 20].into(); + let slot: H256 = H256::from_low_u64_be(5); - post_state.add_receipt( - number, - Receipt { + let bundle = BundleStateWithReceipts::new_init( + HashMap::from([ + ( + account1, + ( + None, + Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }), + HashMap::from([(slot, (U256::from(0), U256::from(10)))]), + ), + ), + ( + account2, + ( + None, + Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }), + HashMap::from([]), + ), + ), + ]), + HashMap::from([( + number, + HashMap::from([ + (account1, (Some(None), vec![StorageEntry::new(slot, U256::from(0))])), + (account2, (Some(None), vec![])), + ]), + )]), + vec![], + vec![vec![Some(Receipt { tx_type: TxType::EIP2930, success: true, cumulative_gas_used: 300, @@ -125,14 +137,18 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, PostState) { topics: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)], data: Bytes::default(), }], - }, + })]], + number, ); - (SealedBlockWithSenders { block, senders: vec![H160([0x30; 20])] }, post_state) + (SealedBlockWithSenders { block, senders: vec![H160([0x30; 20])] }, bundle) } /// Block two that points to block 1 -fn block2(number: BlockNumber, parent_hash: H256) -> (SealedBlockWithSenders, PostState) { +fn block2( + number: BlockNumber, + parent_hash: H256, +) -> (SealedBlockWithSenders, BundleStateWithReceipts) { let mut block_rlp = hex!("f9025ff901f7a0c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583da00967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192a0e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001830f42408238108203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f862f860800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba072ed817487b84ba367d15d2f039b5fc5f087d0a8882fbdf73e8cb49357e1ce30a0403d800545b8fc544f92ce8124e2255f8c3c6af93f28243a120585d4c4c6a2a3c0").as_slice(); let mut block = SealedBlock::decode(&mut block_rlp).unwrap(); block.withdrawals = Some(vec![Withdrawal::default()]); @@ -144,22 +160,31 @@ fn block2(number: BlockNumber, parent_hash: H256) -> (SealedBlockWithSenders, Po header.parent_hash = parent_hash; block.header = header.seal_slow(); - let mut post_state = PostState::default(); // block changes - post_state.change_account( - number, - H160([0x60; 20]), - Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }, - Account { nonce: 3, balance: U256::from(20), bytecode_hash: None }, - ); - post_state.change_storage( - number, - H160([0x60; 20]), - BTreeMap::from([(U256::from(5), (U256::from(10), U256::from(15)))]), - ); - post_state.add_receipt( - number, - Receipt { + let account: H160 = [0x60; 20].into(); + let slot: H256 = H256::from_low_u64_be(5); + + let bundle = BundleStateWithReceipts::new_init( + HashMap::from([( + account, + ( + None, + Some(Account { nonce: 3, balance: U256::from(20), bytecode_hash: None }), + HashMap::from([(slot, (U256::from(0), U256::from(15)))]), + ), + )]), + HashMap::from([( + number, + HashMap::from([( + account, + ( + Some(Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None })), + vec![StorageEntry::new(slot, U256::from(10))], + ), + )]), + )]), + vec![], + vec![vec![Some(Receipt { tx_type: TxType::EIP1559, success: false, cumulative_gas_used: 400, @@ -168,8 +193,8 @@ fn block2(number: BlockNumber, parent_hash: H256) -> (SealedBlockWithSenders, Po topics: vec![H256::from_low_u64_be(3), H256::from_low_u64_be(4)], data: Bytes::default(), }], - }, + })]], + number, ); - - (SealedBlockWithSenders { block, senders: vec![H160([0x31; 20])] }, post_state) + (SealedBlockWithSenders { block, senders: vec![H160([0x31; 20])] }, bundle) } diff --git a/crates/storage/provider/src/test_utils/executor.rs b/crates/storage/provider/src/test_utils/executor.rs index 58bca889e..5ed366dfb 100644 --- a/crates/storage/provider/src/test_utils/executor.rs +++ b/crates/storage/provider/src/test_utils/executor.rs @@ -1,19 +1,25 @@ -use crate::{post_state::PostState, BlockExecutor, ExecutorFactory, StateProvider}; +use crate::{ + bundle_state::BundleStateWithReceipts, BlockExecutor, BlockExecutorStats, ExecutorFactory, + PrunableBlockExecutor, StateProvider, +}; use parking_lot::Mutex; use reth_interfaces::executor::BlockExecutionError; -use reth_primitives::{Address, Block, ChainSpec, U256}; +use reth_primitives::{Address, Block, BlockNumber, ChainSpec, PruneModes, U256}; use std::sync::Arc; /// Test executor with mocked result. -pub struct TestExecutor(pub Option); +pub struct TestExecutor(pub Option); -impl BlockExecutor for TestExecutor { +impl BlockExecutor for TestExecutor { fn execute( &mut self, _block: &Block, _total_difficulty: U256, _senders: Option>, - ) -> Result { - self.0.clone().ok_or(BlockExecutionError::UnavailableForTest) + ) -> Result<(), BlockExecutionError> { + if self.0.is_none() { + return Err(BlockExecutionError::UnavailableForTest) + } + Ok(()) } fn execute_and_verify_receipt( @@ -21,15 +27,36 @@ impl BlockExecutor for TestExecutor { _block: &Block, _total_difficulty: U256, _senders: Option>, - ) -> Result { - self.0.clone().ok_or(BlockExecutionError::UnavailableForTest) + ) -> Result<(), BlockExecutionError> { + if self.0.is_none() { + return Err(BlockExecutionError::UnavailableForTest) + } + Ok(()) } + + fn take_output_state(&mut self) -> BundleStateWithReceipts { + self.0.clone().unwrap_or_default() + } + + fn stats(&self) -> BlockExecutorStats { + BlockExecutorStats::default() + } + + fn size_hint(&self) -> Option { + None + } +} + +impl PrunableBlockExecutor for TestExecutor { + fn set_tip(&mut self, _tip: BlockNumber) {} + + fn set_prune_modes(&mut self, _prune_modes: PruneModes) {} } /// Executor factory with pre-set execution results. #[derive(Clone, Debug)] pub struct TestExecutorFactory { - exec_results: Arc>>, + exec_results: Arc>>, chain_spec: Arc, } @@ -40,17 +67,18 @@ impl TestExecutorFactory { } /// Extend the mocked execution results - pub fn extend(&self, results: Vec) { + pub fn extend(&self, results: Vec) { self.exec_results.lock().extend(results); } } impl ExecutorFactory for TestExecutorFactory { - type Executor = TestExecutor; - - fn with_sp(&self, _sp: SP) -> Self::Executor { + fn with_state<'a, SP: StateProvider + 'a>( + &'a self, + _sp: SP, + ) -> Box { let exec_res = self.exec_results.lock().pop(); - TestExecutor(exec_res) + Box::new(TestExecutor(exec_res)) } fn chain_spec(&self) -> &ChainSpec { diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index ccb39f6c9..11e690715 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -1,7 +1,8 @@ use crate::{ + bundle_state::BundleStateWithReceipts, traits::{BlockSource, ReceiptProvider}, AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, - ChainSpecProvider, EvmEnvProvider, HeaderProvider, PostState, PostStateDataProvider, + BundleStateDataProvider, ChainSpecProvider, EvmEnvProvider, HeaderProvider, ReceiptProviderIdExt, StateProvider, StateProviderBox, StateProviderFactory, StateRootProvider, TransactionsProvider, WithdrawalsProvider, }; @@ -399,7 +400,7 @@ impl AccountReader for MockEthProvider { } impl StateRootProvider for MockEthProvider { - fn state_root(&self, _post_state: PostState) -> Result { + fn state_root(&self, _state: BundleStateWithReceipts) -> Result { todo!() } } @@ -498,7 +499,7 @@ impl StateProviderFactory for MockEthProvider { fn pending_with_provider<'a>( &'a self, - _post_state_data: Box, + _post_state_data: Box, ) -> Result> { todo!() } @@ -531,7 +532,7 @@ impl StateProviderFactory for Arc { fn pending_with_provider<'a>( &'a self, - _post_state_data: Box, + _post_state_data: Box, ) -> Result> { todo!() } diff --git a/crates/storage/provider/src/test_utils/noop.rs b/crates/storage/provider/src/test_utils/noop.rs index 593b33834..530f0cbbd 100644 --- a/crates/storage/provider/src/test_utils/noop.rs +++ b/crates/storage/provider/src/test_utils/noop.rs @@ -1,10 +1,10 @@ use crate::{ + bundle_state::BundleStateWithReceipts, traits::{BlockSource, ReceiptProvider}, AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, - ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider, PostState, - PruneCheckpointReader, ReceiptProviderIdExt, StageCheckpointReader, StateProvider, - StateProviderBox, StateProviderFactory, StateRootProvider, TransactionsProvider, - WithdrawalsProvider, + ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider, PruneCheckpointReader, + ReceiptProviderIdExt, StageCheckpointReader, StateProvider, StateProviderBox, + StateProviderFactory, StateRootProvider, TransactionsProvider, WithdrawalsProvider, }; use reth_db::models::{AccountBeforeTx, StoredBlockBodyIndices}; use reth_interfaces::Result; @@ -243,7 +243,7 @@ impl ChangeSetReader for NoopProvider { } impl StateRootProvider for NoopProvider { - fn state_root(&self, _post_state: PostState) -> Result { + fn state_root(&self, _state: BundleStateWithReceipts) -> Result { todo!() } } @@ -333,7 +333,7 @@ impl StateProviderFactory for NoopProvider { fn pending_with_provider<'a>( &'a self, - _post_state_data: Box, + _post_state_data: Box, ) -> Result> { Ok(Box::new(*self)) } diff --git a/crates/storage/provider/src/traits/block.rs b/crates/storage/provider/src/traits/block.rs index c78070161..a094bd2f8 100644 --- a/crates/storage/provider/src/traits/block.rs +++ b/crates/storage/provider/src/traits/block.rs @@ -1,5 +1,5 @@ use crate::{ - BlockIdReader, BlockNumReader, HeaderProvider, PostState, ReceiptProvider, + BlockIdReader, BlockNumReader, BundleStateWithReceipts, Chain, HeaderProvider, ReceiptProvider, ReceiptProviderIdExt, TransactionsProvider, WithdrawalsProvider, }; use auto_impl::auto_impl; @@ -210,7 +210,7 @@ pub trait BlockExecutionWriter: BlockWriter + BlockReader + Send + Sync { &self, chain_spec: &ChainSpec, range: RangeInclusive, - ) -> Result> { + ) -> Result { self.get_or_take_block_and_execution_range::(chain_spec, range) } @@ -219,7 +219,7 @@ pub trait BlockExecutionWriter: BlockWriter + BlockReader + Send + Sync { &self, chain_spec: &ChainSpec, range: RangeInclusive, - ) -> Result> { + ) -> Result { self.get_or_take_block_and_execution_range::(chain_spec, range) } @@ -228,7 +228,7 @@ pub trait BlockExecutionWriter: BlockWriter + BlockReader + Send + Sync { &self, chain_spec: &ChainSpec, range: RangeInclusive, - ) -> Result>; + ) -> Result; } /// Block Writer @@ -250,7 +250,7 @@ pub trait BlockWriter: Send + Sync { /// updates the post-state. /// /// Inserts the blocks into the database and updates the state with - /// provided `PostState`. + /// provided `BundleState`. /// /// # Parameters /// @@ -261,11 +261,10 @@ pub trait BlockWriter: Send + Sync { /// # Returns /// /// Returns `Ok(())` on success, or an error if any operation fails. - - fn append_blocks_with_post_state( + fn append_blocks_with_bundle_state( &self, blocks: Vec, - state: PostState, + state: BundleStateWithReceipts, prune_modes: Option<&PruneModes>, ) -> Result<()>; } diff --git a/crates/storage/provider/src/traits/chain.rs b/crates/storage/provider/src/traits/chain.rs index 93f5a61a4..c87f17573 100644 --- a/crates/storage/provider/src/traits/chain.rs +++ b/crates/storage/provider/src/traits/chain.rs @@ -59,8 +59,8 @@ impl Stream for CanonStateNotificationStream { } /// Chain action that is triggered when a new block is imported or old block is reverted. -/// and will return all [`crate::PostState`] and [`reth_primitives::SealedBlockWithSenders`] of both -/// reverted and committed blocks. +/// and will return all [`crate::BundleStateWithReceipts`] and +/// [`reth_primitives::SealedBlockWithSenders`] of both reverted and committed blocks. #[derive(Clone, Debug)] #[allow(missing_docs)] pub enum CanonStateNotification { diff --git a/crates/storage/provider/src/traits/executor.rs b/crates/storage/provider/src/traits/executor.rs index 2ff648d43..e0c1805fa 100644 --- a/crates/storage/provider/src/traits/executor.rs +++ b/crates/storage/provider/src/traits/executor.rs @@ -1,25 +1,27 @@ //! Executor Factory -use crate::{post_state::PostState, StateProvider}; +use crate::{bundle_state::BundleStateWithReceipts, StateProvider}; use reth_interfaces::executor::BlockExecutionError; -use reth_primitives::{Address, Block, ChainSpec, U256}; +use reth_primitives::{Address, Block, BlockNumber, ChainSpec, PruneModes, U256}; +use std::time::Duration; +use tracing::info; /// Executor factory that would create the EVM with particular state provider. /// /// It can be used to mock executor. pub trait ExecutorFactory: Send + Sync + 'static { - /// The executor produced by the factory - type Executor: BlockExecutor; - /// Executor with [`StateProvider`] - fn with_sp(&self, sp: SP) -> Self::Executor; + fn with_state<'a, SP: StateProvider + 'a>( + &'a self, + _sp: SP, + ) -> Box; /// Return internal chainspec fn chain_spec(&self) -> &ChainSpec; } /// An executor capable of executing a block. -pub trait BlockExecutor { +pub trait BlockExecutor { /// Execute a block. /// /// The number of `senders` should be equal to the number of transactions in the block. @@ -33,13 +35,63 @@ pub trait BlockExecutor { block: &Block, total_difficulty: U256, senders: Option>, - ) -> Result; + ) -> Result<(), BlockExecutionError>; - /// Executes the block and checks receipts + /// Executes the block and checks receipts. fn execute_and_verify_receipt( &mut self, block: &Block, total_difficulty: U256, senders: Option>, - ) -> Result; + ) -> Result<(), BlockExecutionError>; + + /// Return bundle state. This is output of executed blocks. + fn take_output_state(&mut self) -> BundleStateWithReceipts; + + /// Internal statistics of execution. + fn stats(&self) -> BlockExecutorStats; + + /// Returns the size hint of current in-memory changes. + fn size_hint(&self) -> Option; +} + +/// A [BlockExecutor] capable of in-memory pruning of the data that will be written to the database. +pub trait PrunableBlockExecutor: BlockExecutor { + /// Set tip - highest known block number. + fn set_tip(&mut self, tip: BlockNumber); + + /// Set prune modes. + fn set_prune_modes(&mut self, prune_modes: PruneModes); +} + +/// Block execution statistics. Contains duration of each step of block execution. +#[derive(Clone, Debug, Default)] +pub struct BlockExecutorStats { + /// Execution duration. + pub execution_duration: Duration, + /// Time needed to apply output of revm execution to revm cached state. + pub apply_state_duration: Duration, + /// Time needed to apply post execution state changes. + pub apply_post_execution_state_changes_duration: Duration, + /// Time needed to merge transitions and create reverts. + /// It this time transitions are applies to revm bundle state. + pub merge_transitions_duration: Duration, + /// Time needed to caclulate receipt roots. + pub receipt_root_duration: Duration, + /// Time needed to recovere senders. + pub sender_recovery_duration: Duration, +} + +impl BlockExecutorStats { + /// Log duration to info level log. + pub fn log_info(&self) { + info!(target: "evm", + evm_transact = ?self.execution_duration, + apply_state = ?self.apply_state_duration, + apply_post_state = ?self.apply_post_execution_state_changes_duration, + merge_transitions = ?self.merge_transitions_duration, + receipt_root = ?self.receipt_root_duration, + sender_recovery = ?self.sender_recovery_duration, + "Execution time"); + } } diff --git a/crates/storage/provider/src/traits/mod.rs b/crates/storage/provider/src/traits/mod.rs index 3c2e06d21..474549577 100644 --- a/crates/storage/provider/src/traits/mod.rs +++ b/crates/storage/provider/src/traits/mod.rs @@ -29,7 +29,7 @@ pub use receipts::{ReceiptProvider, ReceiptProviderIdExt}; mod state; pub use state::{ - BlockchainTreePendingStateProvider, PostStateDataProvider, StateProvider, StateProviderBox, + BlockchainTreePendingStateProvider, BundleStateDataProvider, StateProvider, StateProviderBox, StateProviderFactory, StateRootProvider, }; @@ -40,7 +40,7 @@ mod withdrawals; pub use withdrawals::WithdrawalsProvider; mod executor; -pub use executor::{BlockExecutor, ExecutorFactory}; +pub use executor::{BlockExecutor, BlockExecutorStats, ExecutorFactory, PrunableBlockExecutor}; mod chain; pub use chain::{ diff --git a/crates/storage/provider/src/traits/state.rs b/crates/storage/provider/src/traits/state.rs index 66b66bb9e..bea4bc207 100644 --- a/crates/storage/provider/src/traits/state.rs +++ b/crates/storage/provider/src/traits/state.rs @@ -1,5 +1,5 @@ use super::AccountReader; -use crate::{post_state::PostState, BlockHashReader, BlockIdReader}; +use crate::{BlockHashReader, BlockIdReader, BundleStateWithReceipts}; use auto_impl::auto_impl; use reth_interfaces::{provider::ProviderError, Result}; use reth_primitives::{ @@ -177,7 +177,7 @@ pub trait StateProviderFactory: BlockIdReader + Send + Sync { /// Used to inspect or execute transaction on the pending state. fn pending_with_provider( &self, - post_state_data: Box, + post_state_data: Box, ) -> Result>; } @@ -191,7 +191,7 @@ pub trait BlockchainTreePendingStateProvider: Send + Sync { fn pending_state_provider( &self, block_hash: BlockHash, - ) -> Result> { + ) -> Result> { Ok(self .find_pending_state_provider(block_hash) .ok_or(ProviderError::StateForHashNotFound(block_hash))?) @@ -201,20 +201,20 @@ pub trait BlockchainTreePendingStateProvider: Send + Sync { fn find_pending_state_provider( &self, block_hash: BlockHash, - ) -> Option>; + ) -> Option>; } /// Post state data needs for execution on it. /// This trait is used to create a state provider over pending state. /// /// Pending state contains: -/// * [`PostState`] contains all changed of accounts and storage of pending chain +/// * [`BundleStateWithReceipts`] contains all changed of accounts and storage of pending chain /// * block hashes of pending chain and canonical blocks. /// * canonical fork, the block on what pending chain was forked from. #[auto_impl[Box,&]] -pub trait PostStateDataProvider: Send + Sync { +pub trait BundleStateDataProvider: Send + Sync { /// Return post state - fn state(&self) -> &PostState; + fn state(&self) -> &BundleStateWithReceipts; /// Return block hash by block number of pending or canonical chain. fn block_hash(&self, block_number: BlockNumber) -> Option; /// return canonical fork, the block on what post state was forked from. @@ -226,7 +226,6 @@ pub trait PostStateDataProvider: Send + Sync { /// A type that can compute the state root of a given post state. #[auto_impl[Box,&, Arc]] pub trait StateRootProvider: Send + Sync { - /// Returns the state root of the PostState on top of the current state. - /// See [PostState::state_root_slow] for more info. - fn state_root(&self, post_state: PostState) -> Result; + /// Returns the state root of the BundleState on top of the current state. + fn state_root(&self, post_state: BundleStateWithReceipts) -> Result; } diff --git a/crates/storage/provider/src/transaction.rs b/crates/storage/provider/src/transaction.rs deleted file mode 100644 index 2ce2643c3..000000000 --- a/crates/storage/provider/src/transaction.rs +++ /dev/null @@ -1,197 +0,0 @@ -use reth_interfaces::{db::DatabaseError as DbError, provider::ProviderError}; -use reth_primitives::{BlockHash, BlockNumber, H256}; -use reth_trie::StateRootError; -use std::fmt::Debug; - -#[cfg(test)] -mod test { - use crate::{test_utils::blocks::*, ProviderFactory, TransactionsProvider}; - use reth_db::{ - models::{storage_sharded_key::StorageShardedKey, ShardedKey}, - tables, - test_utils::create_test_rw_db, - }; - use reth_primitives::{ChainSpecBuilder, IntegerList, H160, MAINNET, U256}; - use std::sync::Arc; - - #[test] - fn insert_block_and_hashes_get_take() { - let db = create_test_rw_db(); - - // setup - let chain_spec = ChainSpecBuilder::default() - .chain(MAINNET.chain) - .genesis(MAINNET.genesis.clone()) - .shanghai_activated() - .build(); - - let factory = ProviderFactory::new(db.as_ref(), Arc::new(chain_spec.clone())); - let provider = factory.provider_rw().unwrap(); - - let data = BlockChainTestData::default(); - let genesis = data.genesis.clone(); - let (block1, exec_res1) = data.blocks[0].clone(); - let (block2, exec_res2) = data.blocks[1].clone(); - - let acc1_shard_key = ShardedKey::new(H160([0x60; 20]), u64::MAX); - let acc2_shard_key = ShardedKey::new(H160([0x61; 20]), u64::MAX); - let storage1_shard_key = - StorageShardedKey::new(H160([0x60; 20]), U256::from(5).into(), u64::MAX); - - provider.insert_block(data.genesis.clone(), None).unwrap(); - - assert_genesis_block(&provider, data.genesis); - - provider.append_blocks_with_post_state(vec![block1.clone()], exec_res1.clone()).unwrap(); - - assert_eq!( - provider.table::().unwrap(), - vec![ - (acc1_shard_key.clone(), IntegerList::new(vec![1]).unwrap()), - (acc2_shard_key.clone(), IntegerList::new(vec![1]).unwrap()) - ] - ); - assert_eq!( - provider.table::().unwrap(), - vec![(storage1_shard_key.clone(), IntegerList::new(vec![1]).unwrap())] - ); - - // get one block - let get = provider.get_block_and_execution_range(&chain_spec, 1..=1).unwrap(); - let get_block = get[0].0.clone(); - let get_state = get[0].1.clone(); - assert_eq!(get_block, block1); - assert_eq!(get_state, exec_res1); - - // take one block - let take = provider.take_block_and_execution_range(&chain_spec, 1..=1).unwrap(); - assert_eq!(take, vec![(block1.clone(), exec_res1.clone())]); - assert_genesis_block(&provider, genesis.clone()); - - // check if history is empty. - assert_eq!(provider.table::().unwrap(), vec![]); - assert_eq!(provider.table::().unwrap(), vec![]); - - provider.append_blocks_with_post_state(vec![block1.clone()], exec_res1.clone()).unwrap(); - provider.append_blocks_with_post_state(vec![block2.clone()], exec_res2.clone()).unwrap(); - - // check history of two blocks - assert_eq!( - provider.table::().unwrap(), - vec![ - (acc1_shard_key, IntegerList::new(vec![1, 2]).unwrap()), - (acc2_shard_key, IntegerList::new(vec![1]).unwrap()) - ] - ); - assert_eq!( - provider.table::().unwrap(), - vec![(storage1_shard_key, IntegerList::new(vec![1, 2]).unwrap())] - ); - provider.commit().unwrap(); - - // Check that transactions map onto blocks correctly. - { - let provider = factory.provider_rw().unwrap(); - assert_eq!( - provider.transaction_block(0).unwrap(), - Some(1), - "Transaction 0 should be in block 1" - ); - assert_eq!( - provider.transaction_block(1).unwrap(), - Some(2), - "Transaction 1 should be in block 2" - ); - assert_eq!( - provider.transaction_block(2).unwrap(), - None, - "Transaction 0 should not exist" - ); - } - - let provider = factory.provider_rw().unwrap(); - // get second block - let get = provider.get_block_and_execution_range(&chain_spec, 2..=2).unwrap(); - assert_eq!(get, vec![(block2.clone(), exec_res2.clone())]); - - // get two blocks - let get = provider.get_block_and_execution_range(&chain_spec, 1..=2).unwrap(); - assert_eq!(get[0].0, block1); - assert_eq!(get[1].0, block2); - assert_eq!(get[0].1, exec_res1); - assert_eq!(get[1].1, exec_res2); - - // take two blocks - let get = provider.take_block_and_execution_range(&chain_spec, 1..=2).unwrap(); - assert_eq!(get, vec![(block1, exec_res1), (block2, exec_res2)]); - - // assert genesis state - assert_genesis_block(&provider, genesis); - } - - #[test] - fn insert_get_take_multiblocks() { - let db = create_test_rw_db(); - - // setup - - let chain_spec = Arc::new( - ChainSpecBuilder::default() - .chain(MAINNET.chain) - .genesis(MAINNET.genesis.clone()) - .shanghai_activated() - .build(), - ); - - let factory = ProviderFactory::new(db.as_ref(), chain_spec.clone()); - let provider = factory.provider_rw().unwrap(); - - let data = BlockChainTestData::default(); - let genesis = data.genesis.clone(); - let (block1, exec_res1) = data.blocks[0].clone(); - let (block2, exec_res2) = data.blocks[1].clone(); - - provider.insert_block(data.genesis.clone(), None).unwrap(); - - assert_genesis_block(&provider, data.genesis); - - provider.append_blocks_with_post_state(vec![block1.clone()], exec_res1.clone()).unwrap(); - - // get one block - let get = provider.get_block_and_execution_range(&chain_spec, 1..=1).unwrap(); - assert_eq!(get, vec![(block1.clone(), exec_res1.clone())]); - - // take one block - let take = provider.take_block_and_execution_range(&chain_spec, 1..=1).unwrap(); - assert_eq!(take, vec![(block1.clone(), exec_res1.clone())]); - assert_genesis_block(&provider, genesis.clone()); - - // insert two blocks - let mut merged_state = exec_res1.clone(); - merged_state.extend(exec_res2.clone()); - provider - .append_blocks_with_post_state( - vec![block1.clone(), block2.clone()], - merged_state.clone(), - ) - .unwrap(); - - // get second block - let get = provider.get_block_and_execution_range(&chain_spec, 2..=2).unwrap(); - assert_eq!(get, vec![(block2.clone(), exec_res2.clone())]); - - // get two blocks - let get = provider.get_block_and_execution_range(&chain_spec, 1..=2).unwrap(); - assert_eq!( - get, - vec![(block1.clone(), exec_res1.clone()), (block2.clone(), exec_res2.clone())] - ); - - // take two blocks - let get = provider.take_block_and_execution_range(&chain_spec, 1..=2).unwrap(); - assert_eq!(get, vec![(block1, exec_res1), (block2, exec_res2)]); - - // assert genesis state - assert_genesis_block(&provider, genesis); - } -} diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index 257c389e6..16f3a9ebc 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -14,7 +14,8 @@ use reth_primitives::{ Address, BlockHash, BlockNumber, BlockNumberOrTag, FromRecoveredTransaction, }; use reth_provider::{ - BlockReaderIdExt, CanonStateNotification, ChainSpecProvider, PostState, StateProviderFactory, + BlockReaderIdExt, BundleStateWithReceipts, CanonStateNotification, ChainSpecProvider, + StateProviderFactory, }; use reth_tasks::TaskSpawner; use std::{ @@ -247,9 +248,8 @@ pub async fn maintain_transaction_pool( // find all accounts that were changed in the old chain but _not_ in the new chain let missing_changed_acc = old_state - .accounts() - .keys() - .copied() + .accounts_iter() + .map(|(a, _)| a) .filter(|addr| !new_changed_accounts.contains(addr)); // for these we need to fetch the nonce+balance from the db at the new tip @@ -353,7 +353,7 @@ pub async fn maintain_transaction_pool( continue } - let mut changed_accounts = Vec::with_capacity(state.accounts().len()); + let mut changed_accounts = Vec::with_capacity(state.state().len()); for acc in changed_accounts_iter(state) { // we can always clear the dirty flag for this account dirty_addresses.remove(&acc.address); @@ -497,15 +497,14 @@ where Ok(res) } -/// Extracts all changed accounts from the PostState -fn changed_accounts_iter(state: &PostState) -> impl Iterator + '_ { - state.accounts().iter().filter_map(|(addr, acc)| acc.map(|acc| (addr, acc))).map( - |(address, acc)| ChangedAccount { - address: *address, - nonce: acc.nonce, - balance: acc.balance, - }, - ) +/// Extracts all changed accounts from the BundleState +fn changed_accounts_iter( + state: &BundleStateWithReceipts, +) -> impl Iterator + '_ { + state + .accounts_iter() + .filter_map(|(addr, acc)| acc.map(|acc| (addr, acc))) + .map(|(address, acc)| ChangedAccount { address, nonce: acc.nonce, balance: acc.balance }) } #[cfg(test)] diff --git a/crates/transaction-pool/src/pool/state.rs b/crates/transaction-pool/src/pool/state.rs index 05c8fd4c6..055ecd272 100644 --- a/crates/transaction-pool/src/pool/state.rs +++ b/crates/transaction-pool/src/pool/state.rs @@ -38,7 +38,7 @@ impl TxState { /// - enough fee cap #[inline] pub(crate) fn is_pending(&self) -> bool { - *self >= TxState::PENDING_POOL_BITS + self.bits() >= TxState::PENDING_POOL_BITS.bits() } /// Returns `true` if the transaction has a nonce gap. @@ -95,7 +95,7 @@ impl From for SubPool { if value.is_pending() { return SubPool::Pending } - if value < TxState::BASE_FEE_POOL_BITS { + if value.bits() < TxState::BASE_FEE_POOL_BITS.bits() { return SubPool::Queued } SubPool::BaseFee