From ae22409259eb44c9e0f8a90f73ad2d930484618a Mon Sep 17 00:00:00 2001 From: Alexander Fedorovskyi Date: Wed, 18 Feb 2026 15:23:45 +0100 Subject: [PATCH 1/4] feat: cancun activated --- Cargo.lock | 805 +++--------------- Cargo.toml | 104 +-- bin/alethia-reth/src/main.rs | 2 +- crates/block/Cargo.toml | 18 +- crates/block/src/assembler.rs | 10 +- crates/block/src/config.rs | 32 +- crates/block/src/executor.rs | 7 +- crates/block/src/factory.rs | 15 +- crates/block/src/lib.rs | 2 + crates/block/src/receipt_builder.rs | 30 + crates/block/src/tx_selection.rs | 23 +- crates/cli/Cargo.toml | 7 +- crates/cli/src/command.rs | 4 +- crates/cli/src/lib.rs | 17 +- crates/consensus/Cargo.toml | 59 +- crates/consensus/src/alloy_compat.rs | 217 +++++ crates/consensus/src/lib.rs | 5 + crates/consensus/src/transaction/compress.rs | 179 ++++ crates/consensus/src/transaction/envelope.rs | 615 +++++++++++++ crates/consensus/src/transaction/mod.rs | 27 + crates/consensus/src/transaction/pooled.rs | 352 ++++++++ crates/consensus/src/transaction/tx_type.rs | 68 ++ crates/consensus/src/transaction/typed.rs | 535 ++++++++++++ crates/consensus/src/validation.rs | 35 +- crates/db/Cargo.toml | 5 +- crates/db/src/model.rs | 5 +- crates/evm/Cargo.toml | 3 + crates/evm/src/alloy.rs | 9 +- crates/evm/src/context.rs | 90 ++ crates/evm/src/evm.rs | 46 +- crates/evm/src/factory.rs | 18 +- crates/evm/src/handler/instructions.rs | 91 ++ crates/evm/src/{handler.rs => handler/mod.rs} | 34 +- crates/evm/src/lib.rs | 2 + crates/evm/src/spec.rs | 4 +- crates/network/Cargo.toml | 21 - crates/node-builder/Cargo.toml | 9 +- crates/node-builder/src/consensus.rs | 5 +- .../src/eth_api.rs} | 16 +- crates/node-builder/src/executor.rs | 5 +- crates/node-builder/src/lib.rs | 17 +- .../lib.rs => node-builder/src/network.rs} | 27 +- crates/node-builder/src/payload_builder.rs | 41 + crates/node/Cargo.toml | 32 +- crates/node/src/lib.rs | 64 +- crates/node/src/pool.rs | 83 ++ crates/node/src/txpool/mod.rs | 4 + crates/node/src/txpool/transaction.rs | 223 +++++ crates/payload/Cargo.toml | 5 +- crates/payload/src/builder.rs | 77 +- crates/payload/src/lib.rs | 43 - crates/primitives/Cargo.toml | 26 +- crates/primitives/src/engine/mod.rs | 26 +- crates/primitives/src/engine/types.rs | 18 +- crates/primitives/src/lib.rs | 27 + crates/primitives/src/payload/attributes.rs | 16 +- crates/primitives/src/payload/builder.rs | 12 +- .../primitives/src/payload/built_payload.rs | 209 +++++ crates/primitives/src/payload/mod.rs | 1 + crates/rpc/Cargo.toml | 16 +- crates/rpc/src/converter.rs | 22 + crates/rpc/src/engine/api.rs | 17 +- crates/rpc/src/engine/builder.rs | 7 +- crates/rpc/src/engine/validator.rs | 19 +- crates/rpc/src/eth/auth.rs | 31 +- crates/rpc/src/eth/mod.rs | 3 - crates/rpc/src/lib.rs | 3 + 67 files changed, 3501 insertions(+), 1099 deletions(-) create mode 100644 crates/block/src/receipt_builder.rs create mode 100644 crates/consensus/src/alloy_compat.rs create mode 100644 crates/consensus/src/transaction/compress.rs create mode 100644 crates/consensus/src/transaction/envelope.rs create mode 100644 crates/consensus/src/transaction/mod.rs create mode 100644 crates/consensus/src/transaction/pooled.rs create mode 100644 crates/consensus/src/transaction/tx_type.rs create mode 100644 crates/consensus/src/transaction/typed.rs create mode 100644 crates/evm/src/context.rs create mode 100644 crates/evm/src/handler/instructions.rs rename crates/evm/src/{handler.rs => handler/mod.rs} (91%) delete mode 100644 crates/network/Cargo.toml rename crates/{rpc/src/eth/builder.rs => node-builder/src/eth_api.rs} (64%) rename crates/{network/src/lib.rs => node-builder/src/network.rs} (63%) create mode 100644 crates/node-builder/src/payload_builder.rs create mode 100644 crates/node/src/pool.rs create mode 100644 crates/node/src/txpool/mod.rs create mode 100644 crates/node/src/txpool/transaction.rs create mode 100644 crates/primitives/src/payload/built_payload.rs create mode 100644 crates/rpc/src/converter.rs diff --git a/Cargo.lock b/Cargo.lock index 67b7cc37..49e3478a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,7 @@ name = "alethia-reth-block" version = "0.6.0" dependencies = [ "alethia-reth-chainspec", + "alethia-reth-consensus", "alethia-reth-evm", "alethia-reth-primitives", "alloy-consensus", @@ -98,7 +99,6 @@ dependencies = [ "reth-ethereum-forks", "reth-evm", "reth-evm-ethereum", - "reth-execution-types", "reth-payload-primitives", "reth-primitives", "reth-primitives-traits", @@ -142,15 +142,20 @@ dependencies = [ "alloy-hardforks", "clap", "eyre", - "reth", + "reth-chainspec", "reth-cli", "reth-cli-commands", + "reth-cli-runner", "reth-db", "reth-ethereum", + "reth-ethereum-cli", + "reth-ethereum-consensus", "reth-ethereum-forks", "reth-evm-ethereum", "reth-node-api", "reth-node-builder", + "reth-node-core", + "reth-node-metrics", "reth-storage-api", "reth-tracing", "tracing", @@ -163,17 +168,33 @@ dependencies = [ "alethia-reth-chainspec", "alethia-reth-evm", "alloy-consensus", + "alloy-eips", "alloy-hardforks", + "alloy-network", "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", "alloy-sol-types", + "arbitrary", + "bincode 2.0.1", + "eyre", + "parity-scale-codec", + "rand 0.9.2", "reth-chainspec", + "reth-codecs", "reth-consensus", "reth-consensus-common", - "reth-ethereum", + "reth-db-api", "reth-ethereum-consensus", - "reth-execution-types", - "reth-primitives", + "reth-ethereum-primitives", + "reth-evm", "reth-primitives-traits", + "reth-rpc-convert", + "reth-rpc-eth-api", + "serde", + "serde_json", + "serde_with", + "tracing", ] [[package]] @@ -183,10 +204,10 @@ dependencies = [ "alethia-reth-primitives", "alloy-primitives", "alloy-rlp", - "reth", "reth-codecs", "reth-db", "reth-db-api", + "reth-revm", "serde", "serde_with", ] @@ -204,19 +225,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "alethia-reth-network" -version = "0.6.0" -dependencies = [ - "alethia-reth-chainspec", - "eyre", - "reth", - "reth-ethereum", - "reth-node-api", - "reth-node-builder", - "tracing", -] - [[package]] name = "alethia-reth-node" version = "0.6.0" @@ -226,23 +234,35 @@ dependencies = [ "alethia-reth-consensus", "alethia-reth-db", "alethia-reth-evm", - "alethia-reth-network", "alethia-reth-node-builder", "alethia-reth-payload", "alethia-reth-primitives", "alethia-reth-rpc", + "alloy-consensus", + "alloy-eips", + "alloy-primitives", "alloy-rpc-types-eth", "eyre", - "reth", + "reth-chainspec", + "reth-db-api", "reth-engine-local", "reth-engine-primitives", "reth-ethereum", "reth-ethereum-primitives", + "reth-evm", + "reth-evm-ethereum", + "reth-network", "reth-node-api", "reth-node-builder", "reth-node-ethereum", + "reth-primitives", + "reth-primitives-traits", + "reth-provider", "reth-rpc", + "reth-storage-api", + "reth-transaction-pool", "serde", + "tracing", ] [[package]] @@ -252,14 +272,21 @@ dependencies = [ "alethia-reth-block", "alethia-reth-chainspec", "alethia-reth-consensus", + "alethia-reth-payload", "alethia-reth-primitives", + "alethia-reth-rpc", "alloy-primitives", "eyre", "reth-ethereum", + "reth-network", "reth-node-api", "reth-node-builder", + "reth-node-ethereum", "reth-primitives-traits", "reth-provider", + "reth-rpc", + "reth-transaction-pool", + "tracing", ] [[package]] @@ -281,7 +308,6 @@ dependencies = [ "alloy-sol-types", "eyre", "op-alloy-flz", - "reth", "reth-basic-payload-builder", "reth-chainspec", "reth-engine-local", @@ -291,9 +317,11 @@ dependencies = [ "reth-evm", "reth-evm-ethereum", "reth-node-api", + "reth-payload-primitives", "reth-primitives", "reth-primitives-traits", "reth-provider", + "reth-revm", "reth-transaction-pool", "tracing", ] @@ -302,20 +330,26 @@ dependencies = [ name = "alethia-reth-primitives" version = "0.6.0" dependencies = [ + "alethia-reth-chainspec", + "alethia-reth-consensus", "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", "alloy-rpc-types-eth", "alloy-serde", + "reth-chain-state", "reth-chainspec", "reth-engine-local", + "reth-engine-primitives", "reth-ethereum", "reth-ethereum-engine-primitives", "reth-node-api", "reth-payload-primitives", "reth-primitives", "reth-primitives-traits", + "reth-revm", "serde", "serde_with", "sha2", @@ -336,6 +370,7 @@ dependencies = [ "alloy-eips", "alloy-hardforks", "alloy-json-rpc", + "alloy-network", "alloy-primitives", "alloy-rpc-types-engine", "alloy-rpc-types-eth", @@ -345,7 +380,7 @@ dependencies = [ "jsonrpsee-core", "jsonrpsee-types", "op-alloy-flz", - "reth", + "reth-chainspec", "reth-db", "reth-db-api", "reth-engine-primitives", @@ -358,14 +393,20 @@ dependencies = [ "reth-node-builder", "reth-node-core", "reth-node-ethereum", + "reth-payload-builder", "reth-payload-primitives", + "reth-primitives", "reth-primitives-traits", "reth-provider", "reth-revm", "reth-rpc", + "reth-rpc-api", + "reth-rpc-convert", "reth-rpc-engine-api", "reth-rpc-eth-api", "reth-rpc-eth-types", + "reth-tasks", + "reth-transaction-pool", "serde", "serde_json", "thiserror 2.0.17", @@ -373,15 +414,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -690,7 +722,6 @@ dependencies = [ "cfg-if", "const-hex", "derive_more", - "fixed-cache", "foldhash 0.2.0", "getrandom 0.4.1", "hashbrown 0.16.1", @@ -1772,6 +1803,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "serde", + "unty", +] + [[package]] name = "bindgen" version = "0.72.1" @@ -1879,147 +1920,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "boa_ast" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc119a5ad34c3f459062a96907f53358989b173d104258891bb74f95d93747e8" -dependencies = [ - "bitflags 2.10.0", - "boa_interner", - "boa_macros", - "boa_string", - "indexmap 2.12.1", - "num-bigint", - "rustc-hash", -] - -[[package]] -name = "boa_engine" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e637ec52ea66d76b0ca86180c259d6c7bb6e6a6e14b2f36b85099306d8b00cc3" -dependencies = [ - "aligned-vec", - "arrayvec", - "bitflags 2.10.0", - "boa_ast", - "boa_gc", - "boa_interner", - "boa_macros", - "boa_parser", - "boa_string", - "bytemuck", - "cfg-if", - "cow-utils", - "dashmap", - "dynify", - "fast-float2", - "float16", - "futures-channel", - "futures-concurrency", - "futures-lite", - "hashbrown 0.16.1", - "icu_normalizer", - "indexmap 2.12.1", - "intrusive-collections", - "itertools 0.14.0", - "num-bigint", - "num-integer", - "num-traits", - "num_enum", - "paste", - "portable-atomic", - "rand 0.9.2", - "regress", - "rustc-hash", - "ryu-js", - "serde", - "serde_json", - "small_btree", - "static_assertions", - "tag_ptr", - "tap", - "thin-vec", - "thiserror 2.0.17", - "time", - "xsum", -] - -[[package]] -name = "boa_gc" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1179f690cbfcbe5364cceee5f1cb577265bb6f07b0be6f210aabe270adcf9da" -dependencies = [ - "boa_macros", - "boa_string", - "hashbrown 0.16.1", - "thin-vec", -] - -[[package]] -name = "boa_interner" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9626505d33dc63d349662437297df1d3afd9d5fc4a2b3ad34e5e1ce879a78848" -dependencies = [ - "boa_gc", - "boa_macros", - "hashbrown 0.16.1", - "indexmap 2.12.1", - "once_cell", - "phf", - "rustc-hash", - "static_assertions", -] - -[[package]] -name = "boa_macros" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f36418a46544b152632c141b0a0b7a453cd69ca150caeef83aee9e2f4b48b7d" -dependencies = [ - "cfg-if", - "cow-utils", - "proc-macro2", - "quote", - "syn 2.0.110", - "synstructure", -] - -[[package]] -name = "boa_parser" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f99bf5b684f0de946378fcfe5f38c3a0fbd51cbf83a0f39ff773a0e218541f" -dependencies = [ - "bitflags 2.10.0", - "boa_ast", - "boa_interner", - "boa_macros", - "fast-float2", - "icu_properties", - "num-bigint", - "num-traits", - "regress", - "rustc-hash", -] - -[[package]] -name = "boa_string" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ce9d7aa5563a2e14eab111e2ae1a06a69a812f6c0c3d843196c9d03fbef440" -dependencies = [ - "fast-float2", - "itoa", - "paste", - "rustc-hash", - "ryu-js", - "static_assertions", -] - [[package]] name = "borsh" version = "1.5.7" @@ -2100,20 +2000,6 @@ name = "bytemuck" version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] [[package]] name = "byteorder" @@ -2130,16 +2016,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "c-kzg" version = "2.1.5" @@ -2247,7 +2123,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -2485,16 +2361,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "cordyceps" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" -dependencies = [ - "loom", - "tracing", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -2520,12 +2386,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "cow-utils" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -2961,12 +2821,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "diatomic-waker" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" - [[package]] name = "diff" version = "0.1.13" @@ -3107,26 +2961,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" -[[package]] -name = "dynify" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81acb15628a3e22358bf73de5e7e62360b8a777dbcb5fc9ac7dfa9ae73723747" -dependencies = [ - "dynify-macros", -] - -[[package]] -name = "dynify-macros" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - [[package]] name = "ecdsa" version = "0.16.9" @@ -3260,26 +3094,6 @@ dependencies = [ "syn 2.0.110", ] -[[package]] -name = "equator" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] - -[[package]] -name = "equator-macro" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -3406,12 +3220,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "fast-float2" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55" - [[package]] name = "fastrand" version = "2.3.0" @@ -3522,12 +3330,6 @@ dependencies = [ "syn 2.0.110", ] -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - [[package]] name = "flate2" version = "1.1.5" @@ -3538,16 +3340,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "float16" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bffafbd079d520191c7c2779ae9cf757601266cf4167d3f659ff09617ff8483" -dependencies = [ - "cfg-if", - "rustc_version 0.2.3", -] - [[package]] name = "fnv" version = "1.0.7" @@ -3591,56 +3383,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-buffered" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" -dependencies = [ - "cordyceps", - "diatomic-waker", - "futures-core", - "pin-project-lite", - "spin", -] - -[[package]] -name = "futures-channel" +name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ + "futures-channel", "futures-core", + "futures-executor", + "futures-io", "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "futures-concurrency" -version = "7.6.3" +name = "futures-channel" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb68017df91f2e477ed4bea586c59eaecaa47ed885a770d0444e21e62572cd2" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ - "fixedbitset", - "futures-buffered", "futures-core", - "futures-lite", - "pin-project", - "slab", - "smallvec", + "futures-sink", ] [[package]] @@ -3666,19 +3430,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.31" @@ -3736,20 +3487,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" -[[package]] -name = "generator" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" -dependencies = [ - "cc", - "cfg-if", - "libc", - "log", - "rustversion", - "windows 0.61.3", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -4234,7 +3971,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -4285,8 +4022,6 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "write16", "zerovec", ] @@ -4514,15 +4249,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "intrusive-collections" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" -dependencies = [ - "memoffset", -] - [[package]] name = "ipconfig" version = "0.3.2" @@ -4921,7 +4647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -4971,22 +4697,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "librocksdb-sys" -version = "0.17.3+10.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef2a00ee60fe526157c9023edab23943fae1ce2ab6f4abb2a807c1746835de9" -dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "libc", - "libz-sys", - "lz4-sys", - "tikv-jemalloc-sys", - "zstd-sys", -] - [[package]] name = "libz-sys" version = "1.1.23" @@ -5058,19 +4768,6 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "tracing", - "tracing-subscriber 0.3.22", -] - [[package]] name = "lru" version = "0.12.5" @@ -5175,15 +4872,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "metrics" version = "0.24.2" @@ -5232,7 +4920,7 @@ dependencies = [ "once_cell", "procfs", "rlimit", - "windows 0.62.2", + "windows", ] [[package]] @@ -5455,7 +5143,6 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "serde", ] [[package]] @@ -5753,18 +5440,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "opentelemetry-appender-tracing" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef6a1ac5ca3accf562b8c306fa8483c85f4390f768185ab775f242f7fe8fdcc2" -dependencies = [ - "opentelemetry", - "tracing", - "tracing-core", - "tracing-subscriber 0.3.22", -] - [[package]] name = "opentelemetry-http" version = "0.31.0" @@ -5889,12 +5564,6 @@ dependencies = [ "syn 2.0.110", ] -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - [[package]] name = "parking_lot" version = "0.12.5" @@ -5915,7 +5584,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -6662,16 +6331,6 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" -[[package]] -name = "regress" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" -dependencies = [ - "hashbrown 0.16.1", - "memchr", -] - [[package]] name = "reqwest" version = "0.12.24" @@ -6956,7 +6615,6 @@ dependencies = [ "secp256k1 0.30.0", "serde", "thiserror 2.0.17", - "tikv-jemallocator", ] [[package]] @@ -7557,18 +7215,9 @@ name = "reth-ethereum" version = "1.11.0" source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.0#564ffa586845fa4a8bb066f0c7b015ff36b26c08" dependencies = [ - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", "reth-chainspec", - "reth-consensus", - "reth-consensus-common", - "reth-ethereum-consensus", "reth-ethereum-primitives", - "reth-evm", - "reth-evm-ethereum", "reth-primitives-traits", - "reth-revm", - "reth-storage-api", ] [[package]] @@ -8084,7 +7733,7 @@ version = "1.11.0" source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.0#564ffa586845fa4a8bb066f0c7b015ff36b26c08" dependencies = [ "anyhow", - "bincode", + "bincode 1.3.3", "derive_more", "lz4_flex", "memmap2", @@ -8347,7 +7996,6 @@ dependencies = [ "reqwest", "reth-metrics", "reth-tasks", - "tikv-jemalloc-ctl", "tokio", "tower", "tracing", @@ -8437,10 +8085,6 @@ version = "1.11.0" source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.0#564ffa586845fa4a8bb066f0c7b015ff36b26c08" dependencies = [ "alloy-consensus", - "alloy-eips", - "alloy-genesis", - "alloy-primitives", - "alloy-rlp", "once_cell", "reth-ethereum-forks", "reth-ethereum-primitives", @@ -8521,7 +8165,6 @@ dependencies = [ "reth-trie-db", "revm-database", "revm-state", - "rocksdb", "strum", "tokio", "tracing", @@ -8914,7 +8557,7 @@ dependencies = [ "alloy-consensus", "alloy-eips", "alloy-primitives", - "bincode", + "bincode 1.3.3", "eyre", "futures-util", "itertools 0.14.0", @@ -9104,7 +8747,6 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.0#564ffa586845fa4a8b dependencies = [ "clap", "eyre", - "reth-tracing-otlp", "rolling-file", "tracing", "tracing-appender", @@ -9122,7 +8764,6 @@ dependencies = [ "clap", "eyre", "opentelemetry", - "opentelemetry-appender-tracing", "opentelemetry-otlp", "opentelemetry-semantic-conventions", "opentelemetry_sdk", @@ -9440,8 +9081,6 @@ dependencies = [ "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", - "boa_engine", - "boa_gc", "colorchoice", "revm", "serde", @@ -9601,16 +9240,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "rocksdb" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddb7af00d2b17dbd07d82c0063e25411959748ff03e8d4f96134c2ff41fce34f" -dependencies = [ - "libc", - "librocksdb-sys", -] - [[package]] name = "rolling-file" version = "0.2.0" @@ -9676,15 +9305,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.3.3" @@ -9815,12 +9435,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "ryu-js" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" - [[package]] name = "salsa20" version = "0.10.2" @@ -9883,12 +9497,6 @@ dependencies = [ "hashbrown 0.13.2", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -9986,22 +9594,13 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser 0.10.3", + "semver-parser", ] [[package]] @@ -10014,12 +9613,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "semver-parser" version = "0.10.3" @@ -10289,15 +9882,6 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" -[[package]] -name = "small_btree" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba60d2df92ba73864714808ca68c059734853e6ab722b40e1cf543ebb3a057a" -dependencies = [ - "arrayvec", -] - [[package]] name = "smallvec" version = "1.15.1" @@ -10350,12 +9934,6 @@ dependencies = [ "sha1", ] -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" - [[package]] name = "spki" version = "0.7.3" @@ -10476,15 +10054,9 @@ dependencies = [ "ntapi", "objc2-core-foundation", "objc2-io-kit", - "windows 0.62.2", + "windows", ] -[[package]] -name = "tag_ptr" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e973b34477b7823833469eb0f5a3a60370fef7a453e02d751b59180d0a5a05" - [[package]] name = "tagptr" version = "0.2.0" @@ -10521,12 +10093,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "thin-vec" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" - [[package]] name = "thiserror" version = "1.0.69" @@ -10585,37 +10151,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "tikv-jemalloc-ctl" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "661f1f6a57b3a36dc9174a2c10f19513b4866816e13425d3e418b11cc37bc24c" -dependencies = [ - "libc", - "paste", - "tikv-jemalloc-sys", -] - -[[package]] -name = "tikv-jemalloc-sys" -version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "tikv-jemallocator" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" -dependencies = [ - "libc", - "tikv-jemalloc-sys", -] - [[package]] name = "time" version = "0.3.45" @@ -10624,7 +10159,6 @@ checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", - "js-sys", "libc", "num-conv", "num_threads", @@ -11076,11 +10610,9 @@ dependencies = [ "serde", "serde_json", "sharded-slab", - "smallvec", "thread_local", "tracing", "tracing-core", - "tracing-log", "tracing-serde", ] @@ -11272,6 +10804,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "url" version = "2.5.7" @@ -11290,12 +10828,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -11663,38 +11195,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections 0.2.0", - "windows-core 0.61.2", - "windows-future 0.2.1", - "windows-link 0.1.3", - "windows-numerics 0.2.0", -] - [[package]] name = "windows" version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ - "windows-collections 0.3.2", - "windows-core 0.62.2", - "windows-future 0.3.2", - "windows-numerics 0.3.1", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", ] [[package]] @@ -11703,20 +11213,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core 0.62.2", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-core", ] [[package]] @@ -11727,20 +11224,9 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading 0.1.0", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -11749,9 +11235,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", + "windows-core", + "windows-link", + "windows-threading", ] [[package]] @@ -11776,45 +11262,20 @@ dependencies = [ "syn 2.0.110", ] -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - [[package]] name = "windows-numerics" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", + "windows-core", + "windows-link", ] [[package]] @@ -11823,16 +11284,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -11841,7 +11293,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -11895,7 +11347,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -11950,7 +11402,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -11961,22 +11413,13 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows-threading" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -12272,12 +11715,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" version = "0.6.2" @@ -12322,12 +11759,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "xsum" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0637d3a5566a82fa5214bae89087bc8c9fb94cd8e8a3c07feb691bb8d9c632db" - [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index ca01252c..066baf59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = [ "crates/consensus", "crates/db", "crates/evm", - "crates/network", "crates/node", "crates/node-builder", "crates/payload", @@ -52,9 +51,11 @@ alloy-sol-types = { version = "1.5.6", default-features = false } arbitrary = "1.3" async-trait = "0.1.68" auto_impl = "1" +bincode = { version = "2.0.1", default-features = false } clap = "4" derive_more = { version = "2", default-features = false, features = ["full"] } eyre = "0.6" +rand = "0.9" # rpc jsonrpsee = "0.26.0" @@ -71,54 +72,59 @@ flate2 = "1.1.5" parity-scale-codec = "3.2.1" # reth -reth = { git = "https://github.com/paradigmxyz/reth", package = "reth", tag = "v1.11.0" } -reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-chain-state = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-cli-commands = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-cli-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-codecs = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-consensus-common = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-db-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-engine-local = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-engine-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-engine-tree = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-ethereum-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-ethereum-engine-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-ethereum-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-ethereum-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-evm-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-execution-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-network-peers = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-payload-validator = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false, features = [ - "std", -] } -reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-revm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-rpc-engine-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-storage-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-storage-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-trie-common = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } -reth-trie-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0" } +reth = { git = "https://github.com/paradigmxyz/reth", package = "reth", tag = "v1.11.0", default-features = false } +reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-chain-state = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-cli-runner = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-cli-commands = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-cli-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-codecs = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-consensus-common = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-storage-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-storage-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-db-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-engine-local = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-engine-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-engine-tree = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-ethereum-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-ethereum-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-ethereum-engine-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-ethereum-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-ethereum-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-evm-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-network = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-network-peers = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-node-metrics = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-payload-validator = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-revm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-rpc-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-rpc-convert = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-rpc-engine-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-trie-common = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } +reth-trie-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.0", default-features = false } # revm revm-database-interface = { version = "9.0.0", default-features = false } diff --git a/bin/alethia-reth/src/main.rs b/bin/alethia-reth/src/main.rs index f7b4c76d..55ae9d35 100644 --- a/bin/alethia-reth/src/main.rs +++ b/bin/alethia-reth/src/main.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(test), deny(missing_docs, clippy::missing_docs_in_private_items))] #![cfg_attr(test, allow(missing_docs, clippy::missing_docs_in_private_items))] //! Rust Taiko node (alethia-reth) binary executable. -use alethia_reth_cli::{TaikoChainSpecParser, TaikoCli, TaikoCliExtArgs}; +use alethia_reth_cli::{TaikoCli, TaikoCliExtArgs, parser::TaikoChainSpecParser}; use alethia_reth_node::{ TaikoNode, rpc::eth::{ diff --git a/crates/block/Cargo.toml b/crates/block/Cargo.toml index 0c4c9026..cdacb625 100644 --- a/crates/block/Cargo.toml +++ b/crates/block/Cargo.toml @@ -12,22 +12,27 @@ path = "src/lib.rs" default = ["net"] prover = [] net = [ - "dep:alethia-reth-primitives", - "alethia-reth-primitives?/net", "dep:reth-payload-primitives", "dep:reth-rpc-eth-api", + "alethia-reth-primitives/net", + "reth-codec", ] -serde = ["dep:alethia-reth-primitives", "alethia-reth-primitives?/serde"] +serde = ["alethia-reth-primitives/serde"] +reth-codec = ["alethia-reth-primitives/reth-codec"] [dependencies] alethia-reth-chainspec = { path = "../chainspec", default-features = false } +alethia-reth-consensus = { path = "../consensus", default-features = false } alethia-reth-evm = { path = "../evm" } -alethia-reth-primitives = { path = "../primitives", default-features = false, optional = true } +alethia-reth-primitives = { path = "../primitives", default-features = false, features = [ + "serde", + "serde-bincode-compat", +] } alloy-consensus = { workspace = true } alloy-eips = { workspace = true } alloy-evm = { workspace = true } alloy-hardforks = { workspace = true } -alloy-primitives = { workspace = true } +alloy-primitives = { workspace = true, features = ["rand"] } alloy-rlp = { workspace = true } alloy-rpc-types-eth = { workspace = true } flate2 = { workspace = true } @@ -37,13 +42,12 @@ reth-ethereum = { workspace = true } reth-ethereum-forks = { workspace = true } reth-evm = { workspace = true } reth-evm-ethereum = { workspace = true } -reth-execution-types = { workspace = true } reth-payload-primitives = { workspace = true, optional = true } reth-primitives = { workspace = true } reth-primitives-traits = { workspace = true } -reth-revm = { workspace = true } reth-rpc-eth-api = { workspace = true, optional = true } reth-storage-errors = { workspace = true } reth-transaction-pool = { workspace = true } +reth-revm = { workspace = true } revm-database-interface = { workspace = true } tracing = { workspace = true } diff --git a/crates/block/src/assembler.rs b/crates/block/src/assembler.rs index 48e9b47f..a442be1e 100644 --- a/crates/block/src/assembler.rs +++ b/crates/block/src/assembler.rs @@ -7,14 +7,14 @@ use alloy_consensus::{ use alloy_eips::merge::BEACON_NONCE; use alloy_primitives::logs_bloom; use alloy_rpc_types_eth::Withdrawals; -use reth_ethereum::{Receipt, TransactionSigned}; +use reth_ethereum::Receipt; use reth_evm::{ - block::{BlockExecutionError, BlockExecutorFactory}, + block::{BlockExecutionError, BlockExecutionResult, BlockExecutorFactory}, execute::{BlockAssembler, BlockAssemblerInput}, }; use reth_evm_ethereum::EthBlockAssembler; -use reth_execution_types::BlockExecutionResult; use reth_primitives::Block; +use reth_primitives_traits::SignedTransaction; use reth_revm::context::Block as _; use crate::factory::TaikoBlockExecutionCtx; @@ -43,12 +43,12 @@ impl BlockAssembler for TaikoBlockAssembler where F: for<'a> BlockExecutorFactory< ExecutionCtx<'a> = TaikoBlockExecutionCtx<'a>, - Transaction = TransactionSigned, + Transaction: SignedTransaction, Receipt = Receipt, >, { /// The block type produced by the assembler. - type Block = Block; + type Block = Block; /// Builds a Taiko network block. fn assemble_block( diff --git a/crates/block/src/config.rs b/crates/block/src/config.rs index 7713eec2..1dfbe7c2 100644 --- a/crates/block/src/config.rs +++ b/crates/block/src/config.rs @@ -2,21 +2,19 @@ use std::{borrow::Cow, sync::Arc}; use alloy_consensus::{BlockHeader, Header}; +#[cfg(feature = "net")] use alloy_eips::Decodable2718; use alloy_hardforks::EthereumHardforks; use alloy_primitives::Bytes; use alloy_rpc_types_eth::Withdrawals; use reth_chainspec::EthChainSpec; -use reth_ethereum::EthPrimitives; use reth_ethereum_forks::Hardforks; #[cfg(feature = "net")] -use reth_evm::ConfigureEngineEvm; -use reth_evm::{ConfigureEvm, EvmEnv, EvmEnvFor, ExecutableTxIterator, ExecutionCtxFor}; -use reth_evm_ethereum::RethReceiptBuilder; +use reth_evm::{ConfigureEngineEvm, ExecutableTxIterator}; +use reth_evm::{ConfigureEvm, EvmEnv, EvmEnvFor}; #[cfg(feature = "net")] use reth_payload_primitives::ExecutionPayload; use reth_primitives::{BlockTy, SealedBlock, SealedHeader}; -use reth_primitives_traits::{SignedTransaction, TxTy, constants::MAX_TX_GAS_LIMIT_OSAKA}; use reth_revm::{ context::{BlockEnv, CfgEnv}, primitives::{Address, B256, U256}, @@ -28,9 +26,11 @@ use reth_storage_errors::any::AnyError; use crate::{ assembler::TaikoBlockAssembler, factory::{TaikoBlockExecutionCtx, TaikoBlockExecutorFactory}, + receipt_builder::TaikoReceiptBuilder, }; use alethia_reth_chainspec::{hardfork::TaikoHardfork, spec::TaikoChainSpec}; use alethia_reth_evm::{factory::TaikoEvmFactory, spec::TaikoSpecId}; +use alethia_reth_primitives::TaikoPrimitives; #[cfg(feature = "net")] use alethia_reth_primitives::engine::types::TaikoExecutionData; @@ -63,7 +63,7 @@ pub struct TaikoEvmConfig { impl TaikoEvmConfig { /// Creates a new Taiko EVM configuration with the given chain spec and extra context. pub fn new(chain_spec: Arc) -> Self { - Self::new_with_evm_factory(chain_spec, TaikoEvmFactory) + Self::new_with_evm_factory(chain_spec, TaikoEvmFactory::default()) } /// Creates a new Taiko EVM configuration with the given chain spec and EVM factory. @@ -74,7 +74,7 @@ impl TaikoEvmConfig { Self { block_assembler: TaikoBlockAssembler::new(chain_spec.clone()), executor_factory: TaikoBlockExecutorFactory::new( - RethReceiptBuilder::default(), + TaikoReceiptBuilder::default(), chain_spec, evm_factory, ), @@ -90,7 +90,7 @@ impl TaikoEvmConfig { impl ConfigureEvm for TaikoEvmConfig { /// The primitives type used by the EVM. - type Primitives = EthPrimitives; + type Primitives = TaikoPrimitives; /// The error type that is returned by [`Self::next_evm_env`]. type Error = AnyError; /// Context required for configuring next block environment. @@ -99,7 +99,7 @@ impl ConfigureEvm for TaikoEvmConfig { type NextBlockEnvCtx = TaikoNextBlockEnvAttributes; /// Configured [`BlockExecutorFactory`], contains [`EvmFactory`] internally. type BlockExecutorFactory = - TaikoBlockExecutorFactory, TaikoEvmFactory>; + TaikoBlockExecutorFactory, TaikoEvmFactory>; /// The assembler to build a Taiko block. type BlockAssembler = TaikoBlockAssembler; @@ -229,7 +229,8 @@ impl ConfigureEngineEvm for TaikoEvmConfig { } if self.chain_spec().is_osaka_active_at_timestamp(timestamp) { - cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA); + cfg_env.tx_gas_limit_cap = + Some(reth_primitives_traits::constants::MAX_TX_GAS_LIMIT_OSAKA); } let block_env = BlockEnv { @@ -250,7 +251,7 @@ impl ConfigureEngineEvm for TaikoEvmConfig { fn context_for_payload<'a>( &self, payload: &'a TaikoExecutionData, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { Ok(TaikoBlockExecutionCtx { parent_hash: payload.parent_hash(), parent_beacon_block_root: payload.parent_beacon_block_root(), @@ -268,10 +269,11 @@ impl ConfigureEngineEvm for TaikoEvmConfig { ) -> Result, Self::Error> { let txs = payload.execution_payload.transactions.clone().unwrap_or_default(); let convert = |tx: Bytes| { - let tx = - TxTy::::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?; - let signer = tx.try_recover().map_err(AnyError::new)?; - Ok::<_, AnyError>(tx.with_signer(signer)) + let tx = reth_primitives::TxTy::::decode_2718_exact(tx.as_ref()) + .map_err(AnyError::new)?; + let signer = reth_primitives_traits::SignedTransaction::try_recover(&tx) + .map_err(AnyError::new)?; + Ok::<_, AnyError>(reth_primitives_traits::SignedTransaction::with_signer(tx, signer)) }; Ok((txs, convert)) diff --git a/crates/block/src/executor.rs b/crates/block/src/executor.rs index 07a01c0e..23ae0452 100644 --- a/crates/block/src/executor.rs +++ b/crates/block/src/executor.rs @@ -9,12 +9,11 @@ use alloy_primitives::{Address, Bytes, Uint}; use reth_evm::{ Evm, OnStateHook, block::{ - BlockExecutionError, BlockExecutor, BlockValidationError, ExecutableTx, - InternalBlockExecutionError, StateChangeSource, SystemCaller, + BlockExecutionError, BlockExecutionResult, BlockExecutor, BlockValidationError, + ExecutableTx, InternalBlockExecutionError, StateChangeSource, SystemCaller, }, eth::receipt_builder::ReceiptBuilderCtx, }; -use reth_execution_types::BlockExecutionResult; use reth_primitives::Log; use reth_revm::{ State, @@ -99,8 +98,6 @@ where self.evm.db_mut().set_state_clear_flag(state_clear_flag); self.system_caller.apply_blockhashes_contract_call(self.ctx.parent_hash, &mut self.evm)?; - self.system_caller - .apply_beacon_root_contract_call(self.ctx.parent_beacon_block_root, &mut self.evm)?; // Initialize the golden touch address nonce if it is not already set. if !self.evm_extra_execution_ctx_initialized { diff --git a/crates/block/src/factory.rs b/crates/block/src/factory.rs index d9eb3944..6ff0b91d 100644 --- a/crates/block/src/factory.rs +++ b/crates/block/src/factory.rs @@ -7,13 +7,12 @@ use alloy_evm::{ block::{BlockExecutorFactory, BlockExecutorFor}, eth::receipt_builder::ReceiptBuilder, }; -use alloy_primitives::{B256, Bytes}; +use alloy_primitives::{B256, Bytes, Log}; use alloy_rpc_types_eth::Withdrawals; -use reth_evm_ethereum::RethReceiptBuilder; -use reth_primitives::Log; + use reth_revm::{Inspector, State}; -use crate::executor::TaikoBlockExecutor; +use crate::{executor::TaikoBlockExecutor, receipt_builder::TaikoReceiptBuilder}; use alethia_reth_chainspec::spec::{TaikoChainSpec, TaikoExecutorSpec}; use alethia_reth_evm::factory::TaikoEvmFactory; @@ -37,7 +36,7 @@ pub struct TaikoBlockExecutionCtx<'a> { /// Taiko block executor factory. #[derive(Debug, Clone, Default)] pub struct TaikoBlockExecutorFactory< - R = RethReceiptBuilder, + R = TaikoReceiptBuilder, Spec = Arc, EvmFactory = TaikoEvmFactory, > { @@ -113,14 +112,14 @@ where #[cfg(test)] mod tests { use super::*; - use reth_evm_ethereum::RethReceiptBuilder; + use alethia_reth_evm::factory::TaikoEvmFactory; use std::sync::Arc; #[test] fn test_taiko_block_executor_factory_creation() { - let receipt_builder = RethReceiptBuilder::default(); + let receipt_builder = TaikoReceiptBuilder::default(); let spec = Arc::new(TaikoChainSpec::default()); - let evm_factory = TaikoEvmFactory; + let evm_factory = TaikoEvmFactory::default(); TaikoBlockExecutorFactory::new(receipt_builder, spec.clone(), evm_factory); } diff --git a/crates/block/src/lib.rs b/crates/block/src/lib.rs index 0d095055..30caec98 100644 --- a/crates/block/src/lib.rs +++ b/crates/block/src/lib.rs @@ -9,5 +9,7 @@ pub mod config; pub mod executor; /// Executor factory wiring for Taiko block execution. pub mod factory; +/// Taiko-specific receipt builder for block execution. +pub mod receipt_builder; /// Shared transaction-selection primitives used by block and RPC flows. pub mod tx_selection; diff --git a/crates/block/src/receipt_builder.rs b/crates/block/src/receipt_builder.rs new file mode 100644 index 00000000..430aab5f --- /dev/null +++ b/crates/block/src/receipt_builder.rs @@ -0,0 +1,30 @@ +use alethia_reth_consensus::transaction::{TaikoTxEnvelope, TaikoTxType}; +use alloy_evm::eth::receipt_builder::{ReceiptBuilder, ReceiptBuilderCtx}; +use reth_ethereum::Receipt; +use reth_evm::Evm; + +/// A builder that operates on Taiko primitive types, specifically [`TaikoTxEnvelope`] and +/// [`Receipt`]. +#[derive(Debug, Clone, Copy, Default)] +#[non_exhaustive] +pub struct TaikoReceiptBuilder; + +impl ReceiptBuilder for TaikoReceiptBuilder { + type Transaction = TaikoTxEnvelope; + type Receipt = Receipt; + + fn build_receipt( + &self, + ctx: ReceiptBuilderCtx<'_, TaikoTxType, E>, + ) -> Self::Receipt { + let ReceiptBuilderCtx { tx_type, result, cumulative_gas_used, .. } = ctx; + Receipt { + tx_type: tx_type.into(), + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), + cumulative_gas_used, + logs: result.into_logs(), + } + } +} diff --git a/crates/block/src/tx_selection.rs b/crates/block/src/tx_selection.rs index 5f6f7481..c682d698 100644 --- a/crates/block/src/tx_selection.rs +++ b/crates/block/src/tx_selection.rs @@ -9,7 +9,8 @@ use alloy_rlp::{encode_list, list_length}; use core::fmt; use flate2::{Compression, write::ZlibEncoder}; use op_alloy_flz::tx_estimated_size_fjord_bytes; -use reth_ethereum::{EthPrimitives, TransactionSigned}; +use alethia_reth_consensus::transaction::TaikoTxEnvelope; +use alethia_reth_primitives::TaikoPrimitives; use reth_evm::{ block::{BlockExecutionError, BlockValidationError, InternalBlockExecutionError}, execute::BlockBuilder, @@ -52,7 +53,7 @@ pub struct TxSelectionConfig { #[derive(Debug, Clone)] pub struct ExecutedTx { /// The executed transaction. - pub tx: Recovered, + pub tx: Recovered, /// Gas used by the transaction. pub gas_used: u64, /// Estimated DA size (compressed). @@ -193,14 +194,14 @@ fn adjusted_estimate(estimated_after: u64, state: &DaRatioState) -> u64 { } /// Returns the zlib-compressed byte size for the list plus the candidate. -fn zlib_tx_list_size_bytes(list: &ExecutedTxList, candidate: &Recovered) -> u64 { - let mut txs: Vec<&TransactionSigned> = Vec::with_capacity(list.transactions.len() + 1); +fn zlib_tx_list_size_bytes(list: &ExecutedTxList, candidate: &Recovered) -> u64 { + let mut txs: Vec<&TaikoTxEnvelope> = Vec::with_capacity(list.transactions.len() + 1); txs.extend(list.transactions.iter().map(|etx| etx.tx.inner())); txs.push(candidate.inner()); let mut rlp_bytes = - Vec::with_capacity(list_length::<&TransactionSigned, TransactionSigned>(&txs)); - encode_list::<&TransactionSigned, TransactionSigned>(&txs, &mut rlp_bytes); + Vec::with_capacity(list_length::<&TaikoTxEnvelope, TaikoTxEnvelope>(&txs)); + encode_list::<&TaikoTxEnvelope, TaikoTxEnvelope>(&txs, &mut rlp_bytes); let mut encoder = ZlibEncoder::new(Vec::new(), zlib_compression()); if encoder.write_all(&rlp_bytes).is_err() { @@ -216,7 +217,7 @@ fn zlib_tx_list_size_bytes(list: &ExecutedTxList, candidate: &Recovered, + tx: &Recovered, tx_da_estimated: u64, state: &mut DaRatioState, config: &TxSelectionConfig, @@ -252,7 +253,7 @@ fn exceeds_da_limit( /// Returns whether the list exceeds gas and the DA limit (if exceeded). fn exceeds_list_limits( list: &ExecutedTxList, - tx: &Recovered, + tx: &Recovered, tx_gas_limit: u64, tx_da_estimated: u64, state: &mut DaRatioState, @@ -293,8 +294,8 @@ pub fn select_and_execute_pool_transactions( is_cancelled: impl Fn() -> bool, ) -> Result where - B: BlockBuilder, - Pool: TransactionPool>, + B: BlockBuilder, + Pool: TransactionPool>, { let mut best_txs = pool .best_transactions_with_attributes(BestTransactionsAttributes::new(config.base_fee, None)); @@ -454,7 +455,7 @@ mod tests { use alloy_consensus::{Signed, TxLegacy}; use alloy_primitives::{Address, B256, Bytes, ChainId, Signature, TxKind, U256}; - fn make_signed_legacy_tx(input: Bytes) -> TransactionSigned { + fn make_signed_legacy_tx(input: Bytes) -> TaikoTxEnvelope { let tx = TxLegacy { chain_id: Some(ChainId::from(1u64)), nonce: 0, diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 80479eb1..d6969976 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -19,14 +19,19 @@ alloy-consensus = { workspace = true } alloy-hardforks = { workspace = true } clap = { workspace = true, features = ["derive"] } eyre = { workspace = true } -reth = { workspace = true } +reth-chainspec = { workspace = true } reth-cli = { workspace = true } +reth-cli-runner = { workspace = true } reth-cli-commands = { workspace = true } reth-db = { workspace = true } reth-ethereum = { workspace = true } reth-ethereum-forks = { workspace = true } +reth-ethereum-cli = { workspace = true } +reth-ethereum-consensus = { workspace = true } reth-evm-ethereum = { workspace = true } reth-node-api = { workspace = true } +reth-node-metrics = { workspace = true } +reth-node-core = { workspace = true } reth-node-builder = { workspace = true } reth-storage-api = { workspace = true } reth-tracing = { workspace = true } diff --git a/crates/cli/src/command.rs b/crates/cli/src/command.rs index a73ceac0..d0b481a1 100644 --- a/crates/cli/src/command.rs +++ b/crates/cli/src/command.rs @@ -3,11 +3,13 @@ use std::{ffi::OsString, fmt, path::PathBuf, sync::Arc}; use alloy_hardforks::EthereumHardforks; use clap::Parser; -use reth::{CliContext, chainspec::EthChainSpec, core::version}; +use reth_chainspec::EthChainSpec; use reth_cli::chainspec::ChainSpecParser; use reth_cli_commands::{NodeCommand, launcher::Launcher, node::NoArgs}; +use reth_cli_runner::CliContext; use reth_db::mdbx::init_db_for; use reth_node_builder::{NodeBuilder, NodeConfig}; +use reth_node_core::version; use alethia_reth_node::chainspec::spec::TaikoDevnetConfigExt; diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 2ee8a4a5..4638cbcb 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -3,28 +3,27 @@ //! CLI entrypoints and command wiring for the Alethia Taiko node. use std::{fmt, sync::Arc}; +use alethia_reth_block::config::TaikoEvmConfig; use alloy_consensus::Header; use clap::Parser; -use reth::{ - CliRunner, - cli::{Cli, Commands}, - prometheus_exporter::install_prometheus_recorder, -}; +use eyre::Ok; use reth_cli::chainspec::ChainSpecParser; use reth_cli_commands::{common::CliNodeTypes, launcher::FnLauncher, node::NoArgs}; +use reth_cli_runner::CliRunner; use reth_db::DatabaseEnv; +use reth_ethereum::EthPrimitives; +use reth_ethereum_cli::{Cli, interface::Commands}; use reth_ethereum_forks::Hardforks; use reth_node_api::{NodePrimitives, NodeTypes}; use reth_node_builder::{NodeBuilder, WithLaunchContext}; +use reth_node_metrics::recorder::install_prometheus_recorder; use reth_tracing::FileWorkerGuard; use tracing::info; -use alethia_reth_block::config::TaikoEvmConfig; -use alethia_reth_chainspec::spec::TaikoChainSpec; use alethia_reth_node::{ - TaikoNode, consensus::validation::TaikoBeaconConsensus, node_builder::ProviderTaikoBlockReader, + TaikoNode, chainspec::spec::TaikoChainSpec, consensus::validation::TaikoBeaconConsensus, + node_builder::ProviderTaikoBlockReader, }; -use reth_ethereum::EthPrimitives; use reth_storage_api::noop::NoopProvider; use crate::command::{TaikoNodeCommand, TaikoNodeExtArgs}; diff --git a/crates/consensus/Cargo.toml b/crates/consensus/Cargo.toml index 4e71d28c..0535db00 100644 --- a/crates/consensus/Cargo.toml +++ b/crates/consensus/Cargo.toml @@ -10,19 +10,70 @@ path = "src/lib.rs" [features] default = [] +serde = [ + "dep:serde", + "alloy-primitives/serde", + "alloy-consensus/serde", + "alloy-eips/serde", +] +arbitrary = [ + "dep:arbitrary", + "alloy-consensus/arbitrary", + "alloy-eips/arbitrary", + "alloy-primitives/rand", + "alloy-primitives/arbitrary", +] +alloy-compat = [ + "dep:serde", + "dep:alloy-network", + "dep:alloy-rpc-types-eth", + "dep:reth-rpc-convert", + "dep:reth-rpc-eth-api", +] +k256 = ["alloy-primitives/k256", "alloy-consensus/k256"] +serde-bincode-compat = [ + "serde", + "dep:serde", + "dep:serde_with", + "alloy-consensus/serde-bincode-compat", + "reth-primitives-traits/serde-bincode-compat", + "reth-ethereum-primitives/serde-bincode-compat", +] +reth-codec = ["dep:reth-codecs", "dep:reth-db-api"] [dependencies] alethia-reth-chainspec = { path = "../chainspec", default-features = false } alethia-reth-evm = { path = "../evm" } alloy-consensus = { workspace = true } alloy-hardforks = { workspace = true } +alloy-rlp = { workspace = true } +alloy-eips = { workspace = true } +alloy-network = { workspace = true, optional = true } alloy-primitives = { workspace = true } alloy-sol-types = { workspace = true } -reth-chainspec = { workspace = true } +alloy-rpc-types-eth = { workspace = true, optional = true } +arbitrary = { workspace = true, features = ["derive"], optional = true } +eyre = { workspace = true } +parity-scale-codec = { workspace = true } +rand = { workspace = true } reth-consensus = { workspace = true } reth-consensus-common = { workspace = true } -reth-ethereum = { workspace = true } +reth-codecs = { workspace = true, features = ["alloy"], optional = true } +reth-chainspec = { workspace = true } +reth-db-api = { workspace = true, optional = true } reth-ethereum-consensus = { workspace = true } -reth-execution-types = { workspace = true } -reth-primitives = { workspace = true } +reth-ethereum-primitives = { workspace = true } +reth-evm = { workspace = true } reth-primitives-traits = { workspace = true } +reth-rpc-convert = { workspace = true, optional = true } +reth-rpc-eth-api = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +serde_with = { workspace = true, optional = true } +tracing = { workspace = true } + +[dev-dependencies] +bincode = { workspace = true, features = ["serde", "std"] } +alloy-primitives = { workspace = true, features = ["rand", "std"] } +alloy-consensus = { workspace = true, features = ["arbitrary"] } +serde_json = { workspace = true } +arbitrary = { workspace = true, features = ["derive"] } diff --git a/crates/consensus/src/alloy_compat.rs b/crates/consensus/src/alloy_compat.rs new file mode 100644 index 00000000..8a54597b --- /dev/null +++ b/crates/consensus/src/alloy_compat.rs @@ -0,0 +1,217 @@ +//! Alloy compatibility implementations for Taiko types. +//! +//! This module contains conversions and trait implementations that bridge +//! between Taiko types and Alloy types (RPC, network, etc.). + +use crate::transaction::{TaikoTxEnvelope, TaikoTxType, TaikoTypedTransaction}; +use alloy_consensus::{TxEip1559, TxEip2930, TxEip7702, TxLegacy}; +use alloy_primitives::{Address, Signature}; +use alloy_rpc_types_eth::TransactionRequest; + +// ============================================================================ +// TaikoTxEnvelope conversions +// ============================================================================ + +/// Converts an RPC [`Transaction`](alloy_rpc_types_eth::Transaction) into a +/// [`TaikoTxEnvelope`]. +/// +/// # Panics +/// +/// Panics if the inner transaction is an EIP-4844 blob transaction, which Taiko +/// does not support. +impl From for TaikoTxEnvelope { + fn from(tx: alloy_rpc_types_eth::Transaction) -> Self { + let envelope = tx.inner.into_inner(); + Self::try_from_eth_envelope(envelope) + .expect("TaikoTxEnvelope does not support EIP-4844 blob transactions") + } +} + +impl From for TransactionRequest { + fn from(value: TaikoTxEnvelope) -> Self { + match value { + TaikoTxEnvelope::Eip2930(tx) => tx.into_parts().0.into(), + TaikoTxEnvelope::Eip1559(tx) => tx.into_parts().0.into(), + TaikoTxEnvelope::Eip7702(tx) => tx.into_parts().0.into(), + TaikoTxEnvelope::Legacy(tx) => tx.into_parts().0.into(), + } + } +} + +impl TaikoTxEnvelope { + /// Attempts to convert an ethereum [`TxEnvelope`] into the taiko variant. + /// + /// Returns the given envelope as error if [`TaikoTxEnvelope`] doesn't support the variant + /// (EIP-4844) + #[allow(clippy::result_large_err)] + pub fn try_from_any_envelope( + tx: alloy_network::AnyTxEnvelope, + ) -> Result { + match tx.try_into_envelope() { + Ok(eth) => { + Self::try_from_eth_envelope(eth).map_err(alloy_network::AnyTxEnvelope::Ethereum) + } + Err(err) => { + let unsupported = err.into_value(); + Err(unsupported) + } + } + } +} + +// ============================================================================ +// TaikoTypedTransaction conversions +// ============================================================================ + +impl From for TransactionRequest { + fn from(tx: TaikoTypedTransaction) -> Self { + match tx { + TaikoTypedTransaction::Legacy(tx) => tx.into(), + TaikoTypedTransaction::Eip2930(tx) => tx.into(), + TaikoTypedTransaction::Eip1559(tx) => tx.into(), + TaikoTypedTransaction::Eip7702(tx) => tx.into(), + } + } +} + +// ============================================================================ +// RPC trait implementations +// ============================================================================ + +/// Implementation of SignableTxRequest for TransactionRequest with TaikoTxEnvelope +/// This allows converting RPC transaction requests into signable Taiko transactions +impl reth_rpc_eth_api::SignableTxRequest for TransactionRequest { + async fn try_build_and_sign( + self, + signer: impl alloy_network::TxSigner + Send, + ) -> Result { + use alloy_consensus::TypedTransaction; + use reth_rpc_eth_api::SignTxRequestError; + + // Build the typed transaction from the request (returns EthereumTypedTransaction) + let eth_typed_tx = + self.build_typed_tx().map_err(|_| SignTxRequestError::InvalidTransactionRequest)?; + + // Convert EthereumTypedTransaction to TaikoTypedTransaction + // by extracting and wrapping each variant + // Note: Taiko doesn't support EIP-4844 (blob transactions) + let mut taiko_typed_tx = match eth_typed_tx { + TypedTransaction::Legacy(tx) => TaikoTypedTransaction::Legacy(tx), + TypedTransaction::Eip2930(tx) => TaikoTypedTransaction::Eip2930(tx), + TypedTransaction::Eip1559(tx) => TaikoTypedTransaction::Eip1559(tx), + TypedTransaction::Eip7702(tx) => TaikoTypedTransaction::Eip7702(tx), + TypedTransaction::Eip4844(_) => { + return Err(SignTxRequestError::InvalidTransactionRequest); + } + }; + + // Sign the transaction + let signature = signer.sign_transaction(&mut taiko_typed_tx).await?; + + // Convert to TaikoTxEnvelope + let envelope = TaikoTxEnvelope::from((taiko_typed_tx, signature)); + + Ok(envelope) + } +} + +/// Implementation of simulation transaction conversion for Taiko. +/// +/// This allows `TransactionRequest` to be converted into `TaikoTxEnvelope` for simulation +/// purposes (eth_call, eth_estimateGas, etc.) using the standard Reth trait. +impl reth_rpc_convert::transaction::TryIntoSimTx for TransactionRequest { + fn try_into_sim_tx(self) -> Result> { + use alloy_primitives::TxKind; + + let signature = Signature::test_signature(); + + // Determine transaction type and create appropriate envelope + let envelope = match self.transaction_type { + Some(ty) if ty == TaikoTxType::Legacy as u8 => { + // Legacy transaction (type 0) + let legacy_tx = TxLegacy { + chain_id: self.chain_id, + nonce: self.nonce.unwrap_or_default(), + gas_price: self.gas_price.unwrap_or_default(), + gas_limit: self.gas.unwrap_or_default(), + to: self.to.unwrap_or_default(), + value: self.value.unwrap_or_default(), + input: self.input.input().cloned().unwrap_or_default(), + }; + let typed_tx = TaikoTypedTransaction::Legacy(legacy_tx); + TaikoTxEnvelope::new_unhashed(typed_tx, signature) + } + Some(ty) if ty == TaikoTxType::Eip2930 as u8 => { + // EIP-2930 (type 1) + let eip2930_tx = TxEip2930 { + chain_id: self.chain_id.unwrap_or_default(), + nonce: self.nonce.unwrap_or_default(), + gas_price: self.gas_price.unwrap_or_default(), + gas_limit: self.gas.unwrap_or_default(), + to: self.to.unwrap_or_default(), + value: self.value.unwrap_or_default(), + input: self.input.input().cloned().unwrap_or_default(), + access_list: self.access_list.clone().unwrap_or_default(), + }; + let typed_tx = TaikoTypedTransaction::Eip2930(eip2930_tx); + TaikoTxEnvelope::new_unhashed(typed_tx, signature) + } + Some(ty) if ty == TaikoTxType::Eip1559 as u8 => { + // EIP-1559 (type 2) + let eip1559_tx = TxEip1559 { + chain_id: self.chain_id.unwrap_or_default(), + nonce: self.nonce.unwrap_or_default(), + max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(), + max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(), + gas_limit: self.gas.unwrap_or_default(), + to: self.to.unwrap_or_default(), + value: self.value.unwrap_or_default(), + input: self.input.input().cloned().unwrap_or_default(), + access_list: self.access_list.clone().unwrap_or_default(), + }; + let typed_tx = TaikoTypedTransaction::Eip1559(eip1559_tx); + TaikoTxEnvelope::new_unhashed(typed_tx, signature) + } + Some(ty) if ty == TaikoTxType::Eip7702 as u8 => { + // EIP-7702 (type 4) + let to_address = match self.to { + Some(TxKind::Call(addr)) => addr, + // For simulation, if no 'to' or if it's Create, default to zero address + _ => Address::ZERO, + }; + let eip7702_tx = TxEip7702 { + chain_id: self.chain_id.unwrap_or_default(), + nonce: self.nonce.unwrap_or_default(), + max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(), + max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(), + gas_limit: self.gas.unwrap_or_default(), + to: to_address, + value: self.value.unwrap_or_default(), + input: self.input.input().cloned().unwrap_or_default(), + access_list: self.access_list.clone().unwrap_or_default(), + authorization_list: self.authorization_list.clone().unwrap_or_default(), + }; + let typed_tx = TaikoTypedTransaction::Eip7702(eip7702_tx); + TaikoTxEnvelope::new_unhashed(typed_tx, signature) + } + None | Some(_) => { + // For None or any other type, default to EIP-1559 + let eip1559_tx = TxEip1559 { + chain_id: self.chain_id.unwrap_or_default(), + nonce: self.nonce.unwrap_or_default(), + max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(), + max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(), + gas_limit: self.gas.unwrap_or_default(), + to: self.to.unwrap_or_default(), + value: self.value.unwrap_or_default(), + input: self.input.input().cloned().unwrap_or_default(), + access_list: self.access_list.clone().unwrap_or_default(), + }; + let typed_tx = TaikoTypedTransaction::Eip1559(eip1559_tx); + TaikoTxEnvelope::new_unhashed(typed_tx, signature) + } + }; + + Ok(envelope) + } +} diff --git a/crates/consensus/src/lib.rs b/crates/consensus/src/lib.rs index bef2a08c..b07b7e1b 100644 --- a/crates/consensus/src/lib.rs +++ b/crates/consensus/src/lib.rs @@ -3,5 +3,10 @@ //! Taiko consensus validation rules and base-fee helpers. /// EIP-4396 base-fee helpers used by Taiko Shasta validation. pub mod eip4396; +/// Taiko-specific transaction types and envelope definitions. +pub mod transaction; /// Block and anchor transaction validation for Taiko consensus. pub mod validation; + +#[cfg(feature = "alloy-compat")] +pub mod alloy_compat; diff --git a/crates/consensus/src/transaction/compress.rs b/crates/consensus/src/transaction/compress.rs new file mode 100644 index 00000000..0334f4b7 --- /dev/null +++ b/crates/consensus/src/transaction/compress.rs @@ -0,0 +1,179 @@ +use crate::transaction::{TaikoTxEnvelope, TaikoTxType, TaikoTypedTransaction}; +use alloy_consensus::{ + Signed, TxEip1559, TxEip2930, TxEip7702, TxLegacy, constants::EIP7702_TX_TYPE_ID, +}; +use alloy_primitives::{Signature, bytes}; +use alloy_rlp::BufMut; +use reth_codecs::{ + Compact, + alloy::transaction::{CompactEnvelope, Envelope, FromTxCompact, ToTxCompact}, + txtype::{ + COMPACT_EXTENDED_IDENTIFIER_FLAG, COMPACT_IDENTIFIER_EIP1559, COMPACT_IDENTIFIER_EIP2930, + COMPACT_IDENTIFIER_LEGACY, + }, +}; +use reth_db_api::{ + DatabaseError, + table::{Compress, Decompress}, +}; + +impl Compact for TaikoTxType { + fn to_compact(&self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + match self { + Self::Legacy => COMPACT_IDENTIFIER_LEGACY, + Self::Eip2930 => COMPACT_IDENTIFIER_EIP2930, + Self::Eip1559 => COMPACT_IDENTIFIER_EIP1559, + Self::Eip7702 => { + buf.put_u8(EIP7702_TX_TYPE_ID); + COMPACT_EXTENDED_IDENTIFIER_FLAG + } + } + } + + // For backwards compatibility purposes only 2 bits of the type are encoded in the identifier + // parameter. In the case of a [`COMPACT_EXTENDED_IDENTIFIER_FLAG`], the full transaction type + // is read from the buffer as a single byte. + fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) { + use bytes::Buf; + ( + match identifier { + COMPACT_IDENTIFIER_LEGACY => Self::Legacy, + COMPACT_IDENTIFIER_EIP2930 => Self::Eip2930, + COMPACT_IDENTIFIER_EIP1559 => Self::Eip1559, + COMPACT_EXTENDED_IDENTIFIER_FLAG => { + let extended_identifier = buf.get_u8(); + match extended_identifier { + EIP7702_TX_TYPE_ID => Self::Eip7702, + _ => panic!("Unsupported TaikoTxType identifier: {extended_identifier}"), + } + } + _ => panic!("Unknown identifier for TxType: {identifier}"), + }, + buf, + ) + } +} + +impl Compact for TaikoTypedTransaction { + fn to_compact(&self, out: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + let identifier = self.tx_type().to_compact(out); + match self { + Self::Legacy(tx) => tx.to_compact(out), + Self::Eip2930(tx) => tx.to_compact(out), + Self::Eip1559(tx) => tx.to_compact(out), + Self::Eip7702(tx) => tx.to_compact(out), + }; + identifier + } + + fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) { + let (tx_type, buf) = TaikoTxType::from_compact(buf, identifier); + match tx_type { + TaikoTxType::Legacy => { + let (tx, buf) = Compact::from_compact(buf, buf.len()); + (Self::Legacy(tx), buf) + } + TaikoTxType::Eip2930 => { + let (tx, buf) = Compact::from_compact(buf, buf.len()); + (Self::Eip2930(tx), buf) + } + TaikoTxType::Eip1559 => { + let (tx, buf) = Compact::from_compact(buf, buf.len()); + (Self::Eip1559(tx), buf) + } + TaikoTxType::Eip7702 => { + let (tx, buf) = Compact::from_compact(buf, buf.len()); + (Self::Eip7702(tx), buf) + } + } + } +} + +impl ToTxCompact for TaikoTxEnvelope { + fn to_tx_compact(&self, buf: &mut (impl BufMut + AsMut<[u8]>)) { + match self { + Self::Legacy(tx) => tx.tx().to_compact(buf), + Self::Eip2930(tx) => tx.tx().to_compact(buf), + Self::Eip1559(tx) => tx.tx().to_compact(buf), + Self::Eip7702(tx) => tx.tx().to_compact(buf), + }; + } +} + +impl FromTxCompact for TaikoTxEnvelope { + type TxType = TaikoTxType; + + fn from_tx_compact(buf: &[u8], tx_type: TaikoTxType, signature: Signature) -> (Self, &[u8]) { + match tx_type { + TaikoTxType::Legacy => { + let (tx, buf) = TxLegacy::from_compact(buf, buf.len()); + let tx = Signed::new_unhashed(tx, signature); + (Self::Legacy(tx), buf) + } + TaikoTxType::Eip2930 => { + let (tx, buf) = TxEip2930::from_compact(buf, buf.len()); + let tx = Signed::new_unhashed(tx, signature); + (Self::Eip2930(tx), buf) + } + TaikoTxType::Eip1559 => { + let (tx, buf) = TxEip1559::from_compact(buf, buf.len()); + let tx = Signed::new_unhashed(tx, signature); + (Self::Eip1559(tx), buf) + } + TaikoTxType::Eip7702 => { + let (tx, buf) = TxEip7702::from_compact(buf, buf.len()); + let tx = Signed::new_unhashed(tx, signature); + (Self::Eip7702(tx), buf) + } + } + } +} + +impl Envelope for TaikoTxEnvelope { + fn signature(&self) -> &Signature { + match self { + Self::Legacy(tx) => tx.signature(), + Self::Eip2930(tx) => tx.signature(), + Self::Eip1559(tx) => tx.signature(), + Self::Eip7702(tx) => tx.signature(), + } + } + + fn tx_type(&self) -> Self::TxType { + Self::tx_type(self) + } +} + +impl Compact for TaikoTxEnvelope { + fn to_compact(&self, buf: &mut B) -> usize + where + B: BufMut + AsMut<[u8]>, + { + CompactEnvelope::to_compact(self, buf) + } + + fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { + CompactEnvelope::from_compact(buf, len) + } +} + +impl Compress for TaikoTxEnvelope { + type Compressed = Vec; + + fn compress_to_buf>(&self, buf: &mut B) { + let _ = Compact::to_compact(self, buf); // Delegates to your Compact impl! + } +} + +impl Decompress for TaikoTxEnvelope { + fn decompress(value: &[u8]) -> Result { + let (obj, _) = Compact::from_compact(value, value.len()); + Ok(obj) + } +} diff --git a/crates/consensus/src/transaction/envelope.rs b/crates/consensus/src/transaction/envelope.rs new file mode 100644 index 00000000..40982176 --- /dev/null +++ b/crates/consensus/src/transaction/envelope.rs @@ -0,0 +1,615 @@ +use crate::transaction::{TaikoPooledTransaction, TaikoTypedTransaction}; +use alloy_consensus::{ + EthereumTxEnvelope, Extended, SignableTransaction, Signed, TransactionEnvelope, TxEip1559, + TxEip2930, TxEip7702, TxEnvelope, TxLegacy, error::ValueError, transaction::TxHashRef, +}; +use alloy_eips::eip2718::Encodable2718; +use alloy_primitives::{Address, B256, Bytes, Signature}; +use reth_evm::{FromRecoveredTx, FromTxWithEncoded, revm::context::TxEnv}; +use reth_primitives_traits::InMemorySize; + +/// The Ethereum [EIP-2718] Transaction Envelope, modified for Taiko chain. +/// +/// # Note: +/// +/// This enum distinguishes between tagged and untagged legacy transactions, as +/// the in-protocol merkle tree may commit to EITHER 0-prefixed or raw. +/// Therefore we must ensure that encoding returns the precise byte-array that +/// was decoded, preserving the presence or absence of the `TransactionType` +/// flag. +/// +/// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 +#[derive(Debug, Clone, TransactionEnvelope)] +#[envelope(tx_type_name = TaikoTxType, serde_cfg(feature = "serde"))] +pub enum TaikoTxEnvelope { + /// An untagged [`TxLegacy`]. + #[envelope(ty = 0)] + Legacy(Signed), + /// A [`TxEip2930`] tagged with type 1. + #[envelope(ty = 1)] + Eip2930(Signed), + /// A [`TxEip1559`] tagged with type 2. + #[envelope(ty = 2)] + Eip1559(Signed), + /// A [`TxEip7702`] tagged with type 4. + #[envelope(ty = 4)] + Eip7702(Signed), +} + +impl AsRef for TaikoTxEnvelope { + fn as_ref(&self) -> &Self { + self + } +} + +impl From> for TaikoTxEnvelope { + fn from(v: Signed) -> Self { + Self::Legacy(v) + } +} + +impl From> for TaikoTxEnvelope { + fn from(v: Signed) -> Self { + Self::Eip2930(v) + } +} + +impl From> for TaikoTxEnvelope { + fn from(v: Signed) -> Self { + Self::Eip1559(v) + } +} + +impl From> for TaikoTxEnvelope { + fn from(v: Signed) -> Self { + Self::Eip7702(v) + } +} + +impl From> for TaikoTxEnvelope { + fn from(value: Signed) -> Self { + let (tx, sig, hash) = value.into_parts(); + match tx { + TaikoTypedTransaction::Legacy(tx_legacy) => { + let tx = Signed::new_unchecked(tx_legacy, sig, hash); + Self::Legacy(tx) + } + TaikoTypedTransaction::Eip2930(tx_eip2930) => { + let tx = Signed::new_unchecked(tx_eip2930, sig, hash); + Self::Eip2930(tx) + } + TaikoTypedTransaction::Eip1559(tx_eip1559) => { + let tx = Signed::new_unchecked(tx_eip1559, sig, hash); + Self::Eip1559(tx) + } + TaikoTypedTransaction::Eip7702(tx_eip7702) => { + let tx = Signed::new_unchecked(tx_eip7702, sig, hash); + Self::Eip7702(tx) + } + } + } +} + +impl From<(TaikoTypedTransaction, Signature)> for TaikoTxEnvelope { + fn from(value: (TaikoTypedTransaction, Signature)) -> Self { + Self::new_unhashed(value.0, value.1) + } +} + +impl From for Extended { + fn from(value: TaikoTxEnvelope) -> Self { + Self::BuiltIn(value) + } +} + +impl From for EthereumTxEnvelope { + fn from(envelope: TaikoTxEnvelope) -> Self { + // Convert your Taiko envelope to Ethereum envelope + // This depends on your TaikoTxEnvelope structure + match envelope { + TaikoTxEnvelope::Legacy(tx) => EthereumTxEnvelope::Legacy(tx), + TaikoTxEnvelope::Eip2930(tx) => EthereumTxEnvelope::Eip2930(tx), + TaikoTxEnvelope::Eip1559(tx) => EthereumTxEnvelope::Eip1559(tx), + TaikoTxEnvelope::Eip7702(tx) => EthereumTxEnvelope::Eip7702(tx), + } + } +} + +impl TryFrom> for TaikoTxEnvelope { + type Error = EthereumTxEnvelope; + + fn try_from(value: EthereumTxEnvelope) -> Result { + Self::try_from_eth_envelope(value) + } +} + +impl TaikoTxEnvelope { + /// Creates a new enveloped transaction from the given transaction, signature and hash. + /// + /// Caution: This assumes the given hash is the correct transaction hash. + pub fn new_unchecked( + transaction: TaikoTypedTransaction, + signature: Signature, + hash: B256, + ) -> Self { + Signed::new_unchecked(transaction, signature, hash).into() + } + + /// Creates a new signed transaction from the given typed transaction and signature without the + /// hash. + /// + /// Note: this only calculates the hash on the first [`TaikoTxEnvelope::hash`] call. + pub fn new_unhashed(transaction: TaikoTypedTransaction, signature: Signature) -> Self { + transaction.into_signed(signature).into() + } + + /// Returns true if the transaction is a legacy transaction. + #[inline] + pub const fn is_legacy(&self) -> bool { + matches!(self, Self::Legacy(_)) + } + + /// Returns true if the transaction is an EIP-2930 transaction. + #[inline] + pub const fn is_eip2930(&self) -> bool { + matches!(self, Self::Eip2930(_)) + } + + /// Returns true if the transaction is an EIP-1559 transaction. + #[inline] + pub const fn is_eip1559(&self) -> bool { + matches!(self, Self::Eip1559(_)) + } + + /// Returns true if the transaction is a system transaction. + #[inline] + pub const fn is_system_transaction(&self) -> bool { + false + } + + /// Attempts to convert the envelope into the pooled variant. + pub fn try_into_pooled(self) -> Result> { + match self { + Self::Legacy(tx) => Ok(tx.into()), + Self::Eip2930(tx) => Ok(tx.into()), + Self::Eip1559(tx) => Ok(tx.into()), + Self::Eip7702(tx) => Ok(tx.into()), + } + } + + /// Attempts to convert the envelope into the ethereum pooled variant. + pub fn try_into_eth_pooled( + self, + ) -> Result> { + self.try_into_pooled().map(Into::into) + } + + /// Attempts to convert the taiko variant into an ethereum [`TxEnvelope`]. + pub fn try_into_eth_envelope(self) -> Result> { + match self { + Self::Legacy(tx) => Ok(tx.into()), + Self::Eip2930(tx) => Ok(tx.into()), + Self::Eip1559(tx) => Ok(tx.into()), + Self::Eip7702(tx) => Ok(tx.into()), + } + } + + /// Attempts to convert an ethereum [`TxEnvelope`] into the taiko variant. + /// + /// Returns the given envelope as error if [`TaikoTxEnvelope`] doesn't support the variant + /// (EIP-4844) + #[allow(clippy::result_large_err)] + pub fn try_from_eth_envelope( + tx: EthereumTxEnvelope, + ) -> Result> { + match tx { + EthereumTxEnvelope::Legacy(tx) => Ok(tx.into()), + EthereumTxEnvelope::Eip2930(tx) => Ok(tx.into()), + EthereumTxEnvelope::Eip1559(tx) => Ok(tx.into()), + tx @ EthereumTxEnvelope::::Eip4844(_) => Err(tx), + EthereumTxEnvelope::Eip7702(tx) => Ok(tx.into()), + } + } + + /// Returns mutable access to the input bytes. + /// + /// Caution: modifying this will cause side-effects on the hash. + #[doc(hidden)] + pub const fn input_mut(&mut self) -> &mut Bytes { + match self { + Self::Eip1559(tx) => &mut tx.tx_mut().input, + Self::Eip2930(tx) => &mut tx.tx_mut().input, + Self::Legacy(tx) => &mut tx.tx_mut().input, + Self::Eip7702(tx) => &mut tx.tx_mut().input, + } + } + + /// Returns the [`TxLegacy`] variant if the transaction is a legacy transaction. + pub const fn as_legacy(&self) -> Option<&Signed> { + match self { + Self::Legacy(tx) => Some(tx), + _ => None, + } + } + + /// Returns the [`TxEip2930`] variant if the transaction is an EIP-2930 transaction. + pub const fn as_eip2930(&self) -> Option<&Signed> { + match self { + Self::Eip2930(tx) => Some(tx), + _ => None, + } + } + + /// Returns the [`TxEip1559`] variant if the transaction is an EIP-1559 transaction. + pub const fn as_eip1559(&self) -> Option<&Signed> { + match self { + Self::Eip1559(tx) => Some(tx), + _ => None, + } + } + + /// Return the reference to signature. + /// + /// Returns `None` if this is a deposit variant. + pub const fn signature(&self) -> Option<&Signature> { + match self { + Self::Legacy(tx) => Some(tx.signature()), + Self::Eip2930(tx) => Some(tx.signature()), + Self::Eip1559(tx) => Some(tx.signature()), + Self::Eip7702(tx) => Some(tx.signature()), + } + } + + /// Return the [`OpTxType`] of the inner txn. + pub const fn tx_type(&self) -> TaikoTxType { + match self { + Self::Legacy(_) => TaikoTxType::Legacy, + Self::Eip2930(_) => TaikoTxType::Eip2930, + Self::Eip1559(_) => TaikoTxType::Eip1559, + Self::Eip7702(_) => TaikoTxType::Eip7702, + } + } + + /// Returns the inner transaction hash. + pub fn hash(&self) -> &B256 { + match self { + Self::Legacy(tx) => tx.hash(), + Self::Eip1559(tx) => tx.hash(), + Self::Eip2930(tx) => tx.hash(), + Self::Eip7702(tx) => tx.hash(), + } + } + + /// Returns the inner transaction hash. + pub fn tx_hash(&self) -> B256 { + *self.hash() + } + + /// Returns the signing hash for the transaction, used for signature verification. + /// + /// This delegates to the inner signed transaction's [`SignableTransaction::signature_hash`]. + pub fn signature_hash(&self) -> B256 { + match self { + Self::Legacy(tx) => tx.signature_hash(), + Self::Eip2930(tx) => tx.signature_hash(), + Self::Eip1559(tx) => tx.signature_hash(), + Self::Eip7702(tx) => tx.signature_hash(), + } + } + + /// Return the length of the inner txn, including type byte length + pub fn eip2718_encoded_length(&self) -> usize { + match self { + Self::Legacy(t) => t.eip2718_encoded_length(), + Self::Eip2930(t) => t.eip2718_encoded_length(), + Self::Eip1559(t) => t.eip2718_encoded_length(), + Self::Eip7702(t) => t.eip2718_encoded_length(), + } + } +} + +impl TxHashRef for TaikoTxEnvelope { + fn tx_hash(&self) -> &B256 { + Self::hash(self) + } +} + +#[cfg(feature = "k256")] +impl alloy_consensus::transaction::SignerRecoverable for TaikoTxEnvelope { + fn recover_signer( + &self, + ) -> Result { + let signature_hash = match self { + Self::Legacy(tx) => tx.signature_hash(), + Self::Eip2930(tx) => tx.signature_hash(), + Self::Eip1559(tx) => tx.signature_hash(), + Self::Eip7702(tx) => tx.signature_hash(), + }; + let signature = match self { + Self::Legacy(tx) => tx.signature(), + Self::Eip2930(tx) => tx.signature(), + Self::Eip1559(tx) => tx.signature(), + Self::Eip7702(tx) => tx.signature(), + }; + alloy_consensus::crypto::secp256k1::recover_signer(signature, signature_hash) + } + + fn recover_signer_unchecked( + &self, + ) -> Result { + let signature_hash = match self { + Self::Legacy(tx) => tx.signature_hash(), + Self::Eip2930(tx) => tx.signature_hash(), + Self::Eip1559(tx) => tx.signature_hash(), + Self::Eip7702(tx) => tx.signature_hash(), + }; + let signature = match self { + Self::Legacy(tx) => tx.signature(), + Self::Eip2930(tx) => tx.signature(), + Self::Eip1559(tx) => tx.signature(), + Self::Eip7702(tx) => tx.signature(), + }; + alloy_consensus::crypto::secp256k1::recover_signer_unchecked(signature, signature_hash) + } + + fn recover_unchecked_with_buf( + &self, + buf: &mut Vec, + ) -> Result { + match self { + Self::Legacy(tx) => { + alloy_consensus::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf) + } + Self::Eip2930(tx) => { + alloy_consensus::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf) + } + Self::Eip1559(tx) => { + alloy_consensus::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf) + } + Self::Eip7702(tx) => { + alloy_consensus::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf) + } + } + } +} + +impl InMemorySize for TaikoTxEnvelope { + fn size(&self) -> usize { + match self { + Self::Legacy(tx) => tx.size(), + Self::Eip2930(tx) => tx.size(), + Self::Eip1559(tx) => tx.size(), + Self::Eip7702(tx) => tx.size(), + } + } +} + +#[cfg(feature = "k256")] +impl reth_primitives_traits::SignedTransaction for TaikoTxEnvelope {} + +/// Bincode-compatible serde implementation for TaikoTxEnvelope. +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub mod serde_bincode_compat { + use alloy_consensus::{ + Signed, + transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxEip7702, TxLegacy}, + }; + use alloy_primitives::Signature; + use reth_primitives_traits::serde_bincode_compat::SerdeBincodeCompat; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + + /// Bincode-compatible representation of an TaikoTxEnvelope. + #[derive(Debug, Serialize, Deserialize)] + pub enum TaikoTxEnvelope<'a> { + /// Legacy variant. + Legacy { + /// Transaction signature. + signature: Signature, + /// Borrowed legacy transaction data. + transaction: TxLegacy<'a>, + }, + /// EIP-2930 variant. + Eip2930 { + /// Transaction signature. + signature: Signature, + /// Borrowed EIP-2930 transaction data. + transaction: TxEip2930<'a>, + }, + /// EIP-1559 variant. + Eip1559 { + /// Transaction signature. + signature: Signature, + /// Borrowed EIP-1559 transaction data. + transaction: TxEip1559<'a>, + }, + /// EIP-7702 variant. + Eip7702 { + /// Transaction signature. + signature: Signature, + /// Borrowed EIP-7702 transaction data. + transaction: TxEip7702<'a>, + }, + } + + impl<'a> From<&'a super::TaikoTxEnvelope> for TaikoTxEnvelope<'a> { + fn from(value: &'a super::TaikoTxEnvelope) -> Self { + match value { + super::TaikoTxEnvelope::Legacy(signed_legacy) => Self::Legacy { + signature: *signed_legacy.signature(), + transaction: signed_legacy.tx().into(), + }, + super::TaikoTxEnvelope::Eip2930(signed_2930) => Self::Eip2930 { + signature: *signed_2930.signature(), + transaction: signed_2930.tx().into(), + }, + super::TaikoTxEnvelope::Eip1559(signed_1559) => Self::Eip1559 { + signature: *signed_1559.signature(), + transaction: signed_1559.tx().into(), + }, + super::TaikoTxEnvelope::Eip7702(signed_7702) => Self::Eip7702 { + signature: *signed_7702.signature(), + transaction: signed_7702.tx().into(), + }, + } + } + } + + impl<'a> From> for super::TaikoTxEnvelope { + fn from(value: TaikoTxEnvelope<'a>) -> Self { + match value { + TaikoTxEnvelope::Legacy { signature, transaction } => { + Self::Legacy(Signed::new_unhashed(transaction.into(), signature)) + } + TaikoTxEnvelope::Eip2930 { signature, transaction } => { + Self::Eip2930(Signed::new_unhashed(transaction.into(), signature)) + } + TaikoTxEnvelope::Eip1559 { signature, transaction } => { + Self::Eip1559(Signed::new_unhashed(transaction.into(), signature)) + } + TaikoTxEnvelope::Eip7702 { signature, transaction } => { + Self::Eip7702(Signed::new_unhashed(transaction.into(), signature)) + } + } + } + } + + impl SerializeAs for TaikoTxEnvelope<'_> { + fn serialize_as( + source: &super::TaikoTxEnvelope, + serializer: S, + ) -> Result + where + S: Serializer, + { + let borrowed = TaikoTxEnvelope::from(source); + borrowed.serialize(serializer) + } + } + + impl<'de> DeserializeAs<'de, super::TaikoTxEnvelope> for TaikoTxEnvelope<'de> { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let borrowed = TaikoTxEnvelope::deserialize(deserializer)?; + Ok(borrowed.into()) + } + } + + impl SerdeBincodeCompat for super::TaikoTxEnvelope { + type BincodeRepr<'a> = TaikoTxEnvelope<'a>; + + fn as_repr(&self) -> Self::BincodeRepr<'_> { + self.into() + } + + fn from_repr(repr: Self::BincodeRepr<'_>) -> Self { + repr.into() + } + } + + #[cfg(test)] + mod tests { + use super::*; + use arbitrary::Arbitrary; + use rand::Rng; + use serde::{Deserialize, Serialize}; + use serde_with::serde_as; + + /// Tests a bincode round-trip for TaikoTxEnvelope using an arbitrary instance. + #[test] + fn test_taiko_tx_envelope_bincode_roundtrip_arbitrary() { + #[serde_as] + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Data { + // Use the bincode-compatible representation defined in this module. + #[serde_as(as = "TaikoTxEnvelope<'_>")] + envelope: super::super::TaikoTxEnvelope, + } + + let mut bytes = [0u8; 1024]; + rand::rng().fill(bytes.as_mut_slice()); + let data = Data { + envelope: super::super::TaikoTxEnvelope::arbitrary( + &mut arbitrary::Unstructured::new(&bytes), + ) + .unwrap(), + }; + + let encoded = bincode::serde::encode_to_vec(&data, bincode::config::legacy()).unwrap(); + let (decoded, _) = + bincode::serde::decode_from_slice::(&encoded, bincode::config::legacy()) + .unwrap(); + assert_eq!(decoded, data); + } + } +} + +impl FromRecoveredTx for TxEnv { + fn from_recovered_tx(tx: &TaikoTxEnvelope, caller: Address) -> Self { + match tx { + TaikoTxEnvelope::Legacy(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + TaikoTxEnvelope::Eip1559(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + TaikoTxEnvelope::Eip2930(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + TaikoTxEnvelope::Eip7702(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + } + } +} + +impl FromTxWithEncoded for TxEnv { + fn from_encoded_tx(tx: &TaikoTxEnvelope, sender: Address, _encoded: Bytes) -> Self { + TxEnv::from_recovered_tx(tx, sender) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_consensus::SignableTransaction; + use alloy_primitives::Signature; + + fn create_test_eip1559_enveloped_tx() -> TaikoTxEnvelope { + use arbitrary::Arbitrary; + use rand::Rng; + + let mut bytes = [0u8; 1024]; + rand::rng().fill(&mut bytes); + + let tx = TxEip1559::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap(); + let sig = Signature::test_signature(); + TaikoTxEnvelope::Eip1559(tx.into_signed(sig)) + } + + #[test] + fn test_encode_decode_eip1559_arbitrary() { + let tx_envelope = create_test_eip1559_enveloped_tx(); + + let encoded = tx_envelope.encoded_2718(); + let decoded = TaikoTxEnvelope::decode_2718(&mut encoded.as_ref()).unwrap(); + + assert_eq!(encoded.len(), tx_envelope.encode_2718_len()); + assert_eq!(decoded, tx_envelope); + } + + #[test] + #[cfg(feature = "serde")] + fn test_serde_roundtrip_deposit() { + let tx_envelope = create_test_eip1559_enveloped_tx(); + + let serialized = serde_json::to_string(&tx_envelope).unwrap(); + let deserialized: TaikoTxEnvelope = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(tx_envelope, deserialized); + } + + #[test] + fn eip1559_decode() { + let envelope = create_test_eip1559_enveloped_tx(); + + let encoded = envelope.encoded_2718(); + let mut slice = encoded.as_slice(); + let decoded = TaikoTxEnvelope::decode_2718(&mut slice).unwrap(); + assert!(matches!(decoded, TaikoTxEnvelope::Eip1559(_))); + } +} diff --git a/crates/consensus/src/transaction/mod.rs b/crates/consensus/src/transaction/mod.rs new file mode 100644 index 00000000..1ccba918 --- /dev/null +++ b/crates/consensus/src/transaction/mod.rs @@ -0,0 +1,27 @@ +//! Transaction types for Taiko. + +mod tx_type; + +/// Transaction envelope types for Taiko transactions. +mod envelope; +pub use envelope::{TaikoTxEnvelope, TaikoTxType}; + +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub use envelope::serde_bincode_compat as envelope_serde_bincode_compat; + +/// Typed transaction representation for Taiko. +mod typed; +pub use typed::TaikoTypedTransaction; + +mod pooled; +pub use pooled::TaikoPooledTransaction; + +/// Compression support for transaction storage. +#[cfg(feature = "reth-codec")] +mod compress; + +/// Bincode-compatible serde implementations for transaction types. +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub mod serde_bincode_compat { + pub use super::envelope::serde_bincode_compat::*; +} diff --git a/crates/consensus/src/transaction/pooled.rs b/crates/consensus/src/transaction/pooled.rs new file mode 100644 index 00000000..8579fb2e --- /dev/null +++ b/crates/consensus/src/transaction/pooled.rs @@ -0,0 +1,352 @@ +//! Defines the exact transaction variants that are allowed to be propagated over the eth p2p +//! protocol in op. + +use alloy_consensus::{ + Extended, SignableTransaction, Signed, TransactionEnvelope, TxEip7702, TxEnvelope, + error::ValueError, + transaction::{TxEip1559, TxEip2930, TxHashRef, TxLegacy}, +}; +use alloy_eips::eip2718::Encodable2718; +use alloy_primitives::{B256, Signature, TxHash, bytes}; +use core::hash::Hash; +use reth_primitives_traits::InMemorySize; + +use crate::transaction::TaikoTxEnvelope; + +/// All possible transactions that can be included in a response to `GetPooledTransactions`. +/// A response to `GetPooledTransactions`. This can include a typed signed transaction, but cannot +/// include a deposit transaction or EIP-4844 transaction. +/// +/// The difference between this and the [`TaikoTxEnvelope`] is that this type does not have the +/// deposit transaction variant, which is not expected to be pooled. +/// +/// Note: this struct is the same as TaikoTxEvelope for now, but they are kept separate for +/// future extensibility. +#[derive(Clone, Debug, TransactionEnvelope)] +#[envelope(tx_type_name = OpPooledTxType, serde_cfg(feature = "serde"))] +pub enum TaikoPooledTransaction { + /// An untagged [`TxLegacy`]. + #[envelope(ty = 0)] + Legacy(Signed), + /// A [`TxEip2930`] transaction tagged with type 1. + #[envelope(ty = 1)] + Eip2930(Signed), + /// A [`TxEip1559`] transaction tagged with type 2. + #[envelope(ty = 2)] + Eip1559(Signed), + /// A [`TxEip7702`] transaction tagged with type 4. + #[envelope(ty = 4)] + Eip7702(Signed), +} + +impl TaikoPooledTransaction { + /// Heavy operation that returns the signature hash over rlp encoded transaction. It is only + /// for signature signing or signer recovery. + pub fn signature_hash(&self) -> B256 { + match self { + Self::Legacy(tx) => tx.signature_hash(), + Self::Eip2930(tx) => tx.signature_hash(), + Self::Eip1559(tx) => tx.signature_hash(), + Self::Eip7702(tx) => tx.signature_hash(), + } + } + + /// Reference to transaction hash. Used to identify transaction. + pub fn hash(&self) -> &TxHash { + match self { + Self::Legacy(tx) => tx.hash(), + Self::Eip2930(tx) => tx.hash(), + Self::Eip1559(tx) => tx.hash(), + Self::Eip7702(tx) => tx.hash(), + } + } + + /// Returns the signature of the transaction. + pub const fn signature(&self) -> &Signature { + match self { + Self::Legacy(tx) => tx.signature(), + Self::Eip2930(tx) => tx.signature(), + Self::Eip1559(tx) => tx.signature(), + Self::Eip7702(tx) => tx.signature(), + } + } + + /// This encodes the transaction _without_ the signature, and is only suitable for creating a + /// hash intended for signing. + pub fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { + match self { + Self::Legacy(tx) => tx.tx().encode_for_signing(out), + Self::Eip2930(tx) => tx.tx().encode_for_signing(out), + Self::Eip1559(tx) => tx.tx().encode_for_signing(out), + Self::Eip7702(tx) => tx.tx().encode_for_signing(out), + } + } + + /// Converts the transaction into the ethereum [`TxEnvelope`]. + pub fn into_envelope(self) -> TxEnvelope { + match self { + Self::Legacy(tx) => tx.into(), + Self::Eip2930(tx) => tx.into(), + Self::Eip1559(tx) => tx.into(), + Self::Eip7702(tx) => tx.into(), + } + } + + /// Converts the transaction into the [`TaikoTxEnvelope`]. + pub fn into_taiko_envelope(self) -> TaikoTxEnvelope { + match self { + Self::Legacy(tx) => tx.into(), + Self::Eip2930(tx) => tx.into(), + Self::Eip1559(tx) => tx.into(), + Self::Eip7702(tx) => tx.into(), + } + } + + /// Returns the [`TxLegacy`] variant if the transaction is a legacy transaction. + pub const fn as_legacy(&self) -> Option<&TxLegacy> { + match self { + Self::Legacy(tx) => Some(tx.tx()), + _ => None, + } + } + + /// Returns the [`TxEip2930`] variant if the transaction is an EIP-2930 transaction. + pub const fn as_eip2930(&self) -> Option<&TxEip2930> { + match self { + Self::Eip2930(tx) => Some(tx.tx()), + _ => None, + } + } + + /// Returns the [`TxEip1559`] variant if the transaction is an EIP-1559 transaction. + pub const fn as_eip1559(&self) -> Option<&TxEip1559> { + match self { + Self::Eip1559(tx) => Some(tx.tx()), + _ => None, + } + } + + /// Returns the [`TxEip7702`] variant if the transaction is an EIP-7702 transaction. + pub const fn as_eip7702(&self) -> Option<&TxEip7702> { + match self { + Self::Eip7702(tx) => Some(tx.tx()), + _ => None, + } + } +} + +impl From> for TaikoPooledTransaction { + fn from(v: Signed) -> Self { + Self::Legacy(v) + } +} + +impl From> for TaikoPooledTransaction { + fn from(v: Signed) -> Self { + Self::Eip2930(v) + } +} + +impl From> for TaikoPooledTransaction { + fn from(v: Signed) -> Self { + Self::Eip1559(v) + } +} + +impl From> for TaikoPooledTransaction { + fn from(v: Signed) -> Self { + Self::Eip7702(v) + } +} + +impl From for alloy_consensus::transaction::PooledTransaction { + fn from(value: TaikoPooledTransaction) -> Self { + match value { + TaikoPooledTransaction::Legacy(tx) => tx.into(), + TaikoPooledTransaction::Eip2930(tx) => tx.into(), + TaikoPooledTransaction::Eip1559(tx) => tx.into(), + TaikoPooledTransaction::Eip7702(tx) => tx.into(), + } + } +} + +impl TxHashRef for TaikoPooledTransaction { + fn tx_hash(&self) -> &B256 { + Self::hash(self) + } +} + +#[cfg(feature = "k256")] +impl alloy_consensus::transaction::SignerRecoverable for TaikoPooledTransaction { + fn recover_signer( + &self, + ) -> Result { + let signature_hash = self.signature_hash(); + alloy_consensus::crypto::secp256k1::recover_signer(self.signature(), signature_hash) + } + + fn recover_signer_unchecked( + &self, + ) -> Result { + let signature_hash = self.signature_hash(); + alloy_consensus::crypto::secp256k1::recover_signer_unchecked( + self.signature(), + signature_hash, + ) + } + + fn recover_unchecked_with_buf( + &self, + buf: &mut Vec, + ) -> Result { + match self { + Self::Legacy(tx) => { + alloy_consensus::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf) + } + Self::Eip2930(tx) => { + alloy_consensus::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf) + } + Self::Eip1559(tx) => { + alloy_consensus::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf) + } + Self::Eip7702(tx) => { + alloy_consensus::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf) + } + } + } +} + +impl From for TxEnvelope { + fn from(tx: TaikoPooledTransaction) -> Self { + tx.into_envelope() + } +} + +impl From for TaikoTxEnvelope { + fn from(tx: TaikoPooledTransaction) -> Self { + tx.into_taiko_envelope() + } +} + +impl TryFrom for TaikoPooledTransaction { + type Error = ValueError; + + fn try_from(value: TaikoTxEnvelope) -> Result { + value.try_into_pooled() + } +} + +impl From for Extended { + fn from(tx: TaikoPooledTransaction) -> Self { + Self::BuiltIn(tx.into()) + } +} + +impl TryFrom> for TaikoPooledTransaction { + type Error = (); + + fn try_from(_tx: Extended) -> Result { + match _tx { + Extended::BuiltIn(inner) => inner.try_into().map_err(|_| ()), + Extended::Other(_tx) => Err(()), + } + } +} + +#[cfg(feature = "k256")] +impl reth_primitives_traits::SignedTransaction for TaikoPooledTransaction {} + +impl InMemorySize for TaikoPooledTransaction { + fn size(&self) -> usize { + match self { + Self::Legacy(tx) => tx.size(), + Self::Eip2930(tx) => tx.size(), + Self::Eip1559(tx) => tx.size(), + Self::Eip7702(tx) => tx.size(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_consensus::Transaction; + use alloy_primitives::{address, hex}; + use alloy_rlp::Decodable; + use bytes::Bytes; + + #[test] + fn invalid_legacy_pooled_decoding_input_too_short() { + let input_too_short = [ + // this should fail because the payload length is longer than expected + &hex!("d90b0280808bc5cd028083c5cdfd9e407c56565656")[..], + // these should fail decoding + // + // The `c1` at the beginning is a list header, and the rest is a valid legacy + // transaction, BUT the payload length of the list header is 1, and the payload is + // obviously longer than one byte. + &hex!("c10b02808083c5cd028883c5cdfd9e407c56565656"), + &hex!("c10b0280808bc5cd028083c5cdfd9e407c56565656"), + // this one is 19 bytes, and the buf is long enough, but the transaction will not + // consume that many bytes. + &hex!("d40b02808083c5cdeb8783c5acfd9e407c5656565656"), + &hex!("d30102808083c5cd02887dc5cdfd9e64fd9e407c56"), + ]; + + for hex_data in &input_too_short { + let input_rlp = &mut &hex_data[..]; + let res = TaikoPooledTransaction::decode(input_rlp); + + assert!( + res.is_err(), + "expected err after decoding rlp input: {:x?}", + Bytes::copy_from_slice(hex_data) + ); + + // this is a legacy tx so we can attempt the same test with decode_enveloped + let input_rlp = &mut &hex_data[..]; + let res = TaikoPooledTransaction::decode_2718(input_rlp); + + assert!( + res.is_err(), + "expected err after decoding enveloped rlp input: {:x?}", + Bytes::copy_from_slice(hex_data) + ); + } + } + + // + #[test] + fn decode_eip1559_enveloped() { + let data = hex!( + "02f903d382426882ba09832dc6c0848674742682ed9694714b6a4ea9b94a8a7d9fd362ed72630688c8898c80b90364492d24749189822d8512430d3f3ff7a2ede675ac08265c08e2c56ff6fdaa66dae1cdbe4a5d1d7809f3e99272d067364e597542ac0c369d69e22a6399c3e9bee5da4b07e3f3fdc34c32c3d88aa2268785f3e3f8086df0934b10ef92cfffc2e7f3d90f5e83302e31382e302d64657600000000000000000000000000000000000000000000569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd000000000000000000000000e1e210594771824dad216568b91c9cb4ceed361c00000000000000000000000000000000000000000000000000000000000546e00000000000000000000000000000000000000000000000000000000000e4e1c00000000000000000000000000000000000000000000000000000000065d6750c00000000000000000000000000000000000000000000000000000000000f288000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002cf600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000f1628e56fa6d8c50e5b984a58c0df14de31c7b857ce7ba499945b99252976a93d06dcda6776fc42167fbe71cb59f978f5ef5b12577a90b132d14d9c6efa528076f0161d7bf03643cfc5490ec5084f4a041db7f06c50bd97efa08907ba79ddcac8b890f24d12d8db31abbaaf18985d54f400449ee0559a4452afe53de5853ce090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000064ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000c080a01428023fc54a27544abc421d5d017b9a7c5936ad501cbdecd0d9d12d04c1a033a0753104bbf1c87634d6ff3f0ffa0982710612306003eb022363b57994bdef445a" + ); + + let res = TaikoPooledTransaction::decode_2718(&mut &data[..]).unwrap(); + assert_eq!(res.to(), Some(address!("714b6a4ea9b94a8a7d9fd362ed72630688c8898c"))); + } + + #[test] + fn legacy_valid_pooled_decoding() { + // d3 <- payload length, d3 - c0 = 0x13 = 19 + // 0b <- nonce + // 02 <- gas_price + // 80 <- gas_limit + // 80 <- to (Create) + // 83 c5cdeb <- value + // 87 83c5acfd9e407c <- input + // 56 <- v (eip155, so modified with a chain id) + // 56 <- r + // 56 <- s + let data = &hex!("d30b02808083c5cdeb8783c5acfd9e407c565656")[..]; + + let input_rlp = &mut &data[..]; + let res = TaikoPooledTransaction::decode(input_rlp); + assert!(res.is_ok()); + assert!(input_rlp.is_empty()); + + // we can also decode_enveloped + let res = TaikoPooledTransaction::decode_2718(&mut &data[..]); + assert!(res.is_ok()); + } +} diff --git a/crates/consensus/src/transaction/tx_type.rs b/crates/consensus/src/transaction/tx_type.rs new file mode 100644 index 00000000..f60216c0 --- /dev/null +++ b/crates/consensus/src/transaction/tx_type.rs @@ -0,0 +1,68 @@ +//! Contains the transaction type identifier for Taiko. + +use alloy_consensus::TxType; + +use crate::transaction::envelope::TaikoTxType; +use core::fmt::Display; + +#[allow(clippy::derivable_impls)] +impl Default for TaikoTxType { + fn default() -> Self { + Self::Legacy + } +} + +impl Display for TaikoTxType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Legacy => write!(f, "legacy"), + Self::Eip2930 => write!(f, "eip2930"), + Self::Eip1559 => write!(f, "eip1559"), + Self::Eip7702 => write!(f, "eip7702"), + } + } +} + +impl TaikoTxType { + /// List of all variants. + pub const ALL: [Self; 4] = [Self::Legacy, Self::Eip2930, Self::Eip1559, Self::Eip7702]; +} + +impl From for TxType { + fn from(value: TaikoTxType) -> Self { + match value { + TaikoTxType::Legacy => TxType::Legacy, + TaikoTxType::Eip2930 => TxType::Eip2930, + TaikoTxType::Eip1559 => TxType::Eip1559, + TaikoTxType::Eip7702 => TxType::Eip7702, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_rlp::{Decodable, Encodable}; + + #[test] + fn test_all_tx_types() { + assert_eq!(TaikoTxType::ALL.len(), 4); + let all = vec![ + TaikoTxType::Legacy, + TaikoTxType::Eip2930, + TaikoTxType::Eip1559, + TaikoTxType::Eip7702, + ]; + assert_eq!(TaikoTxType::ALL.to_vec(), all); + } + + #[test] + fn tx_type_roundtrip() { + for &tx_type in &TaikoTxType::ALL { + let mut buf = Vec::new(); + tx_type.encode(&mut buf); + let decoded = TaikoTxType::decode(&mut &buf[..]).unwrap(); + assert_eq!(tx_type, decoded); + } + } +} diff --git a/crates/consensus/src/transaction/typed.rs b/crates/consensus/src/transaction/typed.rs new file mode 100644 index 00000000..8c95c1df --- /dev/null +++ b/crates/consensus/src/transaction/typed.rs @@ -0,0 +1,535 @@ +use alloy_consensus::{ + EthereumTypedTransaction, SignableTransaction, Signed, Transaction, TxEip1559, TxEip2930, + TxEip7702, TxLegacy, Typed2718, TypedTransaction, error::ValueError, + transaction::RlpEcdsaEncodableTx, +}; +use alloy_eips::{eip2718::IsTyped2718, eip2930::AccessList}; +use alloy_primitives::{Address, B256, Bytes, ChainId, Signature, TxHash, TxKind, bytes::BufMut}; + +use crate::transaction::{TaikoTxEnvelope, TaikoTxType}; + +/// The TypedTransaction enum represents all Ethereum transaction request types, modified for the OP +/// Stack. +/// +/// Its variants correspond to specific allowed transactions: +/// 1. Legacy (pre-EIP2718) [`TxLegacy`] +/// 2. EIP2930 (state access lists) [`TxEip2930`] +/// 3. EIP1559 [`TxEip1559`] +/// 4. EIP7702 [`TxEip7702`] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr( + feature = "serde", + serde( + from = "serde_from::MaybeTaggedTypedTransaction", + into = "serde_from::TaggedTypedTransaction" + ) +)] +pub enum TaikoTypedTransaction { + /// Legacy transaction + Legacy(TxLegacy), + /// EIP-2930 transaction + Eip2930(TxEip2930), + /// EIP-1559 transaction + Eip1559(TxEip1559), + /// EIP-7702 transaction + Eip7702(TxEip7702), +} + +impl From for TaikoTypedTransaction { + fn from(tx: TxLegacy) -> Self { + Self::Legacy(tx) + } +} + +impl From for TaikoTypedTransaction { + fn from(tx: TxEip2930) -> Self { + Self::Eip2930(tx) + } +} + +impl From for TaikoTypedTransaction { + fn from(tx: TxEip1559) -> Self { + Self::Eip1559(tx) + } +} + +impl From for TaikoTypedTransaction { + fn from(tx: TxEip7702) -> Self { + Self::Eip7702(tx) + } +} + +impl From for TaikoTypedTransaction { + fn from(envelope: TaikoTxEnvelope) -> Self { + match envelope { + TaikoTxEnvelope::Legacy(tx) => Self::Legacy(tx.strip_signature()), + TaikoTxEnvelope::Eip2930(tx) => Self::Eip2930(tx.strip_signature()), + TaikoTxEnvelope::Eip1559(tx) => Self::Eip1559(tx.strip_signature()), + TaikoTxEnvelope::Eip7702(tx) => Self::Eip7702(tx.strip_signature()), + } + } +} + +impl TryFrom for EthereumTypedTransaction { + type Error = ValueError; + + fn try_from(value: TaikoTypedTransaction) -> Result { + value.try_into_eth_variant() + } +} + +impl TaikoTypedTransaction { + /// Return the [`TaikoTxType`] of the inner txn. + pub const fn tx_type(&self) -> TaikoTxType { + match self { + Self::Legacy(_) => TaikoTxType::Legacy, + Self::Eip2930(_) => TaikoTxType::Eip2930, + Self::Eip1559(_) => TaikoTxType::Eip1559, + Self::Eip7702(_) => TaikoTxType::Eip7702, + } + } + + /// Calculates the signing hash for the transaction. + /// + /// Returns `None` if the tx is a deposit transaction. + pub fn checked_signature_hash(&self) -> Option { + match self { + Self::Legacy(tx) => Some(tx.signature_hash()), + Self::Eip2930(tx) => Some(tx.signature_hash()), + Self::Eip1559(tx) => Some(tx.signature_hash()), + Self::Eip7702(tx) => Some(tx.signature_hash()), + } + } + + /// Return the inner legacy transaction if it exists. + pub const fn legacy(&self) -> Option<&TxLegacy> { + match self { + Self::Legacy(tx) => Some(tx), + _ => None, + } + } + + /// Return the inner EIP-2930 transaction if it exists. + pub const fn eip2930(&self) -> Option<&TxEip2930> { + match self { + Self::Eip2930(tx) => Some(tx), + _ => None, + } + } + + /// Return the inner EIP-1559 transaction if it exists. + pub const fn eip1559(&self) -> Option<&TxEip1559> { + match self { + Self::Eip1559(tx) => Some(tx), + _ => None, + } + } + + /// Calculate the transaction hash for the given signature. + /// + /// Note: Returns the regular tx hash if this is a deposit variant + pub fn tx_hash(&self, signature: &Signature) -> TxHash { + match self { + Self::Legacy(tx) => tx.tx_hash(signature), + Self::Eip2930(tx) => tx.tx_hash(signature), + Self::Eip1559(tx) => tx.tx_hash(signature), + Self::Eip7702(tx) => tx.tx_hash(signature), + } + } + + /// Convenience function to convert this typed transaction into an [`TaikoTxEnvelope`]. + pub fn into_envelope(self, signature: Signature) -> TaikoTxEnvelope { + self.into_signed(signature).into() + } + + /// Attempts to convert the Taiko variant into an ethereum [`TypedTransaction`]. + /// + /// Currently all Taiko transaction types are compatible with Ethereum. + pub fn try_into_eth(self) -> Result> { + self.try_into_eth_variant() + } + + /// Attempts to convert the Taiko variant into an ethereum [`TypedTransaction`]. + /// + /// Currently all Taiko transaction types are compatible with Ethereum. + pub fn try_into_eth_variant( + self, + ) -> Result, ValueError> { + match self { + Self::Legacy(tx) => Ok(tx.into()), + Self::Eip2930(tx) => Ok(tx.into()), + Self::Eip1559(tx) => Ok(tx.into()), + Self::Eip7702(tx) => Ok(tx.into()), + } + } +} + +impl Typed2718 for TaikoTypedTransaction { + fn ty(&self) -> u8 { + match self { + Self::Legacy(_) => TaikoTxType::Legacy as u8, + Self::Eip2930(_) => TaikoTxType::Eip2930 as u8, + Self::Eip1559(_) => TaikoTxType::Eip1559 as u8, + Self::Eip7702(_) => TaikoTxType::Eip7702 as u8, + } + } +} + +impl IsTyped2718 for TaikoTypedTransaction { + fn is_type(type_id: u8) -> bool { + ::is_type(type_id) + } +} + +impl Transaction for TaikoTypedTransaction { + fn chain_id(&self) -> Option { + match self { + Self::Legacy(tx) => tx.chain_id(), + Self::Eip2930(tx) => tx.chain_id(), + Self::Eip1559(tx) => tx.chain_id(), + Self::Eip7702(tx) => tx.chain_id(), + } + } + + fn nonce(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.nonce(), + Self::Eip2930(tx) => tx.nonce(), + Self::Eip1559(tx) => tx.nonce(), + Self::Eip7702(tx) => tx.nonce(), + } + } + + fn gas_limit(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.gas_limit(), + Self::Eip2930(tx) => tx.gas_limit(), + Self::Eip1559(tx) => tx.gas_limit(), + Self::Eip7702(tx) => tx.gas_limit(), + } + } + + fn gas_price(&self) -> Option { + match self { + Self::Legacy(tx) => tx.gas_price(), + Self::Eip2930(tx) => tx.gas_price(), + Self::Eip1559(tx) => tx.gas_price(), + Self::Eip7702(tx) => tx.gas_price(), + } + } + + fn max_fee_per_gas(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.max_fee_per_gas(), + Self::Eip2930(tx) => tx.max_fee_per_gas(), + Self::Eip1559(tx) => tx.max_fee_per_gas(), + Self::Eip7702(tx) => tx.max_fee_per_gas(), + } + } + + fn max_priority_fee_per_gas(&self) -> Option { + match self { + Self::Legacy(tx) => tx.max_priority_fee_per_gas(), + Self::Eip2930(tx) => tx.max_priority_fee_per_gas(), + Self::Eip1559(tx) => tx.max_priority_fee_per_gas(), + Self::Eip7702(tx) => tx.max_priority_fee_per_gas(), + } + } + + fn max_fee_per_blob_gas(&self) -> Option { + match self { + Self::Legacy(tx) => tx.max_fee_per_blob_gas(), + Self::Eip2930(tx) => tx.max_fee_per_blob_gas(), + Self::Eip1559(tx) => tx.max_fee_per_blob_gas(), + Self::Eip7702(tx) => tx.max_fee_per_blob_gas(), + } + } + + fn priority_fee_or_price(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.priority_fee_or_price(), + Self::Eip2930(tx) => tx.priority_fee_or_price(), + Self::Eip1559(tx) => tx.priority_fee_or_price(), + Self::Eip7702(tx) => tx.priority_fee_or_price(), + } + } + + fn effective_gas_price(&self, base_fee: Option) -> u128 { + match self { + Self::Legacy(tx) => tx.effective_gas_price(base_fee), + Self::Eip2930(tx) => tx.effective_gas_price(base_fee), + Self::Eip1559(tx) => tx.effective_gas_price(base_fee), + Self::Eip7702(tx) => tx.effective_gas_price(base_fee), + } + } + + fn is_dynamic_fee(&self) -> bool { + match self { + Self::Legacy(tx) => tx.is_dynamic_fee(), + Self::Eip2930(tx) => tx.is_dynamic_fee(), + Self::Eip1559(tx) => tx.is_dynamic_fee(), + Self::Eip7702(tx) => tx.is_dynamic_fee(), + } + } + + fn kind(&self) -> TxKind { + match self { + Self::Legacy(tx) => tx.kind(), + Self::Eip2930(tx) => tx.kind(), + Self::Eip1559(tx) => tx.kind(), + Self::Eip7702(tx) => tx.kind(), + } + } + + fn is_create(&self) -> bool { + match self { + Self::Legacy(tx) => tx.is_create(), + Self::Eip2930(tx) => tx.is_create(), + Self::Eip1559(tx) => tx.is_create(), + Self::Eip7702(tx) => tx.is_create(), + } + } + + fn to(&self) -> Option
{ + match self { + Self::Legacy(tx) => tx.to(), + Self::Eip2930(tx) => tx.to(), + Self::Eip1559(tx) => tx.to(), + Self::Eip7702(tx) => tx.to(), + } + } + + fn value(&self) -> alloy_primitives::U256 { + match self { + Self::Legacy(tx) => tx.value(), + Self::Eip2930(tx) => tx.value(), + Self::Eip1559(tx) => tx.value(), + Self::Eip7702(tx) => tx.value(), + } + } + + fn input(&self) -> &Bytes { + match self { + Self::Legacy(tx) => tx.input(), + Self::Eip2930(tx) => tx.input(), + Self::Eip1559(tx) => tx.input(), + Self::Eip7702(tx) => tx.input(), + } + } + + fn access_list(&self) -> Option<&AccessList> { + match self { + Self::Legacy(tx) => tx.access_list(), + Self::Eip2930(tx) => tx.access_list(), + Self::Eip1559(tx) => tx.access_list(), + Self::Eip7702(tx) => tx.access_list(), + } + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + match self { + Self::Legacy(tx) => tx.blob_versioned_hashes(), + Self::Eip2930(tx) => tx.blob_versioned_hashes(), + Self::Eip1559(tx) => tx.blob_versioned_hashes(), + Self::Eip7702(tx) => tx.blob_versioned_hashes(), + } + } + + fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { + match self { + Self::Legacy(tx) => tx.authorization_list(), + Self::Eip2930(tx) => tx.authorization_list(), + Self::Eip1559(tx) => tx.authorization_list(), + Self::Eip7702(tx) => tx.authorization_list(), + } + } +} + +impl RlpEcdsaEncodableTx for TaikoTypedTransaction { + fn rlp_encoded_fields_length(&self) -> usize { + match self { + Self::Legacy(tx) => tx.rlp_encoded_fields_length(), + Self::Eip2930(tx) => tx.rlp_encoded_fields_length(), + Self::Eip1559(tx) => tx.rlp_encoded_fields_length(), + Self::Eip7702(tx) => tx.rlp_encoded_fields_length(), + } + } + + fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) { + match self { + Self::Legacy(tx) => tx.rlp_encode_fields(out), + Self::Eip2930(tx) => tx.rlp_encode_fields(out), + Self::Eip1559(tx) => tx.rlp_encode_fields(out), + Self::Eip7702(tx) => tx.rlp_encode_fields(out), + } + } + + fn eip2718_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), + Self::Eip2930(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), + Self::Eip1559(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), + Self::Eip7702(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), + } + } + + fn eip2718_encode(&self, signature: &Signature, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.eip2718_encode(signature, out), + Self::Eip2930(tx) => tx.eip2718_encode(signature, out), + Self::Eip1559(tx) => tx.eip2718_encode(signature, out), + Self::Eip7702(tx) => tx.eip2718_encode(signature, out), + } + } + + fn network_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.network_encode_with_type(signature, tx.ty(), out), + Self::Eip2930(tx) => tx.network_encode_with_type(signature, tx.ty(), out), + Self::Eip1559(tx) => tx.network_encode_with_type(signature, tx.ty(), out), + Self::Eip7702(tx) => tx.network_encode_with_type(signature, tx.ty(), out), + } + } + + fn network_encode(&self, signature: &Signature, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.network_encode(signature, out), + Self::Eip2930(tx) => tx.network_encode(signature, out), + Self::Eip1559(tx) => tx.network_encode(signature, out), + Self::Eip7702(tx) => tx.network_encode(signature, out), + } + } + + fn tx_hash_with_type(&self, signature: &Signature, _ty: u8) -> TxHash { + match self { + Self::Legacy(tx) => tx.tx_hash_with_type(signature, tx.ty()), + Self::Eip2930(tx) => tx.tx_hash_with_type(signature, tx.ty()), + Self::Eip1559(tx) => tx.tx_hash_with_type(signature, tx.ty()), + Self::Eip7702(tx) => tx.tx_hash_with_type(signature, tx.ty()), + } + } + + fn tx_hash(&self, signature: &Signature) -> TxHash { + match self { + Self::Legacy(tx) => tx.tx_hash(signature), + Self::Eip2930(tx) => tx.tx_hash(signature), + Self::Eip1559(tx) => tx.tx_hash(signature), + Self::Eip7702(tx) => tx.tx_hash(signature), + } + } +} + +impl SignableTransaction for TaikoTypedTransaction { + fn set_chain_id(&mut self, chain_id: ChainId) { + match self { + Self::Legacy(tx) => tx.set_chain_id(chain_id), + Self::Eip2930(tx) => tx.set_chain_id(chain_id), + Self::Eip1559(tx) => tx.set_chain_id(chain_id), + Self::Eip7702(tx) => tx.set_chain_id(chain_id), + } + } + + fn encode_for_signing(&self, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.encode_for_signing(out), + Self::Eip2930(tx) => tx.encode_for_signing(out), + Self::Eip1559(tx) => tx.encode_for_signing(out), + Self::Eip7702(tx) => tx.encode_for_signing(out), + } + } + + fn payload_len_for_signature(&self) -> usize { + match self { + Self::Legacy(tx) => tx.payload_len_for_signature(), + Self::Eip2930(tx) => tx.payload_len_for_signature(), + Self::Eip1559(tx) => tx.payload_len_for_signature(), + Self::Eip7702(tx) => tx.payload_len_for_signature(), + } + } + + fn into_signed(self, signature: Signature) -> Signed + where + Self: Sized, + { + let hash = self.tx_hash(&signature); + Signed::new_unchecked(self, signature, hash) + } +} + +#[cfg(feature = "serde")] +mod serde_from { + //! NB: Why do we need this? + //! + //! Because the tag may be missing, we need an abstraction over tagged (with + //! type) and untagged (always legacy). This is + //! [`MaybeTaggedTypedTransaction`]. + //! + //! The tagged variant is [`TaggedTypedTransaction`], which always has a + //! type tag. + //! + //! We serialize via [`TaggedTypedTransaction`] and deserialize via + //! [`MaybeTaggedTypedTransaction`]. + use super::*; + + /// Internal representation that handles both tagged and untagged transaction formats during + /// deserialization. + #[derive(Debug, serde::Deserialize)] + #[serde(untagged)] + pub(crate) enum MaybeTaggedTypedTransaction { + /// Transaction with explicit type tag. + Tagged(TaggedTypedTransaction), + /// Legacy transaction without type tag. + Untagged(TxLegacy), + } + + /// Transaction types with explicit type tags for serde serialization. + #[derive(Debug, serde::Serialize, serde::Deserialize)] + #[serde(tag = "type")] + pub(crate) enum TaggedTypedTransaction { + /// Legacy transaction + #[serde(rename = "0x00", alias = "0x0")] + Legacy(TxLegacy), + /// EIP-2930 transaction + #[serde(rename = "0x01", alias = "0x1")] + Eip2930(TxEip2930), + /// EIP-1559 transaction + #[serde(rename = "0x02", alias = "0x2")] + Eip1559(TxEip1559), + /// EIP-7702 transaction + #[serde(rename = "0x04", alias = "0x4")] + Eip7702(TxEip7702), + } + + impl From for TaikoTypedTransaction { + fn from(value: MaybeTaggedTypedTransaction) -> Self { + match value { + MaybeTaggedTypedTransaction::Tagged(tagged) => tagged.into(), + MaybeTaggedTypedTransaction::Untagged(tx) => Self::Legacy(tx), + } + } + } + + impl From for TaikoTypedTransaction { + fn from(value: TaggedTypedTransaction) -> Self { + match value { + TaggedTypedTransaction::Legacy(signed) => Self::Legacy(signed), + TaggedTypedTransaction::Eip2930(signed) => Self::Eip2930(signed), + TaggedTypedTransaction::Eip1559(signed) => Self::Eip1559(signed), + TaggedTypedTransaction::Eip7702(signed) => Self::Eip7702(signed), + } + } + } + + impl From for TaggedTypedTransaction { + fn from(value: TaikoTypedTransaction) -> Self { + match value { + TaikoTypedTransaction::Legacy(signed) => Self::Legacy(signed), + TaikoTypedTransaction::Eip2930(signed) => Self::Eip2930(signed), + TaikoTypedTransaction::Eip1559(signed) => Self::Eip1559(signed), + TaikoTypedTransaction::Eip7702(signed) => Self::Eip7702(signed), + } + } + } +} diff --git a/crates/consensus/src/validation.rs b/crates/consensus/src/validation.rs index 8531d398..e27af7a4 100644 --- a/crates/consensus/src/validation.rs +++ b/crates/consensus/src/validation.rs @@ -12,11 +12,10 @@ use reth_consensus_common::validation::{ validate_header_extra_data, validate_header_gas, }; use reth_ethereum_consensus::validate_block_post_execution; -use reth_execution_types::BlockExecutionResult; -use reth_primitives::SealedBlock; +use reth_evm::block::BlockExecutionResult; use reth_primitives_traits::{ - Block, BlockBody, BlockHeader, GotExpected, NodePrimitives, RecoveredBlock, SealedHeader, - SignedTransaction, + Block, BlockBody, BlockHeader, GotExpected, NodePrimitives, RecoveredBlock, SealedBlock, + SealedHeader, SignedTransaction, }; use crate::eip4396::{ @@ -53,6 +52,16 @@ pub trait TaikoBlockReader: Send + Sync + Debug { fn block_timestamp_by_hash(&self, hash: B256) -> Option; } +#[derive(Debug, Clone, Default)] +/// Noop Taiko block reader implementation that returns `None` for all queries. +pub struct NoopTaikoBlockReader; + +impl TaikoBlockReader for NoopTaikoBlockReader { + fn block_timestamp_by_hash(&self, _hash: B256) -> Option { + None + } +} + /// Taiko consensus implementation. /// /// Provides basic checks as outlined in the execution specs. @@ -69,6 +78,11 @@ impl TaikoBeaconConsensus { pub fn new(chain_spec: Arc, block_reader: Arc) -> Self { Self { chain_spec, block_reader } } + + /// Create a new instance of [`TaikoBeaconConsensus`] with a noop block reader. + pub fn new_with_noop_block_reader(chain_spec: Arc) -> Self { + Self { chain_spec, block_reader: Arc::new(NoopTaikoBlockReader) } + } } impl FullConsensus for TaikoBeaconConsensus @@ -154,7 +168,18 @@ where validate_header_extra_data(header, MAXIMUM_EXTRA_DATA_SIZE)?; validate_header_gas(header)?; - validate_header_base_fee(header, &self.chain_spec) + validate_header_base_fee(header, &self.chain_spec)?; + + // Ensures that Cancun related fields are still not set in Taiko + if header.blob_gas_used().is_some() { + return Err(ConsensusError::BlobGasUsedUnexpected); + } else if header.excess_blob_gas().is_some() { + return Err(ConsensusError::ExcessBlobGasUnexpected); + } else if header.parent_beacon_block_root().is_some() { + return Err(ConsensusError::ParentBeaconBlockRootUnexpected); + } + + Ok(()) } /// Validate that the header information regarding parent are correct. diff --git a/crates/db/Cargo.toml b/crates/db/Cargo.toml index 59f7465a..92e878a3 100644 --- a/crates/db/Cargo.toml +++ b/crates/db/Cargo.toml @@ -15,9 +15,12 @@ default = [] alethia-reth-primitives = { path = "../primitives", features = ["serde"] } alloy-primitives = { workspace = true } alloy-rlp = { workspace = true } -reth = { workspace = true } +reth-revm = { workspace = true } reth-codecs = { workspace = true } reth-db = { workspace = true } reth-db-api = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_with = { workspace = true } + +[dev-dependencies] +alloy-primitives = { workspace = true, features = ["rand"] } diff --git a/crates/db/src/model.rs b/crates/db/src/model.rs index a222f7e3..639fcf2f 100644 --- a/crates/db/src/model.rs +++ b/crates/db/src/model.rs @@ -1,11 +1,8 @@ use core::fmt; use alloy_primitives::BlockNumber; -use reth::revm::primitives::{ - B256, U256, - alloy_primitives::{self}, -}; use reth_db_api::{TableSet, TableType, TableViewer, table::TableInfo, tables}; +use reth_revm::primitives::{B256, U256}; use serde::{Deserialize, Serialize}; use serde_with::{Bytes, serde_as}; diff --git a/crates/evm/Cargo.toml b/crates/evm/Cargo.toml index 859cf193..aa81af4f 100644 --- a/crates/evm/Cargo.toml +++ b/crates/evm/Cargo.toml @@ -20,3 +20,6 @@ reth-revm = { workspace = true } revm-database-interface = { workspace = true } serde = { workspace = true, optional = true } tracing = { workspace = true } + +[dev-dependencies] +alloy-primitives = { workspace = true, features = ["rand", "std"] } diff --git a/crates/evm/src/alloy.rs b/crates/evm/src/alloy.rs index 6b9912d6..d3178687 100644 --- a/crates/evm/src/alloy.rs +++ b/crates/evm/src/alloy.rs @@ -9,7 +9,7 @@ use alloy_primitives::hex; use reth_revm::{ Context, ExecuteEvm, InspectEvm, Inspector, context::{ - BlockEnv, CfgEnv, TxEnv, + BlockEnv, TxEnv, result::{EVMError, ExecutionResult, HaltReason, Output, ResultAndState, SuccessReason}, }, handler::PrecompileProvider, @@ -18,7 +18,9 @@ use reth_revm::{ }; use tracing::debug; -use crate::{evm::TaikoEvm, handler::get_treasury_address, spec::TaikoSpecId}; +use crate::{ + context::TaikoEvmContext, evm::TaikoEvm, handler::get_treasury_address, spec::TaikoSpecId, +}; /// System caller address used for Taiko anchor system-call pre-execution. pub const TAIKO_GOLDEN_TOUCH_ADDRESS: [u8; 20] = hex!("0x0000777735367b36bc9b61c50022d9d0700db4ec"); @@ -69,9 +71,6 @@ impl DerefMut for TaikoEvmWrapper { } } -/// Canonical Taiko EVM context type used by the Alloy adapter. -pub type TaikoEvmContext = Context, DB>; - /// An instance of an ethereum virtual machine. /// /// An EVM is commonly initialized with the corresponding block context and state and it's only diff --git a/crates/evm/src/context.rs b/crates/evm/src/context.rs new file mode 100644 index 00000000..981f4121 --- /dev/null +++ b/crates/evm/src/context.rs @@ -0,0 +1,90 @@ +//! Taiko EVM context types and builder traits. +use reth_revm::{ + Context, Database, + context::{BlockEnv, CfgEnv, Evm, FrameStack, TxEnv}, + handler::EthPrecompiles, + primitives::hardfork::SpecId, +}; +use revm_database_interface::EmptyDB; + +use crate::{evm::TaikoEvm, handler::instructions::TaikoInstructions, spec::TaikoSpecId}; + +/// Canonical Taiko EVM context type used by the Alloy adapter. +pub type TaikoEvmContext = Context, DB>; + +/// Trait used to initialize Context with default taiko_mainnet types. +pub trait TaikoContext { + /// Creates a new taiko_mainnet context with default configuration. + fn taiko_mainnet() -> Self; +} + +impl TaikoContext for TaikoEvmContext { + fn taiko_mainnet() -> Self { + Context::new(EmptyDB::new(), SpecId::default()) + .with_cfg(CfgEnv::new_with_spec(TaikoSpecId::SHASTA)) + } +} + +/// Builder trait for constructing [`TaikoEvm`] instances from a context. +pub trait TaikoEvmBuilder: Sized { + /// The context type that will be used in the EVM. + type Context; + + /// Builds a mainnet EVM instance without an inspector. + fn build_taiko_mainnet(self) -> TaikoEvm; + + /// Builds a mainnet EVM instance with the provided inspector. + fn build_taiko_mainnet_with_inspector( + self, + inspector: INSP, + ) -> TaikoEvm; +} + +impl TaikoEvmBuilder for TaikoEvmContext +where + DB: Database, +{ + type Context = Self; + + fn build_taiko_mainnet(self) -> TaikoEvm { + let spec = self.cfg.spec().clone(); + TaikoEvm::new(Evm { + ctx: self, + inspector: (), + instruction: TaikoInstructions::new_taiko_mainnet(spec), + precompiles: EthPrecompiles::default(), + frame_stack: FrameStack::new(), + }) + } + + fn build_taiko_mainnet_with_inspector( + self, + inspector: INSP, + ) -> TaikoEvm { + let spec = self.cfg.spec().clone(); + TaikoEvm::new(Evm { + ctx: self, + inspector, + instruction: TaikoInstructions::new_taiko_mainnet(spec), + precompiles: EthPrecompiles::default(), + frame_stack: FrameStack::new(), + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use reth_revm::{ExecuteEvm, InspectEvm, inspector::NoOpInspector}; + + #[test] + fn default_run_taiko() { + let ctx = Context::taiko_mainnet(); + // convert to taiko_mainnet context + let mut evm = ctx.build_taiko_mainnet_with_inspector(NoOpInspector {}); + // execute + let _ = evm.inner.transact(TxEnv::builder().build_fill()); + // inspect + let _ = evm.inner.inspect_one_tx(TxEnv::builder().build_fill()); + } +} diff --git a/crates/evm/src/evm.rs b/crates/evm/src/evm.rs index 519efb72..f5e1b9f1 100644 --- a/crates/evm/src/evm.rs +++ b/crates/evm/src/evm.rs @@ -2,21 +2,20 @@ use alloy_primitives::Address; use reth_revm::{ context::{ContextError, ContextTr, Evm as RevmEvm, FrameStack}, - handler::{ - EthFrame, EvmTr, FrameInitOrResult, FrameTr, ItemOrResult, PrecompileProvider, - instructions::EthInstructions, - }, + handler::{EthFrame, EvmTr, FrameInitOrResult, FrameTr, ItemOrResult, PrecompileProvider}, interpreter::{InterpreterResult, interpreter::EthInterpreter}, }; use revm_database_interface::Database; +use crate::handler::instructions::TaikoInstructions; + /// Custom EVM for Taiko, we extend the RevmEvm with /// [`TaikoEvmExtraContext`] to provide additional context /// for Anchor transaction pre-execution checks and base fee sharing. pub struct TaikoEvm { /// Inner revm engine with Taiko instruction wiring. pub inner: - RevmEvm, P, EthFrame>, + RevmEvm, P, EthFrame>, /// Optional per-block context captured from pre-executed anchor system calls. pub extra_execution_ctx: Option, } @@ -27,7 +26,7 @@ impl TaikoEvm { inner: RevmEvm< CTX, INSP, - EthInstructions, + TaikoInstructions, P, EthFrame, >, @@ -49,6 +48,27 @@ impl TaikoEvm { anchor_caller_nonce, }); } + + /// Consumed self and returns new Evm type with given Inspector. + pub fn with_inspector(self, inspector: OINSP) -> TaikoEvm { + TaikoEvm { + inner: self.inner.with_inspector(inspector), + extra_execution_ctx: self.extra_execution_ctx, + } + } + + /// Consumes self and returns new Evm type with given Precompiles. + pub fn with_precompiles(self, precompiles: OP) -> TaikoEvm { + TaikoEvm { + inner: self.inner.with_precompiles(precompiles), + extra_execution_ctx: self.extra_execution_ctx, + } + } + + /// Consumes self and returns inner Inspector. + pub fn into_inspector(self) -> INSP { + self.inner.into_inspector() + } } /// A trait that integrates context, instruction set, and precompiles to create an EVM struct, @@ -61,7 +81,7 @@ where /// The context type that implements ContextTr to provide access to execution state type Context = CTX; /// The instruction set type that implements InstructionProvider to define available operations - type Instructions = EthInstructions; + type Instructions = TaikoInstructions; /// The type containing the available precompiled contracts type Precompiles = P; type Frame = EthFrame; @@ -192,12 +212,12 @@ impl TaikoEvmExtraExecutionCtx { #[cfg(test)] mod test { use alloy_primitives::{U64, U256}; - use reth_revm::{ - Context, ExecuteEvm, MainBuilder, MainContext, context::TxEnv, db::InMemoryDB, - state::AccountInfo, - }; + use reth_revm::{Context, ExecuteEvm, context::TxEnv, db::InMemoryDB, state::AccountInfo}; - use crate::alloy::TAIKO_GOLDEN_TOUCH_ADDRESS; + use crate::{ + alloy::TAIKO_GOLDEN_TOUCH_ADDRESS, + context::{TaikoContext, TaikoEvmBuilder}, + }; use super::*; @@ -211,7 +231,7 @@ mod test { AccountInfo { nonce, balance: U256::from(0), ..Default::default() }, ); - let mut taiko_evm = TaikoEvm::new(Context::mainnet().with_db(db).build_mainnet()); + let mut taiko_evm = Context::taiko_mainnet().with_db(db).build_taiko_mainnet(); let mut state = taiko_evm.transact_one( TxEnv::builder() diff --git a/crates/evm/src/factory.rs b/crates/evm/src/factory.rs index c71a1e96..148c3774 100644 --- a/crates/evm/src/factory.rs +++ b/crates/evm/src/factory.rs @@ -1,7 +1,7 @@ use alloy_evm::{Database, EvmEnv, EvmFactory}; use reth_evm::precompiles::PrecompilesMap; use reth_revm::{ - Context, Inspector, MainBuilder, MainContext, + Inspector, context::{ BlockEnv, TxEnv, result::{EVMError, HaltReason}, @@ -12,8 +12,8 @@ use reth_revm::{ }; use crate::{ - alloy::{TaikoEvmContext, TaikoEvmWrapper}, - evm::TaikoEvm, + alloy::TaikoEvmWrapper, + context::{TaikoContext, TaikoEvmBuilder, TaikoEvmContext}, spec::TaikoSpecId, }; @@ -47,16 +47,16 @@ impl EvmFactory for TaikoEvmFactory { input: EvmEnv, ) -> Self::Evm { let spec_id = input.cfg_env.spec; - let evm = Context::mainnet() + let evm = TaikoEvmContext::taiko_mainnet() .with_cfg(input.cfg_env) .with_block(input.block_env) .with_db(db) - .build_mainnet_with_inspector(NoOpInspector {}) + .build_taiko_mainnet_with_inspector(NoOpInspector {}) .with_precompiles(PrecompilesMap::from_static(Precompiles::new( PrecompileSpecId::from_spec_id(spec_id.into()), ))); - TaikoEvmWrapper::new(TaikoEvm::new(evm), false) + TaikoEvmWrapper::new(evm, false) } /// Creates a new instance of an EVM with an inspector. @@ -67,16 +67,16 @@ impl EvmFactory for TaikoEvmFactory { inspector: I, ) -> Self::Evm { let spec_id = input.cfg_env.spec; - let evm = Context::mainnet() + let evm = TaikoEvmContext::taiko_mainnet() .with_cfg(input.cfg_env) .with_block(input.block_env) .with_db(db) - .build_mainnet_with_inspector(NoOpInspector {}) + .build_taiko_mainnet_with_inspector(NoOpInspector {}) .with_precompiles(PrecompilesMap::from_static(Precompiles::new( PrecompileSpecId::from_spec_id(spec_id.into()), ))) .with_inspector(inspector); - TaikoEvmWrapper::new(TaikoEvm::new(evm), true) + TaikoEvmWrapper::new(evm, true) } } diff --git a/crates/evm/src/handler/instructions.rs b/crates/evm/src/handler/instructions.rs new file mode 100644 index 00000000..4b51d75a --- /dev/null +++ b/crates/evm/src/handler/instructions.rs @@ -0,0 +1,91 @@ +//! Taiko-customised instruction table that replaces blob-related opcodes. +use reth_revm::{ + bytecode::opcode::{BLOBBASEFEE, BLOBHASH}, + context::Host, + handler::instructions::{EthInstructions, InstructionProvider}, + interpreter::{ + Instruction, InstructionContext, InstructionTable, InterpreterTypes, + instructions::instruction_table_gas_changes_spec, + }, + primitives::hardfork::SpecId, +}; + +use crate::spec::TaikoSpecId; + +/// Taiko instruction contains list of mainnet instructions that is used for Interpreter +/// execution, contains custom Taiko instructions as well. +#[derive(Debug)] +pub struct TaikoInstructions { + /// Table containing instruction implementations indexed by opcode. + inner: EthInstructions, +} + +impl Clone for TaikoInstructions +where + WIRE: InterpreterTypes, +{ + fn clone(&self) -> Self { + Self { inner: self.inner.clone() } + } +} + +impl TaikoInstructions +where + WIRE: InterpreterTypes, + HOST: Host, +{ + /// Returns `TaikoInstructions` with mainnet spec. + /// This function also customizes the instruction table by replacing + /// the BLOBBASEFEE instruction with Taiko's custom implementation. + pub fn new_taiko_mainnet(spec: TaikoSpecId) -> Self { + let mut table = instruction_table_gas_changes_spec::(spec.into()); + + table[BLOBBASEFEE as usize] = Instruction::new(blob_basefee, 2); + table[BLOBHASH as usize] = Instruction::new(blob_hash, 3); + + Self::new(table, spec.into()) + } + + /// Returns a new instance of `TaikoInstructions` with custom instruction table. + #[inline] + pub fn new(base_table: InstructionTable, spec: SpecId) -> Self { + Self { inner: EthInstructions::new(base_table, spec) } + } + + /// Inserts a new instruction into the instruction table. + #[inline] + pub fn insert_instruction(&mut self, opcode: u8, instruction: Instruction) { + self.inner.insert_instruction(opcode, instruction); + } +} + +impl InstructionProvider for TaikoInstructions +where + IT: InterpreterTypes, + CTX: Host, +{ + type InterpreterTypes = IT; + type Context = CTX; + + fn instruction_table(&self) -> &InstructionTable { + self.inner.instruction_table() + } +} + +/// Custom implementation of BLOBBASEFEE instruction for Taiko EVM. +/// In Taiko, the BLOBBASEFEE instruction is not activated, +/// so it halts the interpreter when executed. +pub fn blob_basefee( + context: InstructionContext<'_, H, WIRE>, +) { + context.interpreter.halt_not_activated(); +} + +/// Custom implementation of BLOBHASH instruction for Taiko EVM. +/// In Taiko, the BLOBHASH instruction is not activated, +/// so it halts the interpreter when executed. +pub fn blob_hash( + context: InstructionContext<'_, H, WIRE>, +) { + context.interpreter.halt_not_activated(); +} diff --git a/crates/evm/src/handler.rs b/crates/evm/src/handler/mod.rs similarity index 91% rename from crates/evm/src/handler.rs rename to crates/evm/src/handler/mod.rs index 74d6b885..c5de4957 100644 --- a/crates/evm/src/handler.rs +++ b/crates/evm/src/handler/mod.rs @@ -5,25 +5,29 @@ use reth_revm::{ Database, Inspector, context::{ Block, Cfg, ContextTr, JournalTr, Transaction, - result::{HaltReason, InvalidTransaction}, + result::{HaltReason, InvalidHeader, InvalidTransaction}, }, context_interface::journaled_state::account::JournaledAccountTr, handler::{ EthFrame, EvmTr, EvmTrError, FrameResult, FrameTr, Handler, PrecompileProvider, instructions::InstructionProvider, pre_execution::validate_account_nonce_and_code_with_components, + validation::validate_tx_env, }, inspector::{InspectorEvmTr, InspectorHandler}, interpreter::{ Gas, InterpreterResult, interpreter::EthInterpreter, interpreter_action::FrameInit, }, - primitives::{Address, U256}, + primitives::{Address, U256, hardfork::SpecId}, state::EvmState, }; use tracing::debug; use crate::evm::TaikoEvmExtraExecutionCtx; +/// Taiko-customised instruction table that replaces blob-related opcodes. +pub mod instructions; + /// Handler for Taiko EVM, it implements the `Handler` trait /// and provides methods to handle the execution of transactions and the /// reward for the beneficiary. @@ -105,6 +109,17 @@ where ) -> Result<(), Self::Error> { validate_against_state_and_deduct_caller(evm.ctx(), self.extra_execution_ctx.clone()) } + + /// Validates block, transaction and configuration fields. + /// + /// Performs all validation checks that can be done without loading state. + /// For example, verifies transaction gas limit is below block gas limit. + /// + /// Note: In Taiko blob_excess_gas_and_price is ignored. + #[inline] + fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + validate_env(evm.ctx()) + } } /// Trait that extends [`Handler`] with inspection functionality, here we just use the default @@ -319,6 +334,21 @@ pub fn reimburse_caller( Ok(()) } +/// Validates the execution environment including block and transaction parameters. +pub fn validate_env + From>( + context: CTX, +) -> Result<(), ERROR> { + let spec = context.cfg().spec().into(); + // `prevrandao` is required for the merge + if spec.is_enabled_in(SpecId::MERGE) && context.block().prevrandao().is_none() { + return Err(InvalidHeader::PrevrandaoNotSet.into()); + } + + // Note: `excess_blob_gas` is ignored in Taiko + + validate_tx_env::(context, spec).map_err(Into::into) +} + /// Generates the network treasury address based on the chain ID. #[inline] pub fn get_treasury_address(chain_id: u64) -> Address { diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index 849838b2..29832904 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -3,6 +3,8 @@ //! Taiko EVM extensions, handlers, and fork-spec adapters. /// Alloy-facing EVM wrapper and helpers. pub mod alloy; +/// Taiko EVM context types and builder traits. +pub mod context; #[allow(clippy::module_inception)] /// Core Taiko EVM type extensions over `reth-revm`. pub mod evm; diff --git a/crates/evm/src/spec.rs b/crates/evm/src/spec.rs index 0f6b06ab..1e9043e2 100644 --- a/crates/evm/src/spec.rs +++ b/crates/evm/src/spec.rs @@ -23,7 +23,7 @@ impl TaikoSpecId { /// Converts the [`TaikoSpecId`] into a [`SpecId`]. pub const fn into_eth_spec(self) -> SpecId { match self { - Self::GENESIS | Self::ONTAKE | Self::PACAYA | Self::SHASTA => SpecId::SHANGHAI, + Self::GENESIS | Self::ONTAKE | Self::PACAYA | Self::SHASTA => SpecId::CANCUN, } } @@ -91,7 +91,7 @@ mod tests { vec![ (SpecId::MERGE, true), (SpecId::SHANGHAI, true), - (SpecId::CANCUN, false), + (SpecId::CANCUN, true), (SpecId::default(), false), ], vec![ diff --git a/crates/network/Cargo.toml b/crates/network/Cargo.toml deleted file mode 100644 index b36071b7..00000000 --- a/crates/network/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "alethia-reth-network" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true - -[lib] -path = "src/lib.rs" - -[features] -default = [] - -[dependencies] -alethia-reth-chainspec = { path = "../chainspec" } -eyre = { workspace = true } -reth = { workspace = true } -reth-ethereum = { workspace = true } -reth-node-api = { workspace = true } -reth-node-builder = { workspace = true } -tracing = { workspace = true } diff --git a/crates/node-builder/Cargo.toml b/crates/node-builder/Cargo.toml index 2e36c789..3ec5d3fa 100644 --- a/crates/node-builder/Cargo.toml +++ b/crates/node-builder/Cargo.toml @@ -11,12 +11,19 @@ path = "src/lib.rs" [dependencies] alethia-reth-block = { path = "../block" } alethia-reth-chainspec = { path = "../chainspec" } -alethia-reth-consensus = { path = "../consensus" } +alethia-reth-consensus = { path = "../consensus", features = ["alloy-compat"] } alethia-reth-primitives = { path = "../primitives" } +alethia-reth-payload = { path = "../payload" } +alethia-reth-rpc = { path = "../rpc" } alloy-primitives = { workspace = true } eyre = { workspace = true } reth-ethereum = { workspace = true } +reth-rpc = { workspace = true } +reth-node-ethereum = { workspace = true } reth-node-api = { workspace = true } reth-node-builder = { workspace = true } +reth-network = { workspace = true } reth-primitives-traits = { workspace = true } reth-provider = { workspace = true } +reth-transaction-pool = { workspace = true } +tracing = { workspace = true } diff --git a/crates/node-builder/src/consensus.rs b/crates/node-builder/src/consensus.rs index 30897a48..125f346e 100644 --- a/crates/node-builder/src/consensus.rs +++ b/crates/node-builder/src/consensus.rs @@ -2,9 +2,8 @@ use std::{fmt::Debug, sync::Arc}; use alethia_reth_chainspec::spec::TaikoChainSpec; use alethia_reth_consensus::validation::{TaikoBeaconConsensus, TaikoBlockReader}; -use alethia_reth_primitives::engine::TaikoEngineTypes; +use alethia_reth_primitives::{TaikoPrimitives, engine::TaikoEngineTypes}; use alloy_primitives::B256; -use reth_ethereum::EthPrimitives; use reth_node_api::{FullNodeTypes, NodeTypes}; use reth_node_builder::{BuilderContext, components::ConsensusBuilder}; use reth_primitives_traits::{AlloyBlockHeader, Block}; @@ -32,7 +31,7 @@ impl ConsensusBuilder for TaikoConsensusBuilder where Node: FullNodeTypes< Types: NodeTypes< - Primitives = EthPrimitives, + Primitives = TaikoPrimitives, ChainSpec = TaikoChainSpec, Payload = TaikoEngineTypes, >, diff --git a/crates/rpc/src/eth/builder.rs b/crates/node-builder/src/eth_api.rs similarity index 64% rename from crates/rpc/src/eth/builder.rs rename to crates/node-builder/src/eth_api.rs index ce738d50..4d40fc8f 100644 --- a/crates/rpc/src/eth/builder.rs +++ b/crates/node-builder/src/eth_api.rs @@ -1,29 +1,25 @@ use alethia_reth_block::config::TaikoEvmConfig; use alethia_reth_chainspec::spec::TaikoChainSpec; -use alethia_reth_primitives::engine::TaikoEngineTypes; - -use crate::eth::types::TaikoEthApi; -use reth_ethereum::EthPrimitives; +use alethia_reth_primitives::{TaikoPrimitives, engine::TaikoEngineTypes}; +use alethia_reth_rpc::{converter::TaikoRpcConverter, eth::types::TaikoEthApi}; use reth_node_api::{FullNodeComponents, NodeTypes}; use reth_node_builder::rpc::{EthApiBuilder, EthApiCtx}; -use reth_node_ethereum::EthereumEthApiBuilder; -use reth_rpc::eth::core::EthRpcConverterFor; /// Builds [`TaikoEthApi`] for the Taiko node. -#[derive(Debug, Default)] -pub struct TaikoEthApiBuilder(EthereumEthApiBuilder); +#[derive(Debug, Default, Clone)] +pub struct TaikoEthApiBuilder; impl EthApiBuilder for TaikoEthApiBuilder where N: FullNodeComponents, N::Types: NodeTypes< - Primitives = EthPrimitives, + Primitives = TaikoPrimitives, ChainSpec = TaikoChainSpec, Payload = TaikoEngineTypes, >, { /// The Ethapi implementation this builder will build. - type EthApi = TaikoEthApi>; + type EthApi = TaikoEthApi>; /// Builds the [`TaikoEthApi`] from the given context. async fn build_eth_api(self, ctx: EthApiCtx<'_, N>) -> eyre::Result { diff --git a/crates/node-builder/src/executor.rs b/crates/node-builder/src/executor.rs index 155a2af6..3d973884 100644 --- a/crates/node-builder/src/executor.rs +++ b/crates/node-builder/src/executor.rs @@ -2,8 +2,7 @@ use std::future; use alethia_reth_block::config::TaikoEvmConfig; use alethia_reth_chainspec::spec::TaikoChainSpec; -use alethia_reth_primitives::engine::TaikoEngineTypes; -use reth_ethereum::EthPrimitives; +use alethia_reth_primitives::{TaikoPrimitives, engine::TaikoEngineTypes}; use reth_node_api::{FullNodeTypes, NodeTypes}; use reth_node_builder::{BuilderContext, components::ExecutorBuilder}; @@ -14,7 +13,7 @@ pub struct TaikoExecutorBuilder; impl ExecutorBuilder for TaikoExecutorBuilder where Types: NodeTypes< - Primitives = EthPrimitives, + Primitives = TaikoPrimitives, ChainSpec = TaikoChainSpec, Payload = TaikoEngineTypes, >, diff --git a/crates/node-builder/src/lib.rs b/crates/node-builder/src/lib.rs index 81eefc5f..c90d47bb 100644 --- a/crates/node-builder/src/lib.rs +++ b/crates/node-builder/src/lib.rs @@ -1,12 +1,25 @@ #![cfg_attr(not(test), deny(missing_docs, clippy::missing_docs_in_private_items))] #![cfg_attr(test, allow(missing_docs, clippy::missing_docs_in_private_items))] //! Node component builders for Taiko consensus and executors. + /// Taiko consensus builder integration for node composition. mod consensus; -/// Taiko executor builder integration for node composition. + +/// Taiko `eth` API builder and method implementations. +mod eth_api; + +/// Taiko block execution and validation builder implementations. mod executor; +/// Network builder for Taiko P2P integration with `reth` node services. +mod network; + +/// Taiko payload builder integration with `reth` node services. +mod payload_builder; + /// Provider-backed block reader and consensus builder exports. pub use consensus::{ProviderTaikoBlockReader, TaikoConsensusBuilder}; -/// Taiko executor builder export. +pub use eth_api::TaikoEthApiBuilder; pub use executor::TaikoExecutorBuilder; +pub use network::TaikoNetworkBuilder; +pub use payload_builder::TaikoPayloadBuilderBuilder; diff --git a/crates/network/src/lib.rs b/crates/node-builder/src/network.rs similarity index 63% rename from crates/network/src/lib.rs rename to crates/node-builder/src/network.rs index d20e5a3a..2774eccc 100644 --- a/crates/network/src/lib.rs +++ b/crates/node-builder/src/network.rs @@ -1,34 +1,27 @@ #![cfg_attr(not(test), deny(missing_docs, clippy::missing_docs_in_private_items))] #![cfg_attr(test, allow(missing_docs, clippy::missing_docs_in_private_items))] -//! Taiko network builder wiring for `reth` node composition. -use reth::{ - network::{EthNetworkPrimitives, NetworkHandle, PeersInfo}, - transaction_pool::{PoolTransaction, TransactionPool}, -}; -use reth_ethereum::{EthPrimitives, PooledTransactionVariant}; -use reth_node_api::{FullNodeTypes, NodeTypes, TxTy}; +use alethia_reth_chainspec::spec::TaikoChainSpec; +use alethia_reth_primitives::TaikoPrimitives; +use reth_network::{NetworkHandle, PeersInfo, types::BasicNetworkPrimitives}; +use reth_node_api::{FullNodeTypes, NodeTypes, PrimitivesTy, TxTy}; use reth_node_builder::{BuilderContext, components::NetworkBuilder}; +use reth_transaction_pool::{PoolPooledTx, PoolTransaction, TransactionPool}; use tracing::info; -use alethia_reth_chainspec::spec::TaikoChainSpec; - /// A basic Taiko network builder service. #[derive(Debug, Default, Clone, Copy)] pub struct TaikoNetworkBuilder; impl NetworkBuilder for TaikoNetworkBuilder where - Node: FullNodeTypes>, - Pool: TransactionPool< - Transaction: PoolTransaction< - Consensus = TxTy, - Pooled = PooledTransactionVariant, - >, - > + Unpin + Node: FullNodeTypes>, + Pool: TransactionPool>> + + Unpin + 'static, { /// The network built. - type Network = NetworkHandle; + type Network = + NetworkHandle, PoolPooledTx>>; /// Launches the network implementation and returns the handle to it. async fn build_network( diff --git a/crates/node-builder/src/payload_builder.rs b/crates/node-builder/src/payload_builder.rs new file mode 100644 index 00000000..0402e5d0 --- /dev/null +++ b/crates/node-builder/src/payload_builder.rs @@ -0,0 +1,41 @@ +use alethia_reth_block::config::TaikoEvmConfig; +use alethia_reth_chainspec::spec::TaikoChainSpec; +use alethia_reth_consensus::transaction::TaikoTxEnvelope; +use alethia_reth_payload::TaikoPayloadBuilder; +use alethia_reth_primitives::{TaikoPrimitives, engine::TaikoEngineTypes}; +use reth_node_api::{FullNodeTypes, NodeTypes}; +use reth_node_builder::{BuilderContext, components::PayloadBuilderBuilder}; +use reth_transaction_pool::{PoolTransaction, TransactionPool}; + +/// The builder to spawn [`TaikoPayloadBuilder`] payload building tasks. +#[derive(Debug, Default, Clone)] +pub struct TaikoPayloadBuilderBuilder; + +impl PayloadBuilderBuilder for TaikoPayloadBuilderBuilder +where + Node: FullNodeTypes< + Types: NodeTypes< + Primitives = TaikoPrimitives, + ChainSpec = TaikoChainSpec, + Payload = TaikoEngineTypes, + >, + >, + Pool: TransactionPool> + + Unpin + + 'static, +{ + /// Payload builder implementation. + type PayloadBuilder = TaikoPayloadBuilder; + + /// Spawns the payload service and returns the handle to it. + /// + /// The [`BuilderContext`] is provided to allow access to the node's configuration. + async fn build_payload_builder( + self, + ctx: &BuilderContext, + pool: Pool, + evm_config: TaikoEvmConfig, + ) -> eyre::Result { + Ok(TaikoPayloadBuilder::new(ctx.provider().clone(), pool, evm_config)) + } +} diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 7a3c44af..8ee654db 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -13,28 +13,50 @@ default = ["serde"] arbitrary = [] client = [] prover = [] -serde = ["dep:serde", "alethia-reth-chainspec/serde", "alethia-reth-primitives/serde"] +serde = [ + "dep:serde", + "alethia-reth-chainspec/serde", + "alethia-reth-primitives/serde", +] [dependencies] alethia-reth-block = { path = "../block" } alethia-reth-chainspec = { path = "../chainspec" } -alethia-reth-consensus = { path = "../consensus" } +alethia-reth-consensus = { path = "../consensus", features = ["alloy-compat"] } alethia-reth-db = { path = "../db" } alethia-reth-evm = { path = "../evm" } -alethia-reth-network = { path = "../network" } alethia-reth-node-builder = { path = "../node-builder" } alethia-reth-payload = { path = "../payload" } -alethia-reth-primitives = { path = "../primitives", features = ["serde"] } +alethia-reth-primitives = { path = "../primitives", features = [ + "serde", + "local-payload-builder", +] } alethia-reth-rpc = { path = "../rpc" } +alloy-consensus = { workspace = true } +alloy-eips = { workspace = true } +alloy-primitives = { workspace = true } alloy-rpc-types-eth = { workspace = true } eyre = { workspace = true } -reth = { workspace = true } +reth-storage-api = { workspace = true, features = ["db-api"] } +reth-db-api = { workspace = true } +reth-primitives = { workspace = true } +reth-primitives-traits = { workspace = true } reth-engine-local = { workspace = true } +reth-transaction-pool = { workspace = true } reth-engine-primitives = { workspace = true } +reth-evm = { workspace = true } reth-ethereum = { workspace = true } reth-ethereum-primitives = { workspace = true } reth-node-api = { workspace = true } reth-node-builder = { workspace = true } reth-node-ethereum = { workspace = true } +reth-provider = { workspace = true } +reth-chainspec = { workspace = true } +reth-network = { workspace = true } reth-rpc = { workspace = true } + +tracing = { workspace = true } serde = { workspace = true, optional = true } + +[dev-dependencies] +reth-evm-ethereum = { workspace = true } diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 62c1d40d..5f47ffdb 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -4,58 +4,66 @@ pub use alethia_reth_block as block; pub use alethia_reth_chainspec as chainspec; pub use alethia_reth_consensus as consensus; +use alethia_reth_consensus::transaction::TaikoTxEnvelope; pub use alethia_reth_db as db; pub use alethia_reth_evm as evm; -pub use alethia_reth_network as network; pub use alethia_reth_node_builder as node_builder; pub use alethia_reth_payload as payload; pub use alethia_reth_primitives as primitives; +use alethia_reth_primitives::{TaikoBlock, TaikoPrimitives}; pub use alethia_reth_rpc as rpc; +/// Taiko transaction pool builder. +pub mod pool; +pub mod txpool; use block::config::TaikoEvmConfig; use chainspec::spec::TaikoChainSpec; -use network::TaikoNetworkBuilder; -use node_builder::{TaikoConsensusBuilder, TaikoExecutorBuilder}; -use payload::TaikoPayloadBuilderBuilder; use primitives::engine::TaikoEngineTypes; -use reth::{ - api::{FullNodeComponents, FullNodeTypes, NodeTypes}, - builder::{ - DebugNode, Node, - components::{BasicPayloadServiceBuilder, ComponentsBuilder}, - }, - providers::EthStorage, -}; use reth_engine_local::LocalPayloadAttributesBuilder; use reth_engine_primitives::{EngineApiValidator, PayloadValidator}; -use reth_ethereum::EthPrimitives; -use reth_node_api::{BlockTy, NodeAddOns, PayloadAttributesBuilder, PayloadTypes}; +use reth_node_api::{ + BlockTy, FullNodeComponents, FullNodeTypes, NodeAddOns, NodeTypes, PayloadAttributesBuilder, + PayloadTypes, +}; use reth_node_builder::{ - NodeAdapter, + DebugNode, Node, NodeAdapter, + components::{BasicPayloadServiceBuilder, ComponentsBuilder}, rpc::{ BasicEngineValidatorBuilder, EngineValidatorAddOn, PayloadValidatorBuilder, RethRpcAddOns, RpcAddOns, RpcHandle, RpcHooks, }, }; -use reth_node_ethereum::node::EthereumPoolBuilder; -use reth_rpc::eth::core::EthRpcConverterFor; +use reth_provider::EthStorage; use rpc::{ + converter::TaikoRpcConverter, engine::{builder::TaikoEngineApiBuilder, validator::TaikoEngineValidatorBuilder}, - eth::{builder::TaikoEthApiBuilder, types::TaikoEthApi}, + eth::types::TaikoEthApi, }; use std::sync::Arc; +use alethia_reth_node_builder::{ + TaikoConsensusBuilder, TaikoEthApiBuilder, TaikoExecutorBuilder, TaikoNetworkBuilder, + TaikoPayloadBuilderBuilder, +}; +use pool::TaikoPoolBuilder; + +/// Taiko storage type. +/// Since TaikoTxEnvelope implements Compact (for RLP encoding), we can reuse EthStorage +/// with our custom transaction and header types. +pub type TaikoStorage = + EthStorage; + /// The main node type for a Taiko network node, implementing the `NodeTypes` trait. #[derive(Debug, Clone, Default)] pub struct TaikoNode; impl NodeTypes for TaikoNode { /// The node's primitive types, defining basic operations and structures. - type Primitives = EthPrimitives; + type Primitives = TaikoPrimitives; /// The type used for configuration of the EVM. type ChainSpec = TaikoChainSpec; /// The type responsible for writing chain primitives to storage. - type Storage = EthStorage; + type Storage = TaikoStorage; /// The node's engine types, defining the interaction with the consensus engine. type Payload = TaikoEngineTypes; } @@ -73,7 +81,7 @@ where /// Creates a new instance of `TaikoAddOns` with default configurations. fn default() -> Self { let add_ons = RpcAddOns::new( - TaikoEthApiBuilder::default(), + TaikoEthApiBuilder, PVB::default(), TaikoEngineApiBuilder::default(), Default::default(), @@ -92,7 +100,7 @@ where + EngineApiValidator<::Payload>, { /// Handle to add-ons. - type Handle = RpcHandle>>; + type Handle = RpcHandle::Evm>>>; /// Configures and launches the add-ons. async fn launch_add_ons( @@ -111,7 +119,7 @@ where + EngineApiValidator<::Payload>, { /// eth API implementation. - type EthApi = TaikoEthApi>; + type EthApi = TaikoEthApi::Evm>>; /// Returns a mutable reference to RPC hooks. fn hooks_mut(&mut self) -> &mut RpcHooks { @@ -142,7 +150,7 @@ where /// The type that builds the node's components. type ComponentsBuilder = ComponentsBuilder< N, - EthereumPoolBuilder, + TaikoPoolBuilder, BasicPayloadServiceBuilder, TaikoNetworkBuilder, TaikoExecutorBuilder, @@ -156,7 +164,7 @@ where fn components_builder(&self) -> Self::ComponentsBuilder { ComponentsBuilder::default() .node_types() - .pool(EthereumPoolBuilder::default()) + .pool(TaikoPoolBuilder::default()) .executor(TaikoExecutorBuilder) .payload(BasicPayloadServiceBuilder::new(TaikoPayloadBuilderBuilder)) .network(TaikoNetworkBuilder) @@ -172,11 +180,11 @@ where impl> DebugNode for TaikoNode { /// RPC block type. Used by [`DebugConsensusClient`] to fetch blocks and submit them to the /// engine. - type RpcBlock = alloy_rpc_types_eth::Block; + type RpcBlock = alloy_rpc_types_eth::Block; /// Converts an RPC block to a primitive block. - fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> reth_ethereum_primitives::Block { - rpc_block.into_consensus().convert_transactions() + fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> TaikoBlock { + rpc_block.into_consensus() } /// Creates a payload attributes builder for local mining in dev mode. diff --git a/crates/node/src/pool.rs b/crates/node/src/pool.rs new file mode 100644 index 00000000..b4d7569c --- /dev/null +++ b/crates/node/src/pool.rs @@ -0,0 +1,83 @@ +use crate::{TaikoNode, txpool::TaikoPooledTransaction}; +use reth_chainspec::EthereumHardforks; +use reth_evm::ConfigureEvm; +use reth_node_api::{FullNodeTypes, PrimitivesTy}; +use reth_node_builder::{BuilderContext, components::PoolBuilder}; +use reth_provider::{ChainSpecProvider, StateProviderFactory}; +use reth_transaction_pool::{ + CoinbaseTipOrdering, PoolConfig, TransactionValidationTaskExecutor, + blobstore::DiskFileBlobStore, +}; +use tracing::{debug, info}; + +/// A Taiko transaction pool builder compatible with TaikoTxEnvelope. +/// +/// This pool builder is similar to EthereumPoolBuilder but works with Taiko's custom +/// transaction envelope type. +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct TaikoPoolBuilder { + /// Pool configuration overrides + pub pool_config_overrides: Option, +} + +impl PoolBuilder for TaikoPoolBuilder +where + Node: FullNodeTypes, + Node::Provider: + StateProviderFactory + ChainSpecProvider + Clone + 'static, + Evm: ConfigureEvm> + Clone + 'static, +{ + type Pool = reth_transaction_pool::Pool< + TransactionValidationTaskExecutor< + reth_transaction_pool::validate::EthTransactionValidator< + Node::Provider, + TaikoPooledTransaction, + Evm, + >, + >, + CoinbaseTipOrdering, + DiskFileBlobStore, + >; + + async fn build_pool( + self, + ctx: &BuilderContext, + evm_config: Evm, + ) -> eyre::Result { + let pool_config = ctx.pool_config(); + let data_dir = ctx.config().datadir(); + let blob_store = DiskFileBlobStore::open(data_dir.blobstore(), Default::default())?; + + let validator = + TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone(), evm_config) + .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes) + .kzg_settings(ctx.kzg_settings()?) + .with_local_transactions_config(pool_config.local_transactions_config.clone()) + .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap) + .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit) + .with_additional_tasks(ctx.config().txpool.additional_validation_tasks) + .build_with_tasks(ctx.task_executor().clone(), blob_store.clone()); + + // Spawn KZG settings initialization in background + if validator.validator().eip4844() { + let kzg_settings = validator.validator().kzg_settings().clone(); + ctx.task_executor().spawn_blocking_task(async move { + let _ = kzg_settings.get(); + debug!(target: "reth::cli", "Initialized KZG settings"); + }); + } + + let transaction_pool = reth_transaction_pool::Pool::new( + validator, + CoinbaseTipOrdering::default(), + blob_store, + pool_config.clone(), + ); + + info!(target: "reth::cli", "Taiko transaction pool initialized"); + debug!(target: "reth::cli", "Spawned txpool maintenance task"); + + Ok(transaction_pool) + } +} diff --git a/crates/node/src/txpool/mod.rs b/crates/node/src/txpool/mod.rs new file mode 100644 index 00000000..5a54981e --- /dev/null +++ b/crates/node/src/txpool/mod.rs @@ -0,0 +1,4 @@ +//! Taiko transaction pool types + +mod transaction; +pub use transaction::TaikoPooledTransaction; diff --git a/crates/node/src/txpool/transaction.rs b/crates/node/src/txpool/transaction.rs new file mode 100644 index 00000000..69d8a014 --- /dev/null +++ b/crates/node/src/txpool/transaction.rs @@ -0,0 +1,223 @@ +//! Taiko pooled transaction type + +use alethia_reth_consensus::transaction::TaikoTxEnvelope; +use alloy_consensus::{BlobTransactionValidationError, Transaction}; +use alloy_eips::{ + eip2718::{Encodable2718, Typed2718}, + eip7594::BlobTransactionSidecarVariant, +}; +use alloy_primitives::{Address, B256, Bytes, TxHash, TxKind, U256}; +use reth_primitives_traits::{InMemorySize, Recovered}; +use reth_transaction_pool::{EthBlobTransactionSidecar, EthPoolTransaction, PoolTransaction}; +use std::sync::Arc; + +/// The default [`PoolTransaction`] for Taiko. +/// +/// This type wraps a consensus transaction with additional cached data that's +/// frequently accessed by the pool for transaction ordering and validation: +/// +/// - `cost`: Pre-calculated max cost (gas * price + value) +/// - `encoded_length`: Cached RLP encoding length for size limits +/// +/// This avoids recalculating these values repeatedly during pool operations. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TaikoPooledTransaction { + /// `EcRecovered` transaction, the consensus format. + pub transaction: Recovered, + + /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`. + /// For legacy transactions: `gas_price * gas_limit + tx_value`. + pub cost: U256, + + /// This is the RLP length of the transaction, computed when the transaction is added to the + /// pool. + pub encoded_length: usize, + + /// Cached transaction hash + tx_hash: TxHash, +} + +impl TaikoPooledTransaction { + /// Create new instance of [Self]. + pub fn new(transaction: Recovered, encoded_length: usize) -> Self { + let gas_cost = U256::from(transaction.max_fee_per_gas()) + .saturating_mul(U256::from(transaction.gas_limit())); + + let cost = gas_cost.saturating_add(transaction.value()); + let tx_hash = transaction.tx_hash(); + + Self { transaction, cost, encoded_length, tx_hash } + } + + /// Return the reference to the underlying transaction. + pub const fn transaction(&self) -> &Recovered { + &self.transaction + } +} + +impl PoolTransaction for TaikoPooledTransaction { + type TryFromConsensusError = alloy_consensus::error::ValueError; + + type Consensus = TaikoTxEnvelope; + + // The pooled type is the consensus TaikoPooledTransaction for network compatibility + type Pooled = alethia_reth_consensus::transaction::TaikoPooledTransaction; + + fn clone_into_consensus(&self) -> Recovered { + self.transaction().clone() + } + + fn into_consensus(self) -> Recovered { + self.transaction + } + + fn from_pooled(tx: Recovered) -> Self { + // Convert consensus pooled transaction to envelope + let envelope: TaikoTxEnvelope = tx.inner().clone().into(); + let recovered = Recovered::new_unchecked(envelope, *tx.signer_ref()); + let encoded_length = recovered.encode_2718_len(); + Self::new(recovered, encoded_length) + } + + /// Returns hash of the transaction. + fn hash(&self) -> &TxHash { + &self.tx_hash + } + + /// Returns the Sender of the transaction. + fn sender(&self) -> Address { + self.transaction.signer() + } + + /// Returns a reference to the Sender of the transaction. + fn sender_ref(&self) -> &Address { + self.transaction.signer_ref() + } + + /// Returns the cost that this transaction is allowed to consume: + /// + /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`. + /// For legacy transactions: `gas_price * gas_limit + tx_value`. + fn cost(&self) -> &U256 { + &self.cost + } + + /// Returns the length of the rlp encoded object + fn encoded_length(&self) -> usize { + self.encoded_length + } +} + +impl Typed2718 for TaikoPooledTransaction { + fn ty(&self) -> u8 { + self.transaction.ty() + } +} + +impl InMemorySize for TaikoPooledTransaction { + fn size(&self) -> usize { + self.transaction.size() + } +} + +impl Transaction for TaikoPooledTransaction { + fn chain_id(&self) -> Option { + self.transaction.chain_id() + } + + fn nonce(&self) -> u64 { + self.transaction.nonce() + } + + fn gas_limit(&self) -> u64 { + self.transaction.gas_limit() + } + + fn gas_price(&self) -> Option { + self.transaction.gas_price() + } + + fn max_fee_per_gas(&self) -> u128 { + self.transaction.max_fee_per_gas() + } + + fn max_priority_fee_per_gas(&self) -> Option { + self.transaction.max_priority_fee_per_gas() + } + + fn max_fee_per_blob_gas(&self) -> Option { + self.transaction.max_fee_per_blob_gas() + } + + fn priority_fee_or_price(&self) -> u128 { + self.transaction.priority_fee_or_price() + } + + fn effective_gas_price(&self, base_fee: Option) -> u128 { + self.transaction.effective_gas_price(base_fee) + } + + fn is_dynamic_fee(&self) -> bool { + self.transaction.is_dynamic_fee() + } + + fn kind(&self) -> TxKind { + self.transaction.kind() + } + + fn is_create(&self) -> bool { + self.transaction.is_create() + } + + fn value(&self) -> U256 { + self.transaction.value() + } + + fn input(&self) -> &Bytes { + self.transaction.input() + } + + fn access_list(&self) -> Option<&alloy_eips::eip2930::AccessList> { + self.transaction.access_list() + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + self.transaction.blob_versioned_hashes() + } + + fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { + self.transaction.authorization_list() + } +} + +impl EthPoolTransaction for TaikoPooledTransaction { + fn take_blob(&mut self) -> EthBlobTransactionSidecar { + // Taiko doesn't support blobs + EthBlobTransactionSidecar::None + } + + fn try_into_pooled_eip4844( + self, + _sidecar: Arc, + ) -> Option> { + // Taiko doesn't support EIP-4844 + None + } + + fn try_from_eip4844( + _tx: Recovered, + _sidecar: BlobTransactionSidecarVariant, + ) -> Option { + // Taiko doesn't support EIP-4844 + None + } + + fn validate_blob( + &self, + _blob: &BlobTransactionSidecarVariant, + _settings: &alloy_eips::eip4844::env_settings::KzgSettings, + ) -> Result<(), BlobTransactionValidationError> { + // Taiko doesn't support blobs + Err(BlobTransactionValidationError::NotBlobTransaction(self.ty())) + } +} diff --git a/crates/payload/Cargo.toml b/crates/payload/Cargo.toml index 3e483997..80c37043 100644 --- a/crates/payload/Cargo.toml +++ b/crates/payload/Cargo.toml @@ -26,8 +26,10 @@ alloy-signer = { workspace = true } alloy-signer-local = { workspace = true, features = ["keystore"] } alloy-sol-types = { workspace = true } eyre = { workspace = true } +reth-revm = { workspace = true } +reth-provider = { workspace = true } +reth-payload-primitives = { workspace = true } op-alloy-flz = { workspace = true } -reth = { workspace = true } reth-basic-payload-builder = { workspace = true } reth-chainspec = { workspace = true } reth-engine-local = { workspace = true } @@ -39,6 +41,5 @@ reth-evm-ethereum = { workspace = true } reth-node-api = { workspace = true } reth-primitives = { workspace = true } reth-primitives-traits = { workspace = true } -reth-provider = { workspace = true } reth-transaction-pool = { workspace = true } tracing = { workspace = true } diff --git a/crates/payload/src/builder.rs b/crates/payload/src/builder.rs index e49286ff..3a54fc0a 100644 --- a/crates/payload/src/builder.rs +++ b/crates/payload/src/builder.rs @@ -1,42 +1,43 @@ -use std::sync::Arc; - use alloy_consensus::Transaction; use alloy_eips::eip4844::BYTES_PER_BLOB; -use reth::{ - api::{PayloadBuilderAttributes, PayloadBuilderError}, - providers::{ChainSpecProvider, StateProviderFactory}, - revm::{State, cancelled::CancelOnDrop, database::StateProviderDatabase, primitives::U256}, -}; use reth_basic_payload_builder::{ BuildArguments, BuildOutcome, MissingPayloadBehaviour, PayloadBuilder, PayloadConfig, }; use reth_errors::RethError; -use reth_ethereum::{EthPrimitives, TransactionSigned as EthTransactionSigned}; -use reth_ethereum_engine_primitives::EthBuiltPayload; use reth_evm::{ ConfigureEvm, block::{BlockExecutionError, BlockValidationError}, execute::{BlockBuilder, BlockBuilderOutcome}, }; -use reth_evm_ethereum::RethReceiptBuilder; +use reth_payload_primitives::{PayloadBuilderAttributes, PayloadBuilderError}; use reth_primitives::{Header as RethHeader, Recovered}; +use reth_provider::{ChainSpecProvider, StateProviderFactory}; +use reth_revm::{ + State, cancelled::CancelOnDrop, database::StateProviderDatabase, primitives::U256, +}; +use std::sync::Arc; use tracing::{debug, trace, warn}; use alethia_reth_block::{ assembler::TaikoBlockAssembler, config::{TaikoEvmConfig, TaikoNextBlockEnvAttributes}, factory::TaikoBlockExecutorFactory, + receipt_builder::TaikoReceiptBuilder, tx_selection::{ DEFAULT_DA_ZLIB_GUARD_BYTES, SelectionOutcome, TxSelectionConfig, select_and_execute_pool_transactions, }, }; use alethia_reth_chainspec::spec::TaikoChainSpec; -use alethia_reth_consensus::validation::{ - ANCHOR_V3_V4_GAS_LIMIT, AnchorValidationContext, validate_anchor_transaction, +use alethia_reth_consensus::{ + transaction::TaikoTxEnvelope, + validation::{ANCHOR_V3_V4_GAS_LIMIT, AnchorValidationContext, validate_anchor_transaction}, }; use alethia_reth_evm::factory::TaikoEvmFactory; -use alethia_reth_primitives::payload::builder::TaikoPayloadBuilderAttributes; +use alethia_reth_primitives::{ + TaikoPrimitives, + payload::{builder::TaikoPayloadBuilderAttributes, built_payload::TaikoBuiltPayload}, +}; /// Creates an error for when a transaction's effective tip cannot be calculated. fn missing_tip_error(base_fee: u64) -> PayloadBuilderError { @@ -67,7 +68,7 @@ enum ExecutionOutcome { /// Context for executing transactions in new mode (anchor + pool transactions). struct PoolExecutionContext<'a> { /// Prebuilt anchor transaction for new mode. - anchor_tx: &'a Recovered, + anchor_tx: &'a Recovered, /// The parent block header. parent_header: &'a RethHeader, /// Timestamp for the new block. @@ -85,8 +86,8 @@ struct PoolExecutionContext<'a> { /// Preserves legacy mode: validation errors are skipped, fatal errors abort /// the build, and cancellation short-circuits the loop. fn execute_provided_transactions( - builder: &mut impl BlockBuilder, - transactions: &[Recovered], + builder: &mut impl BlockBuilder, + transactions: &[Recovered], base_fee: u64, cancel: &CancelOnDrop, ) -> Result { @@ -125,7 +126,7 @@ fn execute_provided_transactions( /// Executes new-mode transactions: injects the anchor transaction, then pulls /// from the mempool until exhaustion or cancellation. fn execute_anchor_and_pool_transactions( - builder: &mut impl BlockBuilder, + builder: &mut impl BlockBuilder, pool: &Pool, client: &Client, ctx: &PoolExecutionContext<'_>, @@ -136,9 +137,7 @@ where + ChainSpecProvider + reth_provider::BlockReader, Pool: reth_transaction_pool::TransactionPool< - Transaction: reth_transaction_pool::PoolTransaction< - Consensus = reth_ethereum::TransactionSigned, - >, + Transaction: reth_transaction_pool::PoolTransaction, >, { debug!(target: "payload_builder", id=%ctx.payload_id, "injecting anchor transaction"); @@ -209,10 +208,10 @@ impl TaikoPayloadBuilder { impl PayloadBuilder for TaikoPayloadBuilder where EvmConfig: ConfigureEvm< - Primitives = EthPrimitives, + Primitives = TaikoPrimitives, NextBlockEnvCtx = TaikoNextBlockEnvAttributes, BlockExecutorFactory = TaikoBlockExecutorFactory< - RethReceiptBuilder, + TaikoReceiptBuilder, Arc, TaikoEvmFactory, >, @@ -224,15 +223,13 @@ where + reth_provider::BlockReader + Clone, Pool: reth_transaction_pool::TransactionPool< - Transaction: reth_transaction_pool::PoolTransaction< - Consensus = reth_ethereum::TransactionSigned, - >, + Transaction: reth_transaction_pool::PoolTransaction, > + Clone, { /// The payload attributes type to accept for building. type Attributes = TaikoPayloadBuilderAttributes; /// /// The type of the built payload. - type BuiltPayload = EthBuiltPayload; + type BuiltPayload = TaikoBuiltPayload; /// Tries to build a transaction payload using provided arguments. /// @@ -248,8 +245,8 @@ where /// A `Result` indicating the build outcome or an error. fn try_build( &self, - args: BuildArguments, - ) -> Result, PayloadBuilderError> { + args: BuildArguments, + ) -> Result, PayloadBuilderError> { taiko_payload(&self.evm_config, &self.client, &self.pool, args) } @@ -267,7 +264,7 @@ where fn build_empty_payload( &self, _config: PayloadConfig, - ) -> Result { + ) -> Result { Err(PayloadBuilderError::MissingPayload) } } @@ -278,14 +275,14 @@ fn taiko_payload( evm_config: &EvmConfig, client: &Client, pool: &Pool, - args: BuildArguments, -) -> Result, PayloadBuilderError> + args: BuildArguments, +) -> Result, PayloadBuilderError> where EvmConfig: ConfigureEvm< - Primitives = EthPrimitives, + Primitives = TaikoPrimitives, NextBlockEnvCtx = TaikoNextBlockEnvAttributes, BlockExecutorFactory = TaikoBlockExecutorFactory< - RethReceiptBuilder, + TaikoReceiptBuilder, Arc, TaikoEvmFactory, >, @@ -296,9 +293,7 @@ where + ChainSpecProvider + reth_provider::BlockReader, Pool: reth_transaction_pool::TransactionPool< - Transaction: reth_transaction_pool::PoolTransaction< - Consensus = reth_ethereum::TransactionSigned, - >, + Transaction: reth_transaction_pool::PoolTransaction, >, { let BuildArguments { mut cached_reads, config, cancel, best_payload: _ } = args; @@ -375,11 +370,7 @@ where let sealed_block = Arc::new(block.sealed_block().clone()); debug!(target: "payload_builder", id=%attributes.payload_id(), sealed_block_header = ?sealed_block.sealed_header(), "sealed built block"); - // Build the payload - Ok(BuildOutcome::Freeze(EthBuiltPayload::new( - attributes.payload_id(), - sealed_block, - total_fees, - None, - ))) + let payload = TaikoBuiltPayload::new(attributes.payload_id(), sealed_block, total_fees, None); + + Ok(BuildOutcome::Freeze(payload)) } diff --git a/crates/payload/src/lib.rs b/crates/payload/src/lib.rs index f138fda5..46b4f867 100644 --- a/crates/payload/src/lib.rs +++ b/crates/payload/src/lib.rs @@ -1,50 +1,7 @@ #![cfg_attr(not(test), deny(missing_docs, clippy::missing_docs_in_private_items))] #![cfg_attr(test, allow(missing_docs, clippy::missing_docs_in_private_items))] //! Taiko payload builder integration with `reth` node services. -use reth::{ - api::{FullNodeTypes, NodeTypes}, - builder::{BuilderContext, components::PayloadBuilderBuilder}, - transaction_pool::{PoolTransaction, TransactionPool}, -}; -use reth_ethereum::{EthPrimitives, TransactionSigned}; - -use alethia_reth_block::config::TaikoEvmConfig; -use alethia_reth_chainspec::spec::TaikoChainSpec; -use alethia_reth_primitives::engine::TaikoEngineTypes; /// Taiko payload-building implementation and transaction assembly flow. pub mod builder; pub use builder::TaikoPayloadBuilder; - -/// The builder to spawn [`TaikoPayloadBuilder`] payload building tasks. -#[derive(Debug, Default, Clone)] -pub struct TaikoPayloadBuilderBuilder; - -impl PayloadBuilderBuilder for TaikoPayloadBuilderBuilder -where - Node: FullNodeTypes< - Types: NodeTypes< - Primitives = EthPrimitives, - ChainSpec = TaikoChainSpec, - Payload = TaikoEngineTypes, - >, - >, - Pool: TransactionPool> - + Unpin - + 'static, -{ - /// Payload builder implementation. - type PayloadBuilder = TaikoPayloadBuilder; - - /// Spawns the payload service and returns the handle to it. - /// - /// The [`BuilderContext`] is provided to allow access to the node's configuration. - async fn build_payload_builder( - self, - ctx: &BuilderContext, - pool: Pool, - evm_config: TaikoEvmConfig, - ) -> eyre::Result { - Ok(TaikoPayloadBuilder::new(ctx.provider().clone(), pool, evm_config)) - } -} diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 5d15cc1d..b3c111a4 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -9,32 +9,50 @@ license.workspace = true path = "src/lib.rs" [features] -default = ["serde", "net"] +default = ["serde", "net", "reth-codec", "serde-bincode-compat"] +local-payload-builder = ["dep:reth-engine-local"] serde = [ "dep:serde", "alloy-primitives/serde", "alloy-rpc-types-engine/serde", "alloy-rpc-types-eth/serde", ] +reth-codec = ["alethia-reth-consensus/reth-codec"] +serde-bincode-compat = ["alethia-reth-consensus/serde-bincode-compat"] +alloy-compat = ["alethia-reth-consensus/alloy-compat"] net = ["dep:reth-engine-local", "dep:reth-node-api", "serde"] [dependencies] +alethia-reth-consensus = { path = "../consensus", features = ["k256", "serde"] } alloy-consensus = { workspace = true } alloy-primitives = { workspace = true } alloy-rlp = { workspace = true } +alloy-eips = { workspace = true } alloy-rpc-types-engine = { workspace = true } alloy-rpc-types-eth = { workspace = true } alloy-serde = { workspace = true } -reth-chainspec = { workspace = true } +alethia-reth-chainspec = { path = "../chainspec", default-features = false } +reth-revm = { workspace = true } +reth-chain-state = { workspace = true } reth-engine-local = { workspace = true, optional = true } +reth-node-api = { workspace = true, optional = true } +reth-chainspec = { workspace = true } reth-ethereum = { workspace = true } reth-ethereum-engine-primitives = { workspace = true } -reth-node-api = { workspace = true, optional = true } reth-payload-primitives = { workspace = true } -reth-primitives = { workspace = true } +reth-engine-primitives = { workspace = true } reth-primitives-traits = { workspace = true } +reth-primitives = { workspace = true } serde = { workspace = true, optional = true } serde_with = { workspace = true, features = ["base64"] } sha2 = { workspace = true } tracing = { workspace = true } + +[dev-dependencies] +alethia-reth-consensus = { path = "../consensus", features = [ + "k256", + "serde", + "reth-codec", +] } +reth-engine-local = { workspace = true } diff --git a/crates/primitives/src/engine/mod.rs b/crates/primitives/src/engine/mod.rs index 23179da5..b7e9f208 100644 --- a/crates/primitives/src/engine/mod.rs +++ b/crates/primitives/src/engine/mod.rs @@ -1,14 +1,14 @@ -//! Engine API type adapters for Taiko execution payloads. -use alloy_rpc_types_engine::{ - ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadEnvelopeV4, - ExecutionPayloadEnvelopeV5, ExecutionPayloadEnvelopeV6, ExecutionPayloadV1, -}; -use reth_ethereum_engine_primitives::EthBuiltPayload; -use reth_node_api::{BuiltPayload, EngineTypes, NodePrimitives, PayloadTypes}; +use alloy_rpc_types_engine::{ExecutionPayloadEnvelopeV2, ExecutionPayloadV1}; +use reth_engine_primitives::EngineTypes; +use reth_payload_primitives::{BuiltPayload, PayloadTypes}; use reth_primitives::SealedBlock; +use reth_primitives_traits::NodePrimitives; use self::types::{TaikoExecutionData, TaikoExecutionDataSidecar}; -use crate::payload::{attributes::TaikoPayloadAttributes, builder::TaikoPayloadBuilderAttributes}; +use crate::payload::{ + attributes::TaikoPayloadAttributes, builder::TaikoPayloadBuilderAttributes, + built_payload::TaikoBuiltPayload, +}; /// Taiko execution payload and sidecar structures. pub mod types; @@ -22,7 +22,7 @@ impl PayloadTypes for TaikoEngineTypes { /// The execution payload type provided as input. type ExecutionData = TaikoExecutionData; /// The built payload type. - type BuiltPayload = EthBuiltPayload; + type BuiltPayload = TaikoBuiltPayload; /// The RPC payload attributes type the CL node emits via the engine API. type PayloadAttributes = TaikoPayloadAttributes; /// The payload attributes type that contains information about a running payload job. @@ -56,11 +56,11 @@ impl EngineTypes for TaikoEngineTypes { /// Execution Payload V2 envelope type. type ExecutionPayloadEnvelopeV2 = ExecutionPayloadEnvelopeV2; /// Execution Payload V3 envelope type. - type ExecutionPayloadEnvelopeV3 = ExecutionPayloadEnvelopeV3; + type ExecutionPayloadEnvelopeV3 = ExecutionPayloadEnvelopeV2; /// Execution Payload V4 envelope type. - type ExecutionPayloadEnvelopeV4 = ExecutionPayloadEnvelopeV4; + type ExecutionPayloadEnvelopeV4 = ExecutionPayloadEnvelopeV2; /// Execution Payload V5 envelope type. - type ExecutionPayloadEnvelopeV5 = ExecutionPayloadEnvelopeV5; + type ExecutionPayloadEnvelopeV5 = ExecutionPayloadEnvelopeV2; /// Execution Payload V6 envelope type. - type ExecutionPayloadEnvelopeV6 = ExecutionPayloadEnvelopeV6; + type ExecutionPayloadEnvelopeV6 = ExecutionPayloadEnvelopeV2; } diff --git a/crates/primitives/src/engine/types.rs b/crates/primitives/src/engine/types.rs index e2bd5147..ff71ed53 100644 --- a/crates/primitives/src/engine/types.rs +++ b/crates/primitives/src/engine/types.rs @@ -4,10 +4,12 @@ use alloy_rpc_types_engine::{ExecutionPayload, ExecutionPayloadV1}; use alloy_rpc_types_eth::Withdrawal; use reth_payload_primitives::ExecutionPayload as ExecutionPayloadTr; +/// Static empty withdrawals vector for Taiko (which doesn't use withdrawals) +static EMPTY_WITHDRAWALS: Vec = Vec::new(); + /// Represents the execution data for the Taiko network, which includes the execution payload and a /// sidecar. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct TaikoExecutionData { /// Base execution payload fields returned to the engine API. #[cfg_attr(feature = "serde", serde(flatten))] @@ -33,9 +35,8 @@ impl From for ExecutionPayload { /// Represents the sidecar data for the Taiko execution payload, which includes the transaction /// hash, optional withdrawals hash, and a boolean indicating if the block is a Taiko block. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] pub struct TaikoExecutionDataSidecar { /// Transactions root hash for the payload. pub tx_hash: B256, @@ -63,7 +64,7 @@ impl ExecutionPayloadTr for TaikoExecutionData { /// Returns the withdrawals associated with the block, if any. fn withdrawals(&self) -> Option<&Vec> { - None + Some(&EMPTY_WITHDRAWALS) } /// Returns the access list associated with the block, if any. @@ -97,9 +98,8 @@ impl ExecutionPayloadTr for TaikoExecutionData { /// See also: /// NOTE: we change `transactions` to `Option>` to ensure backward compatibility with the /// taiko-client driver behavior. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] pub struct TaikoExecutionPayloadV1 { /// Keccak256 hash of the parent header, used to link this payload to the canonical chain. pub parent_hash: B256, diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index bf8fd7d2..2384f47e 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -1,6 +1,8 @@ #![cfg_attr(not(test), deny(missing_docs, clippy::missing_docs_in_private_items))] #![cfg_attr(test, allow(missing_docs, clippy::missing_docs_in_private_items))] //! Shared primitive types used across Alethia Reth crates. +use reth_ethereum::Receipt; + #[cfg(feature = "net")] /// Engine API payload and type definitions. pub mod engine; @@ -9,6 +11,31 @@ pub mod extra_data; /// Payload-attribute and builder primitive types. pub mod payload; +/// Taiko-specific block type. +pub type TaikoBlock = alloy_consensus::Block; + +/// Taiko-specific block body type. +pub type TaikoBlockBody = ::Body; + +pub use alethia_reth_consensus::transaction::TaikoTxEnvelope; + +/// Primitive types for Taiko Node. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TaikoPrimitives; + +impl reth_primitives_traits::NodePrimitives for TaikoPrimitives { + type Block = TaikoBlock; + type BlockHeader = alloy_consensus::Header; + type BlockBody = TaikoBlockBody; + type SignedTx = TaikoTxEnvelope; + type Receipt = Receipt; +} +/// Bincode-compatible serde implementations. +#[cfg(feature = "serde-bincode-compat")] +pub mod serde_bincode_compat { + pub use alethia_reth_consensus::transaction::serde_bincode_compat::*; +} pub use extra_data::{ SHASTA_EXTRA_DATA_LEN, decode_shasta_basefee_sharing_pctg, decode_shasta_proposal_id, }; diff --git a/crates/primitives/src/payload/attributes.rs b/crates/primitives/src/payload/attributes.rs index 692bb452..37f35363 100644 --- a/crates/primitives/src/payload/attributes.rs +++ b/crates/primitives/src/payload/attributes.rs @@ -10,14 +10,14 @@ use reth_engine_local::LocalPayloadAttributesBuilder; #[cfg(feature = "net")] use reth_node_api::{PayloadAttributes, PayloadAttributesBuilder}; #[cfg(feature = "net")] -use reth_primitives_traits::{SealedHeader, constants::MAXIMUM_GAS_LIMIT_BLOCK}; +use reth_primitives_traits::SealedHeader; #[cfg(feature = "serde")] use serde_with::{As, Bytes, base64::Base64}; /// Taiko Payload Attributes -#[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct TaikoPayloadAttributes { /// The ETH payload attributes #[cfg_attr(feature = "serde", serde(flatten))] @@ -110,9 +110,9 @@ impl RpcL1Origin { } } -/// Implement `PayloadAttributesBuilder` for `LocalPayloadAttributesBuilder`, +/// Implement `PayloadAttributesBuilder` for `LocalPayloadAttributesBuilder`, /// to build `TaikoPayloadAttributes` from the local payload attributes builder. -#[cfg(feature = "net")] +#[cfg(any(feature = "net", feature = "local-payload-builder"))] impl PayloadAttributesBuilder for LocalPayloadAttributesBuilder where @@ -121,6 +121,8 @@ where /// Return a new payload attribute from the builder. fn build(&self, parent: &SealedHeader) -> TaikoPayloadAttributes { // Delegate to the underlying ETH payload builder to avoid self-recursion. + + use alloy_primitives::Address; let eth_payload_attributes = >>, + pub transactions: Option>>, /// The extra data for the L2 block. pub extra_data: Bytes, /// Prebuilt anchor transaction for new mode, decoded and recovered. - pub anchor_transaction: Option>, + pub anchor_transaction: Option>, } impl PayloadBuilderAttributes for TaikoPayloadBuilderAttributes { @@ -112,7 +112,7 @@ impl PayloadBuilderAttributes for TaikoPayloadBuilderAttributes { .anchor_transaction .as_ref() .map(|bytes| { - TransactionSigned::decode(&mut &bytes[..]) + TaikoTxEnvelope::decode(&mut &bytes[..]) .map_err(|_| alloy_rlp::Error::Custom("invalid anchor_transaction"))? .try_into_recovered() .map_err(|_| alloy_rlp::Error::Custom("anchor tx not recoverable")) @@ -210,8 +210,8 @@ pub fn payload_id_taiko( } /// Decode RLP-encoded bytes into signed transactions. -fn decode_transactions(bytes: &[u8]) -> Result, alloy_rlp::Error> { - Vec::::decode(&mut &bytes[..]) +fn decode_transactions(bytes: &[u8]) -> Result, alloy_rlp::Error> { + Vec::::decode(&mut &bytes[..]) } #[cfg(all(test, feature = "net"))] diff --git a/crates/primitives/src/payload/built_payload.rs b/crates/primitives/src/payload/built_payload.rs new file mode 100644 index 00000000..933d0856 --- /dev/null +++ b/crates/primitives/src/payload/built_payload.rs @@ -0,0 +1,209 @@ +//! Payload related types + +use std::{fmt::Debug, sync::Arc}; + +use alloy_consensus::Block; +use alloy_eips::eip7685::Requests; +use alloy_primitives::U256; +use alloy_rpc_types_engine::{ + BlobsBundleV1, BlobsBundleV2, ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, + ExecutionPayloadEnvelopeV4, ExecutionPayloadEnvelopeV5, ExecutionPayloadFieldV2, + ExecutionPayloadV1, ExecutionPayloadV3, PayloadId, +}; +use reth_ethereum_engine_primitives::BuiltPayloadConversionError; +use reth_payload_primitives::{BuiltPayload, BuiltPayloadExecutedBlock}; +use reth_primitives_traits::{BlockBody, NodePrimitives, SealedBlock, SignedTransaction}; + +use crate::TaikoPrimitives; + +/// Contains the built payload. +#[derive(Debug, Clone)] +pub struct TaikoBuiltPayload { + /// Identifier of the payload + pub(crate) id: PayloadId, + /// Sealed block + pub(crate) block: Arc>, + /// Block execution data for the payload, if any. + pub(crate) executed_block: Option>, + /// The fees of the block + pub(crate) fees: U256, +} + +// === impl BuiltPayload === + +impl TaikoBuiltPayload { + /// Initializes the payload with the given initial block. + pub const fn new( + id: PayloadId, + block: Arc>, + fees: U256, + executed_block: Option>, + ) -> Self { + Self { id, block, fees, executed_block } + } + + /// Returns the identifier of the payload. + pub const fn id(&self) -> PayloadId { + self.id + } + + /// Returns the built block(sealed) + pub fn block(&self) -> &SealedBlock { + &self.block + } + + /// Fees of the block + pub const fn fees(&self) -> U256 { + self.fees + } + + /// Converts the value into [`SealedBlock`]. + pub fn into_sealed_block(self) -> SealedBlock { + Arc::unwrap_or_clone(self.block) + } + + /// Try converting built payload into [`ExecutionPayloadEnvelopeV3`]. + /// + /// Returns an error if the payload contains non EIP-4844 sidecar. + pub fn try_into_v3(self) -> Result + where + N::Block: Into::Transaction>>, + { + let Self { block, fees, .. } = self; + + Ok(ExecutionPayloadEnvelopeV3 { + execution_payload: ExecutionPayloadV3::from_block_unchecked( + block.hash(), + &Arc::unwrap_or_clone(block).into_block().into(), + ), + block_value: fees, + // From the engine API spec: + // + // > Client software **MAY** use any heuristics to decide whether to set + // `shouldOverrideBuilder` flag or not. If client software does not implement any + // heuristic this flag **SHOULD** be set to `false`. + // + // Spec: + // + should_override_builder: false, + blobs_bundle: BlobsBundleV1::empty(), + }) + } + + /// Try converting built payload into [`ExecutionPayloadEnvelopeV4`]. + /// + /// Returns an error if the payload contains non EIP-4844 sidecar. + pub fn try_into_v4(self) -> Result + where + N::Block: Into::Transaction>>, + { + Ok(ExecutionPayloadEnvelopeV4 { + execution_requests: Requests::default(), + envelope_inner: self.try_into_v3()?, + }) + } + + /// Try converting built payload into [`ExecutionPayloadEnvelopeV5`]. + pub fn try_into_v5(self) -> Result + where + N::Block: Into::Transaction>>, + { + let Self { block, fees, .. } = self; + + Ok(ExecutionPayloadEnvelopeV5 { + execution_payload: ExecutionPayloadV3::from_block_unchecked( + block.hash(), + &Arc::unwrap_or_clone(block).into_block().into(), + ), + block_value: fees, + // From the engine API spec: + // + // > Client software **MAY** use any heuristics to decide whether to set + // `shouldOverrideBuilder` flag or not. If client software does not implement any + // heuristic this flag **SHOULD** be set to `false`. + // + // Spec: + // + should_override_builder: false, + blobs_bundle: BlobsBundleV2::empty(), + execution_requests: Requests::default(), + }) + } +} + +impl TryFrom for ExecutionPayloadEnvelopeV3 { + type Error = BuiltPayloadConversionError; + + fn try_from(value: TaikoBuiltPayload) -> Result { + value.try_into_v3() + } +} + +impl TryFrom for ExecutionPayloadEnvelopeV4 { + type Error = BuiltPayloadConversionError; + + fn try_from(value: TaikoBuiltPayload) -> Result { + value.try_into_v4() + } +} + +impl TryFrom for ExecutionPayloadEnvelopeV5 { + type Error = BuiltPayloadConversionError; + + fn try_from(value: TaikoBuiltPayload) -> Result { + value.try_into_v5() + } +} + +impl BuiltPayload for TaikoBuiltPayload { + type Primitives = N; + + fn block(&self) -> &SealedBlock { + self.block() + } + + fn fees(&self) -> U256 { + self.fees + } + + fn executed_block(&self) -> Option> { + self.executed_block.clone() + } + + fn requests(&self) -> Option { + None + } +} + +// V1 engine_getPayloadV1 response +impl From> for ExecutionPayloadV1 +where + T: SignedTransaction, + N: NodePrimitives>, +{ + fn from(value: TaikoBuiltPayload) -> Self { + Self::from_block_unchecked( + value.block().hash(), + &Arc::unwrap_or_clone(value.block).into_block(), + ) + } +} + +// V2 engine_getPayloadV2 response +impl From> for ExecutionPayloadEnvelopeV2 +where + T: SignedTransaction, + N: NodePrimitives>, +{ + fn from(value: TaikoBuiltPayload) -> Self { + let TaikoBuiltPayload { block, fees, .. } = value; + + Self { + block_value: fees, + execution_payload: ExecutionPayloadFieldV2::from_block_unchecked( + block.hash(), + &Arc::unwrap_or_clone(block).into_block(), + ), + } + } +} diff --git a/crates/primitives/src/payload/mod.rs b/crates/primitives/src/payload/mod.rs index 198d4001..6b77105e 100644 --- a/crates/primitives/src/payload/mod.rs +++ b/crates/primitives/src/payload/mod.rs @@ -3,3 +3,4 @@ pub mod attributes; /// Payload builder attributes and payload-id derivation helpers. pub mod builder; +pub mod built_payload; diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index aa2612b1..551c3112 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -19,11 +19,16 @@ alethia-reth-chainspec = { path = "../chainspec" } alethia-reth-consensus = { path = "../consensus" } alethia-reth-db = { path = "../db" } alethia-reth-evm = { path = "../evm" } -alethia-reth-primitives = { path = "../primitives", features = ["serde"] } +alethia-reth-primitives = { path = "../primitives", features = [ + "serde", + "reth-codec", + "serde-bincode-compat", +] } alloy-consensus = { workspace = true } alloy-eips = { workspace = true } alloy-hardforks = { workspace = true } alloy-json-rpc = { workspace = true } +alloy-network = { workspace = true } alloy-primitives = { workspace = true } alloy-rpc-types-engine = { workspace = true } alloy-rpc-types-eth = { workspace = true } @@ -33,7 +38,11 @@ jsonrpsee = { workspace = true, features = ["macros", "server", "client"] } jsonrpsee-core = { workspace = true } jsonrpsee-types = { workspace = true } op-alloy-flz = { workspace = true } -reth = { workspace = true } + +reth-chainspec = { workspace = true } +reth-primitives = { workspace = true } +reth-transaction-pool = { workspace = true } +reth-tasks = { workspace = true } reth-db = { workspace = true } reth-db-api = { workspace = true } reth-engine-primitives = { workspace = true } @@ -47,10 +56,13 @@ reth-node-builder = { workspace = true } reth-node-core = { workspace = true } reth-node-ethereum = { workspace = true } reth-payload-primitives = { workspace = true } +reth-payload-builder = { workspace = true } reth-primitives-traits = { workspace = true } reth-provider = { workspace = true } reth-revm = { workspace = true } reth-rpc = { workspace = true } +reth-rpc-api = { workspace = true } +reth-rpc-convert = { workspace = true } reth-rpc-engine-api = { workspace = true } reth-rpc-eth-api = { workspace = true } reth-rpc-eth-types = { workspace = true } diff --git a/crates/rpc/src/converter.rs b/crates/rpc/src/converter.rs new file mode 100644 index 00000000..6f4e253f --- /dev/null +++ b/crates/rpc/src/converter.rs @@ -0,0 +1,22 @@ +//! Taiko-specific RPC type conversions + +use alethia_reth_chainspec::spec::TaikoChainSpec; +use alloy_network::Ethereum; +use reth_rpc_convert::RpcConverter; +use reth_rpc_eth_types::receipt::EthReceiptConverter; + +/// RPC converter type alias for Taiko with the given EVM config. +/// +/// Uses the default `()` SimTxConverter which delegates to `TryIntoSimTx` trait. +/// The `TransactionRequest -> TaikoTxEnvelope` conversion is implemented via `TryIntoSimTx` +/// in the consensus crate for simulation purposes (eth_call, eth_estimateGas). +pub type TaikoRpcConverter = RpcConverter< + Ethereum, + Evm, + EthReceiptConverter, + (), + (), + (), // SimTxConverter - uses TryIntoSimTx trait (implemented in consensus crate) + (), + (), +>; diff --git a/crates/rpc/src/engine/api.rs b/crates/rpc/src/engine/api.rs index 7a120f30..be0ee4ae 100644 --- a/crates/rpc/src/engine/api.rs +++ b/crates/rpc/src/engine/api.rs @@ -2,7 +2,8 @@ use std::io; use alethia_reth_primitives::{ - engine::types::TaikoExecutionData, payload::attributes::TaikoPayloadAttributes, + engine::types::TaikoExecutionData, + payload::{attributes::TaikoPayloadAttributes, built_payload::TaikoBuiltPayload}, }; use alloy_hardforks::EthereumHardforks; use alloy_primitives::BlockNumber; @@ -11,20 +12,18 @@ use async_trait::async_trait; use jsonrpsee::{RpcModule, proc_macros::rpc}; use jsonrpsee_core::RpcResult; use jsonrpsee_types::ErrorObjectOwned; -use reth::{ - payload::PayloadStore, rpc::api::IntoEngineApiRpcModule, transaction_pool::TransactionPool, -}; use reth_db::transaction::DbTx; use reth_db_api::transaction::DbTxMut; use reth_engine_primitives::EngineApiValidator; -use reth_ethereum_engine_primitives::EthBuiltPayload; -use reth_node_api::{EngineTypes, PayloadBuilderError, PayloadTypes}; -use reth_payload_primitives::PayloadKind; +use reth_node_api::{EngineTypes, PayloadBuilderError, PayloadKind, PayloadTypes}; +use reth_payload_builder::PayloadStore; use reth_provider::{ BlockReader, DBProvider, DatabaseProviderFactory, HeaderProvider, StateProviderFactory, }; use reth_rpc::EngineApi; +use reth_rpc_api::IntoEngineApiRpcModule; use reth_rpc_engine_api::EngineApiError; +use reth_transaction_pool::TransactionPool; use alethia_reth_db::model::{ STORED_L1_HEAD_ORIGIN_KEY, StoredL1HeadOriginTable, StoredL1Origin, StoredL1OriginTable, @@ -102,7 +101,7 @@ where EngineT: EngineTypes< ExecutionData = TaikoExecutionData, PayloadAttributes = TaikoPayloadAttributes, - BuiltPayload = EthBuiltPayload, + BuiltPayload = TaikoBuiltPayload, >, Pool: TransactionPool + 'static, Validator: EngineApiValidator, @@ -163,7 +162,7 @@ where EngineT: EngineTypes< ExecutionData = TaikoExecutionData, PayloadAttributes = TaikoPayloadAttributes, - BuiltPayload = EthBuiltPayload, + BuiltPayload = TaikoBuiltPayload, >, Pool: TransactionPool + 'static, Validator: EngineApiValidator, diff --git a/crates/rpc/src/engine/builder.rs b/crates/rpc/src/engine/builder.rs index 1b960759..ac9367f4 100644 --- a/crates/rpc/src/engine/builder.rs +++ b/crates/rpc/src/engine/builder.rs @@ -1,16 +1,15 @@ //! Builder wiring for Taiko engine RPC module construction. use alloy_rpc_types_engine::ClientVersionV1; -use reth::payload::PayloadStore; use reth_engine_primitives::EngineApiValidator; use reth_node_api::{AddOnsContext, FullNodeComponents, NodeTypes}; use reth_node_builder::rpc::{EngineApiBuilder, PayloadValidatorBuilder}; use reth_node_core::version::{CLIENT_CODE, version_metadata}; +use reth_payload_builder::PayloadStore; use reth_rpc::EngineApi; use reth_rpc_engine_api::EngineCapabilities; use alethia_reth_chainspec::spec::TaikoChainSpec; -use alethia_reth_primitives::engine::TaikoEngineTypes; -use reth_ethereum::EthPrimitives; +use alethia_reth_primitives::{TaikoPrimitives, engine::TaikoEngineTypes}; use crate::engine::api::TaikoEngineApi; @@ -29,7 +28,7 @@ impl EngineApiBuilder for TaikoEngineApiBuilder where N: FullNodeComponents, N::Types: NodeTypes< - Primitives = EthPrimitives, + Primitives = TaikoPrimitives, ChainSpec = TaikoChainSpec, Payload = TaikoEngineTypes, >, diff --git a/crates/rpc/src/engine/validator.rs b/crates/rpc/src/engine/validator.rs index 4e5160ee..caa22574 100644 --- a/crates/rpc/src/engine/validator.rs +++ b/crates/rpc/src/engine/validator.rs @@ -2,16 +2,16 @@ use alethia_reth_block::config::TaikoEvmConfig; use alethia_reth_chainspec::spec::TaikoChainSpec; use alethia_reth_primitives::{ + TaikoBlock, TaikoPrimitives, engine::{TaikoEngineTypes, types::TaikoExecutionData}, payload::attributes::TaikoPayloadAttributes, }; use alloy_consensus::{BlockHeader, EMPTY_ROOT_HASH}; use alloy_rpc_types_engine::{ExecutionPayloadV1, PayloadError}; use alloy_rpc_types_eth::Withdrawals; -use reth::{chainspec::EthChainSpec, primitives::RecoveredBlock}; +use reth_chainspec::EthChainSpec; use reth_engine_primitives::EngineApiValidator; use reth_engine_tree::tree::{TreeConfig, payload_validator::BasicEngineValidator}; -use reth_ethereum::{Block, EthPrimitives}; use reth_evm::ConfigureEngineEvm; use reth_node_api::{ AddOnsContext, FullNodeComponents, NewPayloadError, NodeTypes, PayloadTypes, PayloadValidator, @@ -24,6 +24,7 @@ use reth_payload_primitives::{ EngineApiMessageVersion, EngineObjectValidationError, InvalidPayloadAttributesError, PayloadAttributes, PayloadOrAttributes, }; +use reth_primitives::RecoveredBlock; use reth_primitives_traits::{Block as BlockTrait, SealedBlock}; use std::sync::Arc; @@ -35,7 +36,7 @@ impl PayloadValidatorBuilder for TaikoEngineValidatorBuilder where N: FullNodeComponents, N::Types: NodeTypes< - Primitives = EthPrimitives, + Primitives = TaikoPrimitives, ChainSpec = TaikoChainSpec, Payload = TaikoEngineTypes, >, @@ -53,7 +54,7 @@ impl EngineValidatorBuilder for TaikoEngineValidatorBuilder where N: FullNodeComponents, N::Types: NodeTypes< - Primitives = EthPrimitives, + Primitives = TaikoPrimitives, ChainSpec = TaikoChainSpec, Payload = TaikoEngineTypes, >, @@ -104,7 +105,7 @@ where Types: PayloadTypes, { /// The block type used by the engine. - type Block = Block; + type Block = TaikoBlock; /// Converts the given payload into a sealed block without recovering signatures. fn convert_payload_to_block( @@ -117,6 +118,14 @@ where // First parse the block. let mut block = Into::::into(execution_payload).try_into_block()?; + + // ExecutionPayloadV1 doesn't have withdrawals field (pre-Shanghai) + // For post-Shanghai blocks, we must set withdrawals to empty array + // This ensures blocks cached during sync have correct withdrawals field + if block.body.withdrawals.is_none() { + block.body.withdrawals = Some(Default::default()); + } + if !taiko_sidecar.tx_hash.is_zero() { block.header.transactions_root = taiko_sidecar.tx_hash; } diff --git a/crates/rpc/src/eth/auth.rs b/crates/rpc/src/eth/auth.rs index 0b52663a..8ea869fd 100644 --- a/crates/rpc/src/eth/auth.rs +++ b/crates/rpc/src/eth/auth.rs @@ -8,23 +8,16 @@ use alloy_json_rpc::RpcObject; use alloy_primitives::{Bytes, U256}; use async_trait::async_trait; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; -use reth::{ - revm::primitives::Address, - transaction_pool::{PoolConsensusTx, PoolTransaction, TransactionPool}, -}; -use reth_db_api::{ - DatabaseError, - transaction::{DbTx, DbTxMut}, -}; -use reth_ethereum::{EthPrimitives, TransactionSigned}; +use reth_db::transaction::DbTx; +use reth_db_api::{DatabaseError, transaction::DbTxMut}; use reth_evm::ConfigureEngineEvm; -use reth_evm_ethereum::RethReceiptBuilder; use reth_node_api::{Block, NodePrimitives}; use reth_primitives_traits::BlockBody as _; use reth_provider::{BlockReaderIdExt, DBProvider, DatabaseProviderFactory, StateProviderFactory}; -use reth_revm::{State, database::StateProviderDatabase}; +use reth_revm::{State, database::StateProviderDatabase, primitives::Address}; use reth_rpc_eth_api::{RpcConvert, RpcTransaction}; use reth_rpc_eth_types::EthApiError; +use reth_transaction_pool::{PoolConsensusTx, PoolTransaction, TransactionPool}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -33,19 +26,21 @@ use alethia_reth_block::{ assembler::TaikoBlockAssembler, config::TaikoNextBlockEnvAttributes, factory::TaikoBlockExecutorFactory, + receipt_builder::TaikoReceiptBuilder, tx_selection::{ DEFAULT_DA_ZLIB_GUARD_BYTES, SelectionOutcome, TxSelectionConfig, select_and_execute_pool_transactions, }, }; use alethia_reth_chainspec::spec::TaikoChainSpec; -use alethia_reth_consensus::validation::ANCHOR_V4_SELECTOR; +use alethia_reth_consensus::{transaction::TaikoTxEnvelope, validation::ANCHOR_V4_SELECTOR}; use alethia_reth_db::model::{ BatchToLastBlock, STORED_L1_HEAD_ORIGIN_KEY, StoredL1HeadOriginTable, StoredL1OriginTable, }; use alethia_reth_evm::factory::TaikoEvmFactory; use alethia_reth_primitives::{ - decode_shasta_proposal_id, engine::types::TaikoExecutionData, payload::attributes::RpcL1Origin, + TaikoPrimitives, decode_shasta_proposal_id, engine::types::TaikoExecutionData, + payload::attributes::RpcL1Origin, }; /// A pre-built transaction list that contains the mempool content. @@ -395,7 +390,7 @@ where impl TaikoAuthExtApiServer> for TaikoAuthExt where - Pool: TransactionPool> + 'static, + Pool: TransactionPool> + 'static, Eth: RpcConvert>> + 'static, Provider: DatabaseProviderFactory + BlockReaderIdExt
@@ -403,10 +398,10 @@ where + 'static, Evm: ConfigureEngineEvm< TaikoExecutionData, - Primitives = EthPrimitives, + Primitives = TaikoPrimitives, NextBlockEnvCtx = TaikoNextBlockEnvAttributes, BlockExecutorFactory = TaikoBlockExecutorFactory< - RethReceiptBuilder, + TaikoReceiptBuilder, Arc, TaikoEvmFactory, >, @@ -631,6 +626,7 @@ mod tests { use std::{path::PathBuf, sync::Arc}; #[test] + #[cfg(feature = "serde")] /// Ensures `txPoolContent` accepts a camelCase object payload. fn tx_pool_content_params_deserialize_from_camel_case() { let value = serde_json::json!({ @@ -656,6 +652,7 @@ mod tests { } #[test] + #[cfg(feature = "serde")] /// Ensures `txPoolContentWithMinTip` accepts a camelCase object payload. fn tx_pool_content_with_min_tip_params_deserialize_from_camel_case() { let value = serde_json::json!({ @@ -717,7 +714,7 @@ mod tests { .with_default_tables() .build() .expect("failed to create test RocksDB provider"), - reth::tasks::Runtime::default(), + reth_tasks::Runtime::default(), ) .expect("failed to create test provider factory") } diff --git a/crates/rpc/src/eth/mod.rs b/crates/rpc/src/eth/mod.rs index 9c85a138..76a799e2 100644 --- a/crates/rpc/src/eth/mod.rs +++ b/crates/rpc/src/eth/mod.rs @@ -1,9 +1,6 @@ //! Taiko `eth` and `taikoAuth` namespace RPC extensions. /// Authenticated Taiko RPC methods and tx-pool helpers. pub mod auth; -/// Builder for Taiko `eth` API integration. -pub mod builder; -/// Error types and helpers for Taiko `eth` RPC methods. pub mod error; #[allow(clippy::module_inception)] /// Public Taiko `eth` namespace methods. diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index 70c3a111..fd6e7262 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -5,3 +5,6 @@ pub mod engine; /// Taiko `eth` namespace extensions and custom RPC methods. pub mod eth; + +/// Taiko RPC type conversions +pub mod converter; From 9b7809a3ab4cf45579c2940a67ccffca9ae2e880 Mon Sep 17 00:00:00 2001 From: Alexander Fedorovskyi Date: Wed, 18 Feb 2026 17:27:36 +0100 Subject: [PATCH 2/4] feat: prague activated --- Cargo.lock | 1 - crates/consensus/Cargo.toml | 3 +- crates/consensus/src/validation.rs | 118 +++++++++++++++++++++++++---- crates/evm/src/spec.rs | 4 +- 4 files changed, 108 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49e3478a..19d6d26b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,7 +185,6 @@ dependencies = [ "reth-consensus", "reth-consensus-common", "reth-db-api", - "reth-ethereum-consensus", "reth-ethereum-primitives", "reth-evm", "reth-primitives-traits", diff --git a/crates/consensus/Cargo.toml b/crates/consensus/Cargo.toml index 0535db00..44053c12 100644 --- a/crates/consensus/Cargo.toml +++ b/crates/consensus/Cargo.toml @@ -45,9 +45,9 @@ reth-codec = ["dep:reth-codecs", "dep:reth-db-api"] alethia-reth-chainspec = { path = "../chainspec", default-features = false } alethia-reth-evm = { path = "../evm" } alloy-consensus = { workspace = true } +alloy-eips = { workspace = true } alloy-hardforks = { workspace = true } alloy-rlp = { workspace = true } -alloy-eips = { workspace = true } alloy-network = { workspace = true, optional = true } alloy-primitives = { workspace = true } alloy-sol-types = { workspace = true } @@ -61,7 +61,6 @@ reth-consensus-common = { workspace = true } reth-codecs = { workspace = true, features = ["alloy"], optional = true } reth-chainspec = { workspace = true } reth-db-api = { workspace = true, optional = true } -reth-ethereum-consensus = { workspace = true } reth-ethereum-primitives = { workspace = true } reth-evm = { workspace = true } reth-primitives-traits = { workspace = true } diff --git a/crates/consensus/src/validation.rs b/crates/consensus/src/validation.rs index e27af7a4..1617a0bb 100644 --- a/crates/consensus/src/validation.rs +++ b/crates/consensus/src/validation.rs @@ -2,20 +2,22 @@ use std::{fmt::Debug, sync::Arc}; use alloy_consensus::{ - BlockHeader as AlloyBlockHeader, EMPTY_OMMER_ROOT_HASH, constants::MAXIMUM_EXTRA_DATA_SIZE, + BlockHeader as AlloyBlockHeader, EMPTY_OMMER_ROOT_HASH, TxReceipt, + constants::MAXIMUM_EXTRA_DATA_SIZE, proofs::calculate_receipt_root, }; -use alloy_primitives::{Address, B256, U256}; +use alloy_eips::{Encodable2718, eip7685::Requests}; +use alloy_hardforks::EthereumHardforks; +use alloy_primitives::{Address, B256, Bloom, Bytes, U256}; use alloy_sol_types::{SolCall, sol}; use reth_consensus::{Consensus, ConsensusError, FullConsensus, HeaderValidator, ReceiptRootBloom}; use reth_consensus_common::validation::{ validate_against_parent_hash_number, validate_body_against_header, validate_header_base_fee, validate_header_extra_data, validate_header_gas, }; -use reth_ethereum_consensus::validate_block_post_execution; use reth_evm::block::BlockExecutionResult; use reth_primitives_traits::{ - Block, BlockBody, BlockHeader, GotExpected, NodePrimitives, RecoveredBlock, SealedBlock, - SealedHeader, SignedTransaction, + Block, BlockBody, BlockHeader, GotExpected, NodePrimitives, Receipt, RecoveredBlock, + SealedBlock, SealedHeader, SignedTransaction, receipt::gas_spent_by_transactions, }; use crate::eip4396::{ @@ -97,15 +99,9 @@ where &self, block: &RecoveredBlock, result: &BlockExecutionResult, - receipt_root_bloom: Option, + _receipt_root_bloom: Option, ) -> Result<(), ConsensusError> { - validate_block_post_execution( - block, - &self.chain_spec, - &result.receipts, - &result.requests, - receipt_root_bloom, - )?; + validate_block_post_execution(block, &self.chain_spec, &result.receipts, &result.requests)?; validate_anchor_transaction_in_block::<::Block>( block, &self.chain_spec, @@ -395,6 +391,102 @@ fn validate_input_selector( Ok(()) } +/// Validate a block with regard to execution results: +/// +/// - Compares the receipts root in the block header to the block body +/// - Compares the gas used in the block header to the actual gas usage after execution +/// +/// Note: Taiko does not use requests hash validation, so that part is omitted. +pub fn validate_block_post_execution( + block: &RecoveredBlock, + chain_spec: &ChainSpec, + receipts: &[R], + _requests: &Requests, +) -> Result<(), ConsensusError> +where + B: Block, + R: Receipt, + ChainSpec: EthereumHardforks, +{ + // Check if gas used matches the value set in header. + let cumulative_gas_used = + receipts.last().map(|receipt| receipt.cumulative_gas_used()).unwrap_or(0); + if block.header().gas_used() != cumulative_gas_used { + return Err(ConsensusError::BlockGasUsed { + gas: GotExpected { got: cumulative_gas_used, expected: block.header().gas_used() }, + gas_spent_by_tx: gas_spent_by_transactions(receipts), + }) + } + + // Before Byzantium, receipts contained state root that would mean that expensive + // operation as hashing that is required 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 chain_spec.is_byzantium_active_at_block(block.header().number()) && + let Err(error) = verify_receipts( + block.header().receipts_root(), + block.header().logs_bloom(), + receipts, + ) + { + let receipts = receipts + .iter() + .map(|r| Bytes::from(r.with_bloom_ref().encoded_2718())) + .collect::>(); + tracing::debug!(%error, ?receipts, "receipts verification failed"); + return Err(error) + } + + Ok(()) +} + +/// Calculate the receipts root, and compare it against the expected receipts root and logs +/// bloom. +fn verify_receipts( + expected_receipts_root: B256, + expected_logs_bloom: Bloom, + receipts: &[R], +) -> Result<(), ConsensusError> { + // Calculate receipts root. + let receipts_with_bloom = receipts.iter().map(TxReceipt::with_bloom_ref).collect::>(); + let receipts_root = calculate_receipt_root(&receipts_with_bloom); + + // Calculate header logs bloom. + let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom_ref()); + + compare_receipts_root_and_logs_bloom( + receipts_root, + logs_bloom, + expected_receipts_root, + expected_logs_bloom, + )?; + + Ok(()) +} + +/// Compare the calculated receipts root with the expected receipts root, also compare +/// the calculated logs bloom with the expected logs bloom. +fn compare_receipts_root_and_logs_bloom( + calculated_receipts_root: B256, + calculated_logs_bloom: Bloom, + expected_receipts_root: B256, + expected_logs_bloom: Bloom, +) -> Result<(), ConsensusError> { + if calculated_receipts_root != expected_receipts_root { + return Err(ConsensusError::BodyReceiptRootDiff( + GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(), + )) + } + + if calculated_logs_bloom != expected_logs_bloom { + return Err(ConsensusError::BodyBloomLogDiff( + GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(), + )) + } + + Ok(()) +} + #[cfg(test)] mod test { use super::validate_input_selector; diff --git a/crates/evm/src/spec.rs b/crates/evm/src/spec.rs index 1e9043e2..6829dbfa 100644 --- a/crates/evm/src/spec.rs +++ b/crates/evm/src/spec.rs @@ -23,7 +23,7 @@ impl TaikoSpecId { /// Converts the [`TaikoSpecId`] into a [`SpecId`]. pub const fn into_eth_spec(self) -> SpecId { match self { - Self::GENESIS | Self::ONTAKE | Self::PACAYA | Self::SHASTA => SpecId::CANCUN, + Self::GENESIS | Self::ONTAKE | Self::PACAYA | Self::SHASTA => SpecId::PRAGUE, } } @@ -92,7 +92,7 @@ mod tests { (SpecId::MERGE, true), (SpecId::SHANGHAI, true), (SpecId::CANCUN, true), - (SpecId::default(), false), + (SpecId::PRAGUE, true), ], vec![ (TaikoSpecId::GENESIS, true), From 1862a1ffd0960cbab1e6a9f2bb90cb2610c8b8af Mon Sep 17 00:00:00 2001 From: Alexander Fedorovskyi Date: Wed, 18 Feb 2026 17:29:52 +0100 Subject: [PATCH 3/4] feat: osaka added (shnghai activated) --- Cargo.lock | 2 ++ crates/block/src/config.rs | 20 +++++++++++-------- crates/consensus/src/validation.rs | 27 +++++++++++++++++++++++--- crates/evm/src/context.rs | 4 ++-- crates/evm/src/spec.rs | 7 ++++--- crates/payload/Cargo.toml | 2 ++ crates/payload/src/builder.rs | 31 ++++++++++++++++++++++++++++++ 7 files changed, 77 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19d6d26b..3d86fd8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,6 +309,8 @@ dependencies = [ "op-alloy-flz", "reth-basic-payload-builder", "reth-chainspec", + "reth-consensus", + "reth-consensus-common", "reth-engine-local", "reth-errors", "reth-ethereum", diff --git a/crates/block/src/config.rs b/crates/block/src/config.rs index 1dfbe7c2..34fae5ae 100644 --- a/crates/block/src/config.rs +++ b/crates/block/src/config.rs @@ -4,6 +4,7 @@ use std::{borrow::Cow, sync::Arc}; use alloy_consensus::{BlockHeader, Header}; #[cfg(feature = "net")] use alloy_eips::Decodable2718; +use alloy_eips::eip7825::MAX_TX_GAS_LIMIT_OSAKA; use alloy_hardforks::EthereumHardforks; use alloy_primitives::Bytes; use alloy_rpc_types_eth::Withdrawals; @@ -115,10 +116,14 @@ impl ConfigureEvm for TaikoEvmConfig { /// Creates a new [`EvmEnv`] for the given header. fn evm_env(&self, header: &Header) -> Result, Self::Error> { - let cfg_env = CfgEnv::new() + let mut cfg_env = CfgEnv::new() .with_chain_id(self.chain_spec().inner.chain().id()) .with_spec_and_mainnet_gas_params(taiko_revm_spec(&self.chain_spec().inner, header)); + if self.chain_spec().is_osaka_active_at_timestamp(header.timestamp()) { + cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA); + } + let basefee: u64 = header .base_fee_per_gas() .ok_or_else(|| AnyError::new(MissingBaseFee { block_number: header.number() }))?; @@ -146,7 +151,7 @@ impl ConfigureEvm for TaikoEvmConfig { parent: &Header, attributes: &Self::NextBlockEnvCtx, ) -> Result, Self::Error> { - let cfg = CfgEnv::new() + let mut cfg = CfgEnv::new() .with_chain_id(self.chain_spec().inner.chain().id()) .with_spec_and_mainnet_gas_params(taiko_spec_by_timestamp_and_block_number( &self.chain_spec().inner, @@ -154,6 +159,10 @@ impl ConfigureEvm for TaikoEvmConfig { parent.number + 1, )); + if self.chain_spec().is_osaka_active_at_timestamp(attributes.timestamp) { + cfg.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA); + } + let block_env: BlockEnv = BlockEnv { number: U256::from(parent.number + 1), beneficiary: attributes.suggested_fee_recipient, @@ -215,7 +224,6 @@ impl ConfigureEngineEvm for TaikoEvmConfig { let timestamp = payload.timestamp(); let block_number = payload.block_number(); - let blob_params = self.chain_spec().blob_params_at_timestamp(timestamp); let spec = taiko_spec_by_timestamp_and_block_number(self.chain_spec(), timestamp, block_number); @@ -224,13 +232,9 @@ impl ConfigureEngineEvm for TaikoEvmConfig { .with_chain_id(self.chain_spec().chain().id()) .with_spec_and_mainnet_gas_params(spec); - if let Some(blob_params) = &blob_params { - cfg_env.set_max_blobs_per_tx(blob_params.max_blobs_per_tx); - } - if self.chain_spec().is_osaka_active_at_timestamp(timestamp) { cfg_env.tx_gas_limit_cap = - Some(reth_primitives_traits::constants::MAX_TX_GAS_LIMIT_OSAKA); + Some(MAX_TX_GAS_LIMIT_OSAKA); } let block_env = BlockEnv { diff --git a/crates/consensus/src/validation.rs b/crates/consensus/src/validation.rs index 1617a0bb..cd8d9165 100644 --- a/crates/consensus/src/validation.rs +++ b/crates/consensus/src/validation.rs @@ -3,7 +3,8 @@ use std::{fmt::Debug, sync::Arc}; use alloy_consensus::{ BlockHeader as AlloyBlockHeader, EMPTY_OMMER_ROOT_HASH, TxReceipt, - constants::MAXIMUM_EXTRA_DATA_SIZE, proofs::calculate_receipt_root, + constants::{EMPTY_WITHDRAWALS, MAXIMUM_EXTRA_DATA_SIZE}, + proofs::calculate_receipt_root, }; use alloy_eips::{Encodable2718, eip7685::Requests}; use alloy_hardforks::EthereumHardforks; @@ -11,8 +12,8 @@ use alloy_primitives::{Address, B256, Bloom, Bytes, U256}; use alloy_sol_types::{SolCall, sol}; use reth_consensus::{Consensus, ConsensusError, FullConsensus, HeaderValidator, ReceiptRootBloom}; use reth_consensus_common::validation::{ - validate_against_parent_hash_number, validate_body_against_header, validate_header_base_fee, - validate_header_extra_data, validate_header_gas, + MAX_RLP_BLOCK_SIZE, validate_against_parent_hash_number, validate_body_against_header, + validate_header_base_fee, validate_header_extra_data, validate_header_gas, }; use reth_evm::block::BlockExecutionResult; use reth_primitives_traits::{ @@ -136,6 +137,26 @@ impl Consensus for TaikoBeaconConsensus { )); } + // In Taiko network, withdrawals root is always empty. + if let Some(withdrawals_root) = block.withdrawals_root() { + if withdrawals_root != EMPTY_WITHDRAWALS { + return Err(ConsensusError::BodyWithdrawalsRootDiff( + GotExpected { got: withdrawals_root, expected: EMPTY_WITHDRAWALS }.into(), + )); + } + } else { + return Err(ConsensusError::WithdrawalsRootMissing); + } + + if self.chain_spec.is_osaka_active_at_timestamp(block.timestamp()) && + block.rlp_length() > MAX_RLP_BLOCK_SIZE + { + return Err(ConsensusError::BlockTooLarge { + rlp_length: block.rlp_length(), + max_rlp_length: MAX_RLP_BLOCK_SIZE, + }) + } + Ok(()) } } diff --git a/crates/evm/src/context.rs b/crates/evm/src/context.rs index 981f4121..b791ef1e 100644 --- a/crates/evm/src/context.rs +++ b/crates/evm/src/context.rs @@ -47,7 +47,7 @@ where type Context = Self; fn build_taiko_mainnet(self) -> TaikoEvm { - let spec = self.cfg.spec().clone(); + let spec = self.cfg.spec().to_owned(); TaikoEvm::new(Evm { ctx: self, inspector: (), @@ -61,7 +61,7 @@ where self, inspector: INSP, ) -> TaikoEvm { - let spec = self.cfg.spec().clone(); + let spec = self.cfg.spec().to_owned(); TaikoEvm::new(Evm { ctx: self, inspector, diff --git a/crates/evm/src/spec.rs b/crates/evm/src/spec.rs index 6829dbfa..e175ad9d 100644 --- a/crates/evm/src/spec.rs +++ b/crates/evm/src/spec.rs @@ -23,7 +23,7 @@ impl TaikoSpecId { /// Converts the [`TaikoSpecId`] into a [`SpecId`]. pub const fn into_eth_spec(self) -> SpecId { match self { - Self::GENESIS | Self::ONTAKE | Self::PACAYA | Self::SHASTA => SpecId::PRAGUE, + Self::GENESIS | Self::ONTAKE | Self::PACAYA | Self::SHASTA => SpecId::SHANGHAI, } } @@ -91,8 +91,9 @@ mod tests { vec![ (SpecId::MERGE, true), (SpecId::SHANGHAI, true), - (SpecId::CANCUN, true), - (SpecId::PRAGUE, true), + (SpecId::CANCUN, false), + (SpecId::PRAGUE, false), + (SpecId::OSAKA, false), ], vec![ (TaikoSpecId::GENESIS, true), diff --git a/crates/payload/Cargo.toml b/crates/payload/Cargo.toml index 80c37043..0a363158 100644 --- a/crates/payload/Cargo.toml +++ b/crates/payload/Cargo.toml @@ -31,6 +31,8 @@ reth-provider = { workspace = true } reth-payload-primitives = { workspace = true } op-alloy-flz = { workspace = true } reth-basic-payload-builder = { workspace = true } +reth-consensus = { workspace = true } +reth-consensus-common = { workspace = true } reth-chainspec = { workspace = true } reth-engine-local = { workspace = true } reth-errors = { workspace = true } diff --git a/crates/payload/src/builder.rs b/crates/payload/src/builder.rs index 3a54fc0a..9d366046 100644 --- a/crates/payload/src/builder.rs +++ b/crates/payload/src/builder.rs @@ -1,8 +1,11 @@ use alloy_consensus::Transaction; use alloy_eips::eip4844::BYTES_PER_BLOB; +use alloy_hardforks::EthereumHardforks; use reth_basic_payload_builder::{ BuildArguments, BuildOutcome, MissingPayloadBehaviour, PayloadBuilder, PayloadConfig, }; +use reth_consensus::ConsensusError; +use reth_consensus_common::validation::MAX_RLP_BLOCK_SIZE; use reth_errors::RethError; use reth_evm::{ ConfigureEvm, @@ -306,6 +309,10 @@ where debug!(target: "payload_builder", id=%attributes.payload_id(), parent_hash=?parent_header.hash(), parent_number=parent_header.number, "building new payload"); + // Check if Osaka hardfork is active + let chain_spec = client.chain_spec(); + let is_osaka = chain_spec.is_osaka_active_at_timestamp(attributes.timestamp()); + // Create block builder using the ConfigureEvm API let mut builder = evm_config .builder_for_next_block( @@ -322,6 +329,20 @@ where ) .map_err(PayloadBuilderError::other)?; + // Osaka: Pre-validate that all mandated transactions will fit within block size limit + if is_osaka && let Some(transactions) = &attributes.transactions { + let estimated_txs_size: usize = + transactions.iter().map(|tx| tx.inner().eip2718_encoded_length()).sum(); + let estimated_total_size = estimated_txs_size + 1024; // 1KB overhead for block header + + if estimated_total_size > MAX_RLP_BLOCK_SIZE { + return Err(PayloadBuilderError::other(ConsensusError::BlockTooLarge { + rlp_length: estimated_total_size, + max_rlp_length: MAX_RLP_BLOCK_SIZE, + })); + } + } + builder.apply_pre_execution_changes().map_err(PayloadBuilderError::other)?; // Get the base fee from the builder (already calculated based on attributes) @@ -368,6 +389,16 @@ where // Seal the block let sealed_block = Arc::new(block.sealed_block().clone()); + + // Final Osaka validation: ensure the sealed block doesn't exceed MAX_RLP_BLOCK_SIZE + // This is a safety check - the pre-execution validation should have caught this + if is_osaka && sealed_block.rlp_length() > MAX_RLP_BLOCK_SIZE { + return Err(PayloadBuilderError::other(ConsensusError::BlockTooLarge { + rlp_length: sealed_block.rlp_length(), + max_rlp_length: MAX_RLP_BLOCK_SIZE, + })); + } + debug!(target: "payload_builder", id=%attributes.payload_id(), sealed_block_header = ?sealed_block.sealed_header(), "sealed built block"); let payload = TaikoBuiltPayload::new(attributes.payload_id(), sealed_block, total_fees, None); From 786b3e744410cdd812397e10879163230946fc3d Mon Sep 17 00:00:00 2001 From: Alexander Fedorovskyi Date: Thu, 26 Feb 2026 19:02:28 +0100 Subject: [PATCH 4/4] fix: add Cancun, Prague and Osaka hardforks into chainspec for future --- crates/chainspec/src/hardfork.rs | 3 +++ crates/evm/src/alloy.rs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/crates/chainspec/src/hardfork.rs b/crates/chainspec/src/hardfork.rs index 2d4347b7..29b9fb2c 100644 --- a/crates/chainspec/src/hardfork.rs +++ b/crates/chainspec/src/hardfork.rs @@ -122,6 +122,9 @@ fn extend_with_shared_hardforks( }, ), (EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(0)), + // (EthereumHardfork::Cancun.boxed(), ForkCondition::Timestamp(1)), + // (EthereumHardfork::Prague.boxed(), ForkCondition::Timestamp(1)), + // (EthereumHardfork::Osaka.boxed(), ForkCondition::Timestamp(1)), ]; shared_hardforks.extend(hardforks); diff --git a/crates/evm/src/alloy.rs b/crates/evm/src/alloy.rs index d3178687..469dcfaa 100644 --- a/crates/evm/src/alloy.rs +++ b/crates/evm/src/alloy.rs @@ -205,6 +205,10 @@ where let mut gas_limit = tx.gas_limit; let mut basefee = 0; let mut disable_nonce_check = true; + // Temporarily override the EIP-7825 tx gas limit cap so the 30M system-call + // gas limit isn't rejected (the Osaka-era default cap is only 16M). + // `None` falls back to the spec default, so we must use an explicit value. + let mut tx_gas_limit_cap = Some(u64::MAX); // ensure the block gas limit is >= the tx core::mem::swap(&mut self.block.gas_limit, &mut gas_limit); @@ -212,6 +216,8 @@ where core::mem::swap(&mut self.block.basefee, &mut basefee); // disable the nonce check core::mem::swap(&mut self.cfg.disable_nonce_check, &mut disable_nonce_check); + // disable the tx gas limit cap for system calls + core::mem::swap(&mut self.cfg.tx_gas_limit_cap, &mut tx_gas_limit_cap); let mut res = self.transact(tx); @@ -221,6 +227,8 @@ where core::mem::swap(&mut self.block.basefee, &mut basefee); // swap back to the previous nonce check flag core::mem::swap(&mut self.cfg.disable_nonce_check, &mut disable_nonce_check); + // swap back to the previous tx gas limit cap + core::mem::swap(&mut self.cfg.tx_gas_limit_cap, &mut tx_gas_limit_cap); // NOTE: We assume that only the contract storage is modified. Revm currently marks the // caller and block beneficiary accounts as "touched" when we do the above transact calls,