mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: make payload builder generic over attributes type (#5948)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
256
Cargo.lock
generated
256
Cargo.lock
generated
@ -93,7 +93,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom 0.2.11",
|
||||
"getrandom 0.2.12",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
@ -182,7 +182,7 @@ dependencies = [
|
||||
"derive_arbitrary",
|
||||
"derive_more",
|
||||
"ethereum_ssz",
|
||||
"getrandom 0.2.11",
|
||||
"getrandom 0.2.12",
|
||||
"hex-literal",
|
||||
"itoa",
|
||||
"keccak-asm",
|
||||
@ -726,9 +726,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.5"
|
||||
version = "0.21.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
||||
checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
@ -1305,9 +1305,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.13"
|
||||
version = "4.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642"
|
||||
checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -1315,9 +1315,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.12"
|
||||
version = "4.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
|
||||
checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -1409,7 +1409,7 @@ version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"base64 0.21.6",
|
||||
"bech32",
|
||||
"bs58",
|
||||
"digest 0.10.7",
|
||||
@ -1464,14 +1464,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.7"
|
||||
version = "0.15.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
|
||||
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1618,44 +1618,37 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.10"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2"
|
||||
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.17"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.18"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
@ -2323,7 +2316,7 @@ version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe81b5c06ecfdbc71dd845216f225f53b62a10cb8a16c946836a3467f701d05b"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"base64 0.21.6",
|
||||
"bytes",
|
||||
"ed25519-dalek",
|
||||
"hex",
|
||||
@ -2531,7 +2524,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.48",
|
||||
"toml 0.8.2",
|
||||
"toml 0.8.8",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@ -2566,7 +2559,7 @@ dependencies = [
|
||||
"ethabi",
|
||||
"generic-array",
|
||||
"k256",
|
||||
"num_enum 0.7.1",
|
||||
"num_enum 0.7.2",
|
||||
"once_cell",
|
||||
"open-fastrlp",
|
||||
"rand 0.8.5",
|
||||
@ -2615,7 +2608,7 @@ checksum = "25d6c0c9455d93d4990c06e049abf9b30daf148cf461ee939c11d88907c60816"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"auto_impl",
|
||||
"base64 0.21.5",
|
||||
"base64 0.21.6",
|
||||
"bytes",
|
||||
"const-hex",
|
||||
"enr",
|
||||
@ -2989,9 +2982,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@ -4007,7 +4000,7 @@ version = "8.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"base64 0.21.6",
|
||||
"pem",
|
||||
"ring 0.16.20",
|
||||
"serde",
|
||||
@ -4017,9 +4010,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "k256"
|
||||
version = "0.13.2"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b"
|
||||
checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ecdsa",
|
||||
@ -4065,9 +4058,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.151"
|
||||
version = "0.2.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "libffi"
|
||||
@ -4289,7 +4282,7 @@ version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d4fa7ce7c4862db464a37b0b31d89bca874562f034bd7993895572783d02950"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"base64 0.21.6",
|
||||
"hyper",
|
||||
"indexmap 1.9.3",
|
||||
"ipnet",
|
||||
@ -4618,11 +4611,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0"
|
||||
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||
dependencies = [
|
||||
"num_enum_derive 0.7.1",
|
||||
"num_enum_derive 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4639,11 +4632,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e"
|
||||
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||
dependencies = [
|
||||
"proc-macro-crate 2.0.1",
|
||||
"proc-macro-crate 3.0.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
@ -4786,7 +4779,7 @@ version = "3.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b"
|
||||
dependencies = [
|
||||
"proc-macro-crate 2.0.1",
|
||||
"proc-macro-crate 2.0.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
@ -5219,12 +5212,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "2.0.1"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a"
|
||||
checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8"
|
||||
dependencies = [
|
||||
"toml_datetime",
|
||||
"toml_edit 0.20.2",
|
||||
"toml_edit 0.20.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b2685dd208a3771337d8d386a89840f0f43cd68be8dae90a5f8c2384effc9cd"
|
||||
dependencies = [
|
||||
"toml_edit 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5453,7 +5454,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.11",
|
||||
"getrandom 0.2.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5527,7 +5528,7 @@ version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
|
||||
dependencies = [
|
||||
"getrandom 0.2.11",
|
||||
"getrandom 0.2.12",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
@ -5592,7 +5593,7 @@ version = "0.11.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"base64 0.21.6",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
@ -5685,6 +5686,8 @@ dependencies = [
|
||||
"reth-network",
|
||||
"reth-network-api",
|
||||
"reth-nippy-jar",
|
||||
"reth-node-api",
|
||||
"reth-node-builder",
|
||||
"reth-optimism-payload-builder",
|
||||
"reth-payload-builder",
|
||||
"reth-payload-validator",
|
||||
@ -5712,7 +5715,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"toml 0.8.2",
|
||||
"toml 0.8.8",
|
||||
"tracing",
|
||||
"tui",
|
||||
"vergen",
|
||||
@ -5729,6 +5732,7 @@ dependencies = [
|
||||
"reth",
|
||||
"reth-beacon-consensus",
|
||||
"reth-interfaces",
|
||||
"reth-node-api",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
"reth-revm",
|
||||
@ -5751,6 +5755,7 @@ dependencies = [
|
||||
"metrics",
|
||||
"reth-interfaces",
|
||||
"reth-metrics",
|
||||
"reth-node-api",
|
||||
"reth-payload-builder",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
@ -5776,6 +5781,8 @@ dependencies = [
|
||||
"reth-downloaders",
|
||||
"reth-interfaces",
|
||||
"reth-metrics",
|
||||
"reth-node-api",
|
||||
"reth-node-builder",
|
||||
"reth-payload-builder",
|
||||
"reth-payload-validator",
|
||||
"reth-primitives",
|
||||
@ -5855,7 +5862,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"toml 0.8.2",
|
||||
"toml 0.8.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6288,11 +6295,34 @@ dependencies = [
|
||||
"zstd 0.12.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-node-api"
|
||||
version = "0.1.0-alpha.13"
|
||||
dependencies = [
|
||||
"reth-payload-builder",
|
||||
"reth-primitives",
|
||||
"reth-rpc-types",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-node-builder"
|
||||
version = "0.1.0-alpha.13"
|
||||
dependencies = [
|
||||
"reth-node-api",
|
||||
"reth-payload-builder",
|
||||
"reth-primitives",
|
||||
"reth-rpc-types",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-optimism-payload-builder"
|
||||
version = "0.1.0-alpha.14"
|
||||
dependencies = [
|
||||
"reth-basic-payload-builder",
|
||||
"reth-node-api",
|
||||
"reth-payload-builder",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
@ -6308,10 +6338,12 @@ name = "reth-payload-builder"
|
||||
version = "0.1.0-alpha.14"
|
||||
dependencies = [
|
||||
"alloy-rlp",
|
||||
"async-trait",
|
||||
"futures-util",
|
||||
"metrics",
|
||||
"reth-interfaces",
|
||||
"reth-metrics",
|
||||
"reth-node-api",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
"reth-rpc-types",
|
||||
@ -6320,6 +6352,7 @@ dependencies = [
|
||||
"reth-transaction-pool",
|
||||
"revm",
|
||||
"revm-primitives",
|
||||
"serde",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@ -6356,7 +6389,7 @@ dependencies = [
|
||||
"hash-db",
|
||||
"itertools 0.11.0",
|
||||
"modular-bitfield",
|
||||
"num_enum 0.7.1",
|
||||
"num_enum 0.7.2",
|
||||
"nybbles",
|
||||
"once_cell",
|
||||
"plain_hasher",
|
||||
@ -6380,7 +6413,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"test-fuzz",
|
||||
"thiserror",
|
||||
"toml 0.8.2",
|
||||
"toml 0.8.8",
|
||||
"tracing",
|
||||
"triehash",
|
||||
"zstd 0.12.4",
|
||||
@ -6510,6 +6543,8 @@ name = "reth-rpc-api"
|
||||
version = "0.1.0-alpha.14"
|
||||
dependencies = [
|
||||
"jsonrpsee",
|
||||
"reth-node-api",
|
||||
"reth-payload-builder",
|
||||
"reth-primitives",
|
||||
"reth-rpc-types",
|
||||
"serde_json",
|
||||
@ -6542,6 +6577,8 @@ dependencies = [
|
||||
"reth-ipc",
|
||||
"reth-metrics",
|
||||
"reth-network-api",
|
||||
"reth-node-api",
|
||||
"reth-node-builder",
|
||||
"reth-payload-builder",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
@ -6576,6 +6613,8 @@ dependencies = [
|
||||
"reth-beacon-consensus",
|
||||
"reth-interfaces",
|
||||
"reth-metrics",
|
||||
"reth-node-api",
|
||||
"reth-node-builder",
|
||||
"reth-payload-builder",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
@ -6890,7 +6929,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"getrandom 0.2.11",
|
||||
"getrandom 0.2.12",
|
||||
"libc",
|
||||
"spin 0.9.8",
|
||||
"untrusted 0.9.0",
|
||||
@ -7074,7 +7113,7 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"base64 0.21.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7399,7 +7438,7 @@ version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"base64 0.21.6",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
@ -8191,21 +8230,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.2"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.20.2",
|
||||
"toml_edit 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -8223,9 +8262,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.20.2"
|
||||
version = "0.20.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||
checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
|
||||
dependencies = [
|
||||
"indexmap 2.1.0",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||
dependencies = [
|
||||
"indexmap 2.1.0",
|
||||
"serde",
|
||||
@ -8262,7 +8312,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"base64 0.21.5",
|
||||
"base64 0.21.6",
|
||||
"bitflags 2.4.1",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@ -8728,7 +8778,7 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.11",
|
||||
"getrandom 0.2.12",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -8738,7 +8788,7 @@ version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
|
||||
dependencies = [
|
||||
"getrandom 0.2.11",
|
||||
"getrandom 0.2.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -8979,15 +9029,6 @@ dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@ -9006,21 +9047,6 @@ dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
@ -9051,12 +9077,6 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@ -9069,12 +9089,6 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
@ -9087,12 +9101,6 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
@ -9105,12 +9113,6 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
@ -9123,12 +9125,6 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
@ -9141,12 +9137,6 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@ -9159,12 +9149,6 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
|
||||
@ -36,6 +36,8 @@ members = [
|
||||
"crates/rpc/rpc-testing-util/",
|
||||
"crates/rpc/rpc-types/",
|
||||
"crates/rpc/rpc-types-compat/",
|
||||
"crates/node-builder/",
|
||||
"crates/node-api/",
|
||||
"crates/snapshot/",
|
||||
"crates/stages/",
|
||||
"crates/storage/codecs/",
|
||||
@ -112,6 +114,8 @@ reth-consensus-common = { path = "crates/consensus/common" }
|
||||
reth-db = { path = "crates/storage/db" }
|
||||
reth-discv4 = { path = "crates/net/discv4" }
|
||||
reth-dns-discovery = { path = "crates/net/dns" }
|
||||
reth-node-builder = { path = "crates/node-builder" }
|
||||
reth-node-api = { path = "crates/node-api" }
|
||||
reth-downloaders = { path = "crates/net/downloaders" }
|
||||
reth-ecies = { path = "crates/net/ecies" }
|
||||
reth-eth-wire = { path = "crates/net/eth-wire" }
|
||||
|
||||
@ -54,6 +54,8 @@ reth-prune.workspace = true
|
||||
reth-snapshot = { workspace = true, features = ["clap"] }
|
||||
reth-trie.workspace = true
|
||||
reth-nippy-jar.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
reth-node-builder.workspace = true
|
||||
|
||||
# crypto
|
||||
alloy-rlp.workspace = true
|
||||
|
||||
@ -18,6 +18,7 @@ use clap::{
|
||||
};
|
||||
use futures::TryFutureExt;
|
||||
use reth_network_api::{NetworkInfo, Peers};
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_provider::{
|
||||
AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader,
|
||||
EvmEnvProvider, HeaderProvider, StateProviderFactory,
|
||||
@ -224,7 +225,7 @@ impl RpcServerArgs {
|
||||
/// Returns the handles for the launched regular RPC server(s) (if any) and the server handle
|
||||
/// for the auth server that handles the `engine_` API that's accessed by the consensus
|
||||
/// layer.
|
||||
pub async fn start_servers<Reth, Engine, Conf>(
|
||||
pub async fn start_servers<Reth, Engine, Conf, EngineT: EngineTypes>(
|
||||
&self,
|
||||
components: &Reth,
|
||||
engine_api: Engine,
|
||||
@ -233,7 +234,7 @@ impl RpcServerArgs {
|
||||
) -> eyre::Result<RethRpcServerHandles>
|
||||
where
|
||||
Reth: RethNodeComponents,
|
||||
Engine: EngineApiServer,
|
||||
Engine: EngineApiServer<EngineT>,
|
||||
Conf: RethNodeCommandConfig,
|
||||
{
|
||||
let auth_config = self.auth_server_config(jwt_secret)?;
|
||||
@ -322,13 +323,13 @@ impl RpcServerArgs {
|
||||
}
|
||||
|
||||
/// Create Engine API server.
|
||||
pub async fn start_auth_server<Provider, Pool, Network, Tasks>(
|
||||
pub async fn start_auth_server<Provider, Pool, Network, Tasks, EngineT>(
|
||||
&self,
|
||||
provider: Provider,
|
||||
pool: Pool,
|
||||
network: Network,
|
||||
executor: Tasks,
|
||||
engine_api: EngineApi<Provider>,
|
||||
engine_api: EngineApi<Provider, EngineT>,
|
||||
jwt_secret: JwtSecret,
|
||||
) -> Result<AuthServerHandle, RpcError>
|
||||
where
|
||||
@ -343,6 +344,7 @@ impl RpcServerArgs {
|
||||
Pool: TransactionPool + Clone + 'static,
|
||||
Network: NetworkInfo + Peers + Clone + 'static,
|
||||
Tasks: TaskSpawner + Clone + 'static,
|
||||
EngineT: EngineTypes + 'static,
|
||||
{
|
||||
let socket_address = SocketAddr::new(self.auth_addr, self.auth_port);
|
||||
|
||||
|
||||
@ -59,6 +59,11 @@ use reth_interfaces::{
|
||||
};
|
||||
use reth_network::{NetworkBuilder, NetworkConfig, NetworkEvents, NetworkHandle, NetworkManager};
|
||||
use reth_network_api::{NetworkInfo, PeersInfo};
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
use reth_node_builder::EthEngineTypes;
|
||||
#[cfg(feature = "optimism")]
|
||||
use reth_node_builder::OptimismEngineTypes;
|
||||
use reth_payload_builder::PayloadBuilderHandle;
|
||||
use reth_primitives::{
|
||||
constants::eip4844::{LoadKzgSettingsError, MAINNET_KZG_TRUSTED_SETUP},
|
||||
kzg::KzgSettings,
|
||||
@ -1109,8 +1114,24 @@ impl<DB: Database + DatabaseMetrics + DatabaseMetadata + 'static> NodeBuilderWit
|
||||
ext.on_components_initialized(&components)?;
|
||||
|
||||
debug!(target: "reth::cli", "Spawning payload builder service");
|
||||
let payload_builder =
|
||||
ext.spawn_payload_builder_service(&self.config.builder, &components)?;
|
||||
|
||||
// TODO: stateful node builder should handle this in with_payload_builder
|
||||
// Optimism's payload builder is implemented on the OptimismPayloadBuilder type.
|
||||
#[cfg(feature = "optimism")]
|
||||
let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default()
|
||||
.set_compute_pending_block(self.config.builder.compute_pending_block);
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
let payload_builder: PayloadBuilderHandle<OptimismEngineTypes> =
|
||||
ext.spawn_payload_builder_service(&self.config.builder, &components, payload_builder)?;
|
||||
|
||||
// The default payload builder is implemented on the unit type.
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default();
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
let payload_builder: PayloadBuilderHandle<EthEngineTypes> =
|
||||
ext.spawn_payload_builder_service(&self.config.builder, &components, payload_builder)?;
|
||||
|
||||
let (consensus_engine_tx, mut consensus_engine_rx) = unbounded_channel();
|
||||
if let Some(store_path) = self.config.debug.engine_api_store.clone() {
|
||||
@ -1279,7 +1300,7 @@ impl<DB: Database + DatabaseMetrics + DatabaseMetadata + 'static> NodeBuilderWit
|
||||
#[cfg(feature = "optimism")]
|
||||
if self.config.chain.is_optimism() && !self.config.rollup.enable_genesis_walkback {
|
||||
let client = rpc_server_handles.auth.http_client();
|
||||
reth_rpc_api::EngineApiClient::fork_choice_updated_v2(
|
||||
reth_rpc_api::EngineApiClient::<OptimismEngineTypes>::fork_choice_updated_v2(
|
||||
&client,
|
||||
reth_rpc_types::engine::ForkchoiceState {
|
||||
head_block_hash: head.hash,
|
||||
|
||||
@ -5,7 +5,10 @@ use crate::cli::{
|
||||
config::{PayloadBuilderConfig, RethNetworkConfig, RethRpcConfig},
|
||||
};
|
||||
use clap::Args;
|
||||
use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig};
|
||||
use reth_basic_payload_builder::{
|
||||
BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig, PayloadBuilder,
|
||||
};
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService};
|
||||
use reth_provider::CanonStateSubscriptions;
|
||||
use reth_tasks::TaskSpawner;
|
||||
@ -125,14 +128,22 @@ pub trait RethNodeCommandConfig: fmt::Debug {
|
||||
///
|
||||
/// By default this spawns a [BasicPayloadJobGenerator] with the default configuration
|
||||
/// [BasicPayloadJobGeneratorConfig].
|
||||
fn spawn_payload_builder_service<Conf, Reth>(
|
||||
fn spawn_payload_builder_service<Conf, Reth, Builder, Engine>(
|
||||
&mut self,
|
||||
conf: &Conf,
|
||||
components: &Reth,
|
||||
) -> eyre::Result<PayloadBuilderHandle>
|
||||
payload_builder: Builder,
|
||||
) -> eyre::Result<PayloadBuilderHandle<Engine>>
|
||||
where
|
||||
Conf: PayloadBuilderConfig,
|
||||
Reth: RethNodeComponents,
|
||||
Engine: EngineTypes + 'static,
|
||||
Builder: PayloadBuilder<
|
||||
Reth::Pool,
|
||||
Reth::Provider,
|
||||
Attributes = Engine::PayloadBuilderAttributes,
|
||||
> + Unpin
|
||||
+ 'static,
|
||||
{
|
||||
let payload_job_config = BasicPayloadJobGeneratorConfig::default()
|
||||
.interval(conf.interval())
|
||||
@ -145,15 +156,6 @@ pub trait RethNodeCommandConfig: fmt::Debug {
|
||||
#[cfg(feature = "optimism")]
|
||||
let payload_job_config = payload_job_config.extradata(Default::default());
|
||||
|
||||
// The default payload builder is implemented on the unit type.
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default();
|
||||
|
||||
// Optimism's payload builder is implemented on the OptimismPayloadBuilder type.
|
||||
#[cfg(feature = "optimism")]
|
||||
let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default()
|
||||
.set_compute_pending_block(conf.compute_pending_block());
|
||||
|
||||
let payload_generator = BasicPayloadJobGenerator::with_builder(
|
||||
components.provider(),
|
||||
components.pool(),
|
||||
@ -315,18 +317,26 @@ impl<T: RethNodeCommandConfig> RethNodeCommandConfig for NoArgs<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_payload_builder_service<Conf, Reth>(
|
||||
fn spawn_payload_builder_service<Conf, Reth, Builder, Engine>(
|
||||
&mut self,
|
||||
conf: &Conf,
|
||||
components: &Reth,
|
||||
) -> eyre::Result<PayloadBuilderHandle>
|
||||
payload_builder: Builder,
|
||||
) -> eyre::Result<PayloadBuilderHandle<Engine>>
|
||||
where
|
||||
Conf: PayloadBuilderConfig,
|
||||
Reth: RethNodeComponents,
|
||||
Engine: EngineTypes + 'static,
|
||||
Builder: PayloadBuilder<
|
||||
Reth::Pool,
|
||||
Reth::Provider,
|
||||
Attributes = Engine::PayloadBuilderAttributes,
|
||||
> + Unpin
|
||||
+ 'static,
|
||||
{
|
||||
self.inner_mut()
|
||||
.ok_or_else(|| eyre::eyre!("config value must be set"))?
|
||||
.spawn_payload_builder_service(conf, components)
|
||||
.spawn_payload_builder_service(conf, components, payload_builder)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,10 @@ use reth_blockchain_tree::{
|
||||
};
|
||||
use reth_db::{init_db, DatabaseEnv};
|
||||
use reth_interfaces::{consensus::Consensus, RethResult};
|
||||
use reth_payload_builder::{database::CachedReads, PayloadBuilderAttributes};
|
||||
use reth_node_api::PayloadBuilderAttributes;
|
||||
use reth_payload_builder::database::CachedReads;
|
||||
#[cfg(feature = "optimism")]
|
||||
use reth_payload_builder::OptimismPayloadBuilderAttributes;
|
||||
use reth_primitives::{
|
||||
constants::eip4844::{LoadKzgSettingsError, MAINNET_KZG_TRUSTED_SETUP},
|
||||
fs,
|
||||
@ -34,6 +37,8 @@ use reth_provider::{
|
||||
ProviderFactory, StageCheckpointReader, StateProviderFactory,
|
||||
};
|
||||
use reth_revm::EvmProcessorFactory;
|
||||
#[cfg(feature = "optimism")]
|
||||
use reth_rpc_types::engine::OptimismPayloadAttributes;
|
||||
use reth_rpc_types::engine::{BlobsBundleV1, PayloadAttributes};
|
||||
use reth_transaction_pool::{
|
||||
blobstore::InMemoryBlobStore, BlobStore, EthPooledTransaction, PoolConfig, TransactionOrigin,
|
||||
@ -42,6 +47,9 @@ use reth_transaction_pool::{
|
||||
use std::{path::PathBuf, str::FromStr, sync::Arc};
|
||||
use tracing::*;
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
use reth_payload_builder::EthPayloadBuilderAttributes;
|
||||
|
||||
/// `reth debug build-block` command
|
||||
/// This debug routine requires that the node is positioned at the block before the target.
|
||||
/// The script will then parse the block and attempt to build a similar one.
|
||||
@ -235,16 +243,31 @@ impl Command {
|
||||
suggested_fee_recipient: self.suggested_fee_recipient,
|
||||
// TODO: add support for withdrawals
|
||||
withdrawals: None,
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism_payload_attributes: reth_rpc_types::engine::OptimismPayloadAttributes::default(
|
||||
),
|
||||
};
|
||||
#[cfg(feature = "optimism")]
|
||||
let payload_config = PayloadConfig::new(
|
||||
Arc::clone(&best_block),
|
||||
Bytes::default(),
|
||||
PayloadBuilderAttributes::try_new(best_block.hash, payload_attrs)?,
|
||||
OptimismPayloadBuilderAttributes::try_new(
|
||||
best_block.hash,
|
||||
OptimismPayloadAttributes {
|
||||
payload_attributes: payload_attrs,
|
||||
transactions: None,
|
||||
no_tx_pool: None,
|
||||
gas_limit: None,
|
||||
},
|
||||
)?,
|
||||
self.chain.clone(),
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
let payload_config = PayloadConfig::new(
|
||||
Arc::clone(&best_block),
|
||||
Bytes::default(),
|
||||
EthPayloadBuilderAttributes::try_new(best_block.hash, payload_attrs)?,
|
||||
self.chain.clone(),
|
||||
);
|
||||
|
||||
let args = BuildArguments::new(
|
||||
blockchain_db.clone(),
|
||||
transaction_pool,
|
||||
|
||||
@ -21,7 +21,12 @@ use reth_db::{init_db, DatabaseEnv};
|
||||
use reth_interfaces::consensus::Consensus;
|
||||
use reth_network::NetworkHandle;
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_payload_builder::PayloadBuilderService;
|
||||
use reth_node_api::EngineTypes;
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
use reth_node_builder::EthEngineTypes;
|
||||
#[cfg(feature = "optimism")]
|
||||
use reth_node_builder::OptimismEngineTypes;
|
||||
use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService};
|
||||
use reth_primitives::{
|
||||
fs::{self},
|
||||
ChainSpec,
|
||||
@ -29,7 +34,7 @@ use reth_primitives::{
|
||||
use reth_provider::{providers::BlockchainProvider, CanonStateSubscriptions, ProviderFactory};
|
||||
use reth_revm::EvmProcessorFactory;
|
||||
use reth_rpc_types::{
|
||||
engine::{CancunPayloadFields, ForkchoiceState, PayloadAttributes},
|
||||
engine::{CancunPayloadFields, ForkchoiceState},
|
||||
ExecutionPayload,
|
||||
};
|
||||
use reth_stages::Pipeline;
|
||||
@ -175,8 +180,17 @@ impl Command {
|
||||
self.chain.clone(),
|
||||
payload_builder,
|
||||
);
|
||||
let (payload_service, payload_builder) =
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
let (payload_service, payload_builder): (
|
||||
_,
|
||||
PayloadBuilderHandle<OptimismEngineTypes>,
|
||||
) = PayloadBuilderService::new(payload_generator, blockchain_db.canonical_state_stream());
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
let (payload_service, payload_builder): (_, PayloadBuilderHandle<EthEngineTypes>) =
|
||||
PayloadBuilderService::new(payload_generator, blockchain_db.canonical_state_stream());
|
||||
|
||||
ctx.task_executor.spawn_critical("payload builder service", Box::pin(payload_service));
|
||||
|
||||
// Configure the consensus engine
|
||||
@ -245,8 +259,8 @@ impl Command {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum StoredEngineApiMessage {
|
||||
ForkchoiceUpdated { state: ForkchoiceState, payload_attrs: Option<PayloadAttributes> },
|
||||
enum StoredEngineApiMessage<Attributes> {
|
||||
ForkchoiceUpdated { state: ForkchoiceState, payload_attrs: Option<Attributes> },
|
||||
NewPayload { payload: ExecutionPayload, cancun_fields: Option<CancunPayloadFields> },
|
||||
}
|
||||
|
||||
@ -260,7 +274,14 @@ impl EngineApiStore {
|
||||
Self { path }
|
||||
}
|
||||
|
||||
fn on_message(&self, msg: &BeaconEngineMessage, received_at: SystemTime) -> eyre::Result<()> {
|
||||
fn on_message<Engine>(
|
||||
&self,
|
||||
msg: &BeaconEngineMessage<Engine>,
|
||||
received_at: SystemTime,
|
||||
) -> eyre::Result<()>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
fs::create_dir_all(&self.path)?; // ensure that store path had been created
|
||||
let timestamp = received_at.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis();
|
||||
match msg {
|
||||
@ -278,10 +299,12 @@ impl EngineApiStore {
|
||||
let filename = format!("{}-new_payload-{}.json", timestamp, payload.block_hash());
|
||||
fs::write(
|
||||
self.path.join(filename),
|
||||
serde_json::to_vec(&StoredEngineApiMessage::NewPayload {
|
||||
payload: payload.clone(),
|
||||
cancun_fields: cancun_fields.clone(),
|
||||
})?,
|
||||
serde_json::to_vec(
|
||||
&StoredEngineApiMessage::<Engine::PayloadAttributes>::NewPayload {
|
||||
payload: payload.clone(),
|
||||
cancun_fields: cancun_fields.clone(),
|
||||
},
|
||||
)?,
|
||||
)?;
|
||||
}
|
||||
// noop
|
||||
@ -310,11 +333,14 @@ impl EngineApiStore {
|
||||
Ok(filenames_by_ts.into_iter().flat_map(|(_, paths)| paths))
|
||||
}
|
||||
|
||||
pub(crate) async fn intercept(
|
||||
pub(crate) async fn intercept<Engine>(
|
||||
self,
|
||||
mut rx: UnboundedReceiver<BeaconEngineMessage>,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage>,
|
||||
) {
|
||||
mut rx: UnboundedReceiver<BeaconEngineMessage<Engine>>,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
||||
) where
|
||||
Engine: EngineTypes,
|
||||
BeaconEngineMessage<Engine>: std::fmt::Debug,
|
||||
{
|
||||
loop {
|
||||
let Some(msg) = rx.recv().await else { break };
|
||||
if let Err(error) = self.on_message(&msg, SystemTime::now()) {
|
||||
|
||||
@ -20,6 +20,7 @@ reth-provider.workspace = true
|
||||
reth-stages.workspace = true
|
||||
reth-revm.workspace = true
|
||||
reth-transaction-pool.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
# async
|
||||
futures-util.workspace = true
|
||||
|
||||
@ -19,6 +19,7 @@ use reth_interfaces::{
|
||||
consensus::{Consensus, ConsensusError},
|
||||
executor::{BlockExecutionError, BlockValidationError},
|
||||
};
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_primitives::{
|
||||
constants::{EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, ETHEREUM_BLOCK_GAS_LIMIT},
|
||||
proofs, Block, BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, BlockWithSenders, Bloom,
|
||||
@ -93,28 +94,30 @@ impl Consensus for AutoSealConsensus {
|
||||
|
||||
/// Builder type for configuring the setup
|
||||
#[derive(Debug)]
|
||||
pub struct AutoSealBuilder<Client, Pool> {
|
||||
pub struct AutoSealBuilder<Client, Pool, Engine: EngineTypes> {
|
||||
client: Client,
|
||||
consensus: AutoSealConsensus,
|
||||
pool: Pool,
|
||||
mode: MiningMode,
|
||||
storage: Storage,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage>,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
||||
canon_state_notification: CanonStateNotificationSender,
|
||||
}
|
||||
|
||||
// === impl AutoSealBuilder ===
|
||||
|
||||
impl<Client, Pool: TransactionPool> AutoSealBuilder<Client, Pool>
|
||||
impl<Client, Pool, Engine> AutoSealBuilder<Client, Pool, Engine>
|
||||
where
|
||||
Client: BlockReaderIdExt,
|
||||
Pool: TransactionPool,
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
/// Creates a new builder instance to configure all parts.
|
||||
pub fn new(
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
client: Client,
|
||||
pool: Pool,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage>,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
||||
canon_state_notification: CanonStateNotificationSender,
|
||||
mode: MiningMode,
|
||||
) -> Self {
|
||||
@ -143,7 +146,7 @@ where
|
||||
|
||||
/// Consumes the type and returns all components
|
||||
#[track_caller]
|
||||
pub fn build(self) -> (AutoSealConsensus, AutoSealClient, MiningTask<Client, Pool>) {
|
||||
pub fn build(self) -> (AutoSealConsensus, AutoSealClient, MiningTask<Client, Pool, Engine>) {
|
||||
let Self { client, consensus, pool, mode, storage, to_engine, canon_state_notification } =
|
||||
self;
|
||||
let auto_client = AutoSealClient::new(storage.clone());
|
||||
|
||||
@ -2,6 +2,7 @@ use crate::{mode::MiningMode, Storage};
|
||||
use futures_util::{future::BoxFuture, FutureExt};
|
||||
use reth_beacon_consensus::{BeaconEngineMessage, ForkchoiceStatus};
|
||||
use reth_interfaces::consensus::ForkchoiceState;
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_primitives::{Block, ChainSpec, IntoRecoveredTransaction, SealedBlockWithSenders};
|
||||
use reth_provider::{CanonChainTracker, CanonStateNotificationSender, Chain, StateProviderFactory};
|
||||
use reth_stages::PipelineEvent;
|
||||
@ -18,7 +19,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
/// A Future that listens for new ready transactions and puts new blocks into storage
|
||||
pub struct MiningTask<Client, Pool: TransactionPool> {
|
||||
pub struct MiningTask<Client, Pool: TransactionPool, Engine: EngineTypes> {
|
||||
/// The configured chain spec
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
/// The client used to interact with the state
|
||||
@ -34,7 +35,7 @@ pub struct MiningTask<Client, Pool: TransactionPool> {
|
||||
/// backlog of sets of transactions ready to be mined
|
||||
queued: VecDeque<Vec<Arc<ValidPoolTransaction<<Pool as TransactionPool>::Transaction>>>>,
|
||||
// TODO: ideally this would just be a sender of hashes
|
||||
to_engine: UnboundedSender<BeaconEngineMessage>,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
||||
/// Used to notify consumers of new blocks
|
||||
canon_state_notification: CanonStateNotificationSender,
|
||||
/// The pipeline events to listen on
|
||||
@ -43,12 +44,12 @@ pub struct MiningTask<Client, Pool: TransactionPool> {
|
||||
|
||||
// === impl MiningTask ===
|
||||
|
||||
impl<Client, Pool: TransactionPool> MiningTask<Client, Pool> {
|
||||
impl<Client, Pool: TransactionPool, Engine: EngineTypes> MiningTask<Client, Pool, Engine> {
|
||||
/// Creates a new instance of the task
|
||||
pub(crate) fn new(
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
miner: MiningMode,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage>,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
||||
canon_state_notification: CanonStateNotificationSender,
|
||||
storage: Storage,
|
||||
client: Client,
|
||||
@ -74,11 +75,12 @@ impl<Client, Pool: TransactionPool> MiningTask<Client, Pool> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Client, Pool> Future for MiningTask<Client, Pool>
|
||||
impl<Client, Pool, Engine> Future for MiningTask<Client, Pool, Engine>
|
||||
where
|
||||
Client: StateProviderFactory + CanonChainTracker + Clone + Unpin + 'static,
|
||||
Pool: TransactionPool + Unpin + 'static,
|
||||
<Pool as TransactionPool>::Transaction: IntoRecoveredTransaction,
|
||||
Engine: EngineTypes + 'static,
|
||||
{
|
||||
type Output = ();
|
||||
|
||||
@ -228,7 +230,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Client, Pool: TransactionPool> std::fmt::Debug for MiningTask<Client, Pool> {
|
||||
impl<Client, Pool: TransactionPool, Engine: EngineTypes> std::fmt::Debug
|
||||
for MiningTask<Client, Pool, Engine>
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MiningTask").finish_non_exhaustive()
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ reth-payload-validator.workspace = true
|
||||
reth-prune.workspace = true
|
||||
reth-snapshot.workspace = true
|
||||
reth-tokio-util.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
# async
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
@ -53,6 +54,7 @@ reth-rpc-types-compat.workspace = true
|
||||
reth-tracing.workspace = true
|
||||
reth-revm.workspace = true
|
||||
reth-downloaders.workspace = true
|
||||
reth-node-builder.workspace = true
|
||||
|
||||
assert_matches.workspace = true
|
||||
|
||||
|
||||
@ -6,9 +6,9 @@ use crate::{
|
||||
};
|
||||
use futures::TryFutureExt;
|
||||
use reth_interfaces::RethResult;
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_rpc_types::engine::{
|
||||
CancunPayloadFields, ExecutionPayload, ForkchoiceState, ForkchoiceUpdated, PayloadAttributes,
|
||||
PayloadStatus,
|
||||
CancunPayloadFields, ExecutionPayload, ForkchoiceState, ForkchoiceUpdated, PayloadStatus,
|
||||
};
|
||||
use tokio::sync::{mpsc, mpsc::UnboundedSender, oneshot};
|
||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
@ -17,16 +17,31 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
/// engine task.
|
||||
///
|
||||
/// See also [`BeaconConsensusEngine`](crate::engine::BeaconConsensusEngine).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BeaconConsensusEngineHandle {
|
||||
pub(crate) to_engine: UnboundedSender<BeaconEngineMessage>,
|
||||
#[derive(Debug)]
|
||||
pub struct BeaconConsensusEngineHandle<Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
pub(crate) to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
|
||||
}
|
||||
|
||||
impl<Engine> Clone for BeaconConsensusEngineHandle<Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self { to_engine: self.to_engine.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
// === impl BeaconConsensusEngineHandle ===
|
||||
|
||||
impl BeaconConsensusEngineHandle {
|
||||
impl<Engine> BeaconConsensusEngineHandle<Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
/// Creates a new beacon consensus engine handle.
|
||||
pub fn new(to_engine: UnboundedSender<BeaconEngineMessage>) -> Self {
|
||||
pub fn new(to_engine: UnboundedSender<BeaconEngineMessage<Engine>>) -> Self {
|
||||
Self { to_engine }
|
||||
}
|
||||
|
||||
@ -49,7 +64,7 @@ impl BeaconConsensusEngineHandle {
|
||||
pub async fn fork_choice_updated(
|
||||
&self,
|
||||
state: ForkchoiceState,
|
||||
payload_attrs: Option<PayloadAttributes>,
|
||||
payload_attrs: Option<Engine::PayloadAttributes>,
|
||||
) -> Result<ForkchoiceUpdated, BeaconForkChoiceUpdateError> {
|
||||
Ok(self
|
||||
.send_fork_choice_updated(state, payload_attrs)
|
||||
@ -63,7 +78,7 @@ impl BeaconConsensusEngineHandle {
|
||||
fn send_fork_choice_updated(
|
||||
&self,
|
||||
state: ForkchoiceState,
|
||||
payload_attrs: Option<PayloadAttributes>,
|
||||
payload_attrs: Option<Engine::PayloadAttributes>,
|
||||
) -> oneshot::Receiver<RethResult<OnForkChoiceUpdated>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.to_engine.send(BeaconEngineMessage::ForkchoiceUpdated {
|
||||
|
||||
@ -4,10 +4,11 @@ use crate::{
|
||||
};
|
||||
use futures::{future::Either, FutureExt};
|
||||
use reth_interfaces::{consensus::ForkchoiceState, RethResult};
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_payload_builder::error::PayloadBuilderError;
|
||||
use reth_rpc_types::engine::{
|
||||
CancunPayloadFields, ExecutionPayload, ForkChoiceUpdateResult, ForkchoiceUpdateError,
|
||||
ForkchoiceUpdated, PayloadAttributes, PayloadId, PayloadStatus, PayloadStatusEnum,
|
||||
ForkchoiceUpdated, PayloadId, PayloadStatus, PayloadStatusEnum,
|
||||
};
|
||||
use std::{
|
||||
future::Future,
|
||||
@ -141,7 +142,7 @@ impl Future for PendingPayloadId {
|
||||
/// consensus layer).
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum BeaconEngineMessage {
|
||||
pub enum BeaconEngineMessage<Engine: EngineTypes> {
|
||||
/// Message with new payload.
|
||||
NewPayload {
|
||||
/// The execution payload received by Engine API.
|
||||
@ -156,7 +157,7 @@ pub enum BeaconEngineMessage {
|
||||
/// The updated forkchoice state.
|
||||
state: ForkchoiceState,
|
||||
/// The payload attributes for block building.
|
||||
payload_attrs: Option<PayloadAttributes>,
|
||||
payload_attrs: Option<Engine::PayloadAttributes>,
|
||||
/// The sender for returning forkchoice updated result.
|
||||
tx: oneshot::Sender<RethResult<OnForkChoiceUpdated>>,
|
||||
},
|
||||
|
||||
@ -20,7 +20,8 @@ use reth_interfaces::{
|
||||
sync::{NetworkSyncUpdater, SyncState},
|
||||
RethError, RethResult,
|
||||
};
|
||||
use reth_payload_builder::{PayloadBuilderAttributes, PayloadBuilderHandle};
|
||||
use reth_node_api::{EngineTypes, PayloadAttributes, PayloadBuilderAttributes};
|
||||
use reth_payload_builder::PayloadBuilderHandle;
|
||||
use reth_primitives::{
|
||||
constants::EPOCH_SLOTS, stage::StageId, BlockNumHash, BlockNumber, Head, Header, SealedBlock,
|
||||
SealedHeader, B256,
|
||||
@ -30,8 +31,7 @@ use reth_provider::{
|
||||
StageCheckpointReader,
|
||||
};
|
||||
use reth_rpc_types::engine::{
|
||||
CancunPayloadFields, ExecutionPayload, PayloadAttributes, PayloadStatus, PayloadStatusEnum,
|
||||
PayloadValidationError,
|
||||
CancunPayloadFields, ExecutionPayload, PayloadStatus, PayloadStatusEnum, PayloadValidationError,
|
||||
};
|
||||
|
||||
use reth_stages::{ControlFlow, Pipeline, PipelineError};
|
||||
@ -164,7 +164,7 @@ pub const MIN_BLOCKS_FOR_PIPELINE_RUN: u64 = EPOCH_SLOTS;
|
||||
/// If the future is polled more than once. Leads to undefined state.
|
||||
#[must_use = "Future does nothing unless polled"]
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct BeaconConsensusEngine<DB, BT, Client>
|
||||
pub struct BeaconConsensusEngine<DB, BT, Client, EngineT>
|
||||
where
|
||||
DB: Database,
|
||||
Client: HeadersClient + BodiesClient,
|
||||
@ -173,6 +173,7 @@ where
|
||||
+ BlockIdReader
|
||||
+ CanonChainTracker
|
||||
+ StageCheckpointReader,
|
||||
EngineT: EngineTypes,
|
||||
{
|
||||
/// Controls syncing triggered by engine updates.
|
||||
sync: EngineSyncController<DB, Client>,
|
||||
@ -181,13 +182,13 @@ where
|
||||
/// Used for emitting updates about whether the engine is syncing or not.
|
||||
sync_state_updater: Box<dyn NetworkSyncUpdater>,
|
||||
/// The Engine API message receiver.
|
||||
engine_message_rx: UnboundedReceiverStream<BeaconEngineMessage>,
|
||||
engine_message_rx: UnboundedReceiverStream<BeaconEngineMessage<EngineT>>,
|
||||
/// A clone of the handle
|
||||
handle: BeaconConsensusEngineHandle,
|
||||
handle: BeaconConsensusEngineHandle<EngineT>,
|
||||
/// Tracks the received forkchoice state updates received by the CL.
|
||||
forkchoice_state_tracker: ForkchoiceStateTracker,
|
||||
/// The payload store.
|
||||
payload_builder: PayloadBuilderHandle,
|
||||
payload_builder: PayloadBuilderHandle<EngineT>,
|
||||
/// Validator for execution payloads
|
||||
payload_validator: ExecutionPayloadValidator,
|
||||
/// Listeners for engine events.
|
||||
@ -212,7 +213,7 @@ where
|
||||
hooks: EngineHooksController,
|
||||
}
|
||||
|
||||
impl<DB, BT, Client> BeaconConsensusEngine<DB, BT, Client>
|
||||
impl<DB, BT, Client, EngineT> BeaconConsensusEngine<DB, BT, Client, EngineT>
|
||||
where
|
||||
DB: Database + Unpin + 'static,
|
||||
BT: BlockchainTreeEngine
|
||||
@ -223,6 +224,7 @@ where
|
||||
+ ChainSpecProvider
|
||||
+ 'static,
|
||||
Client: HeadersClient + BodiesClient + Clone + Unpin + 'static,
|
||||
EngineT: EngineTypes + Unpin + 'static,
|
||||
{
|
||||
/// Create a new instance of the [BeaconConsensusEngine].
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -234,11 +236,11 @@ where
|
||||
sync_state_updater: Box<dyn NetworkSyncUpdater>,
|
||||
max_block: Option<BlockNumber>,
|
||||
run_pipeline_continuously: bool,
|
||||
payload_builder: PayloadBuilderHandle,
|
||||
payload_builder: PayloadBuilderHandle<EngineT>,
|
||||
target: Option<B256>,
|
||||
pipeline_run_threshold: u64,
|
||||
hooks: EngineHooks,
|
||||
) -> RethResult<(Self, BeaconConsensusEngineHandle)> {
|
||||
) -> RethResult<(Self, BeaconConsensusEngineHandle<EngineT>)> {
|
||||
let (to_engine, rx) = mpsc::unbounded_channel();
|
||||
Self::with_channel(
|
||||
client,
|
||||
@ -278,13 +280,13 @@ where
|
||||
sync_state_updater: Box<dyn NetworkSyncUpdater>,
|
||||
max_block: Option<BlockNumber>,
|
||||
run_pipeline_continuously: bool,
|
||||
payload_builder: PayloadBuilderHandle,
|
||||
payload_builder: PayloadBuilderHandle<EngineT>,
|
||||
target: Option<B256>,
|
||||
pipeline_run_threshold: u64,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage>,
|
||||
rx: UnboundedReceiver<BeaconEngineMessage>,
|
||||
to_engine: UnboundedSender<BeaconEngineMessage<EngineT>>,
|
||||
rx: UnboundedReceiver<BeaconEngineMessage<EngineT>>,
|
||||
hooks: EngineHooks,
|
||||
) -> RethResult<(Self, BeaconConsensusEngineHandle)> {
|
||||
) -> RethResult<(Self, BeaconConsensusEngineHandle<EngineT>)> {
|
||||
let handle = BeaconConsensusEngineHandle { to_engine };
|
||||
let sync = EngineSyncController::new(
|
||||
pipeline,
|
||||
@ -323,6 +325,219 @@ where
|
||||
Ok((this, handle))
|
||||
}
|
||||
|
||||
/// Called to resolve chain forks and ensure that the Execution layer is working with the latest
|
||||
/// valid chain.
|
||||
///
|
||||
/// These responses should adhere to the [Engine API Spec for
|
||||
/// `engine_forkchoiceUpdated`](https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#specification-1).
|
||||
///
|
||||
/// Returns an error if an internal error occurred like a database error.
|
||||
fn forkchoice_updated(
|
||||
&mut self,
|
||||
state: ForkchoiceState,
|
||||
attrs: Option<EngineT::PayloadAttributes>,
|
||||
) -> RethResult<OnForkChoiceUpdated> {
|
||||
trace!(target: "consensus::engine", ?state, "Received new forkchoice state update");
|
||||
if state.head_block_hash.is_zero() {
|
||||
return Ok(OnForkChoiceUpdated::invalid_state());
|
||||
}
|
||||
|
||||
// check if the new head hash is connected to any ancestor that we previously marked as
|
||||
// invalid
|
||||
let lowest_buffered_ancestor_fcu = self.lowest_buffered_ancestor_or(state.head_block_hash);
|
||||
if let Some(status) = self.check_invalid_ancestor(lowest_buffered_ancestor_fcu) {
|
||||
return Ok(OnForkChoiceUpdated::with_invalid(status));
|
||||
}
|
||||
|
||||
if self.sync.is_pipeline_active() {
|
||||
// We can only process new forkchoice updates if the pipeline is idle, since it requires
|
||||
// exclusive access to the database
|
||||
trace!(target: "consensus::engine", "Pipeline is syncing, skipping forkchoice update");
|
||||
return Ok(OnForkChoiceUpdated::syncing());
|
||||
}
|
||||
|
||||
if let Some(hook) = self.hooks.active_db_write_hook() {
|
||||
// We can only process new forkchoice updates if no hook with db write is running,
|
||||
// since it requires exclusive access to the database
|
||||
warn!(
|
||||
target: "consensus::engine",
|
||||
hook = %hook.name(),
|
||||
"Hook is in progress, skipping forkchoice update. \
|
||||
This may affect the performance of your node as a validator."
|
||||
);
|
||||
return Ok(OnForkChoiceUpdated::syncing());
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
let make_canonical_result = self.blockchain.make_canonical(&state.head_block_hash);
|
||||
let elapsed = self.record_make_canonical_latency(start, &make_canonical_result);
|
||||
|
||||
let status = match make_canonical_result {
|
||||
Ok(outcome) => {
|
||||
match &outcome {
|
||||
CanonicalOutcome::AlreadyCanonical { header } => {
|
||||
// On Optimism, the proposers are allowed to reorg their own chain at will.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "optimism")] {
|
||||
if self.blockchain.chain_spec().is_optimism() {
|
||||
debug!(
|
||||
target: "consensus::engine",
|
||||
fcu_head_num=?header.number,
|
||||
current_head_num=?self.blockchain.canonical_tip().number,
|
||||
"[Optimism] Allowing beacon reorg to old head"
|
||||
);
|
||||
let _ = self.update_head(header.clone());
|
||||
self.listeners.notify(
|
||||
BeaconConsensusEngineEvent::CanonicalChainCommitted(
|
||||
Box::new(header.clone()),
|
||||
elapsed,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
target: "consensus::engine",
|
||||
fcu_head_num=?header.number,
|
||||
current_head_num=?self.blockchain.canonical_tip().number,
|
||||
"Ignoring beacon update to old head"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
target: "consensus::engine",
|
||||
fcu_head_num=?header.number,
|
||||
current_head_num=?self.blockchain.canonical_tip().number,
|
||||
"Ignoring beacon update to old head"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
CanonicalOutcome::Committed { head } => {
|
||||
debug!(
|
||||
target: "consensus::engine",
|
||||
hash=?state.head_block_hash,
|
||||
number=head.number,
|
||||
"Canonicalized new head"
|
||||
);
|
||||
|
||||
// new VALID update that moved the canonical chain forward
|
||||
let _ = self.update_head(head.clone());
|
||||
self.listeners.notify(BeaconConsensusEngineEvent::CanonicalChainCommitted(
|
||||
Box::new(head.clone()),
|
||||
elapsed,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(attrs) = attrs {
|
||||
// if we return early then we wouldn't perform these consistency checks, so we
|
||||
// need to do them here, and should do them before we process any payload
|
||||
// attributes
|
||||
if let Some(invalid_fcu_response) = self.ensure_consistent_state(state)? {
|
||||
trace!(target: "consensus::engine", ?state, head=?state.head_block_hash, "Forkchoice state is inconsistent, returning invalid response");
|
||||
return Ok(invalid_fcu_response);
|
||||
}
|
||||
|
||||
// the CL requested to build a new payload on top of this new VALID head
|
||||
let payload_response = self.process_payload_attributes(
|
||||
attrs,
|
||||
outcome.into_header().unseal(),
|
||||
state,
|
||||
);
|
||||
|
||||
trace!(target: "consensus::engine", status = ?payload_response, ?state, "Returning forkchoice status");
|
||||
return Ok(payload_response);
|
||||
}
|
||||
|
||||
PayloadStatus::new(PayloadStatusEnum::Valid, Some(state.head_block_hash))
|
||||
}
|
||||
Err(error) => {
|
||||
if let RethError::Canonical(ref err) = error {
|
||||
if err.is_fatal() {
|
||||
tracing::error!(target: "consensus::engine", ?err, "Encountered fatal error");
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
|
||||
self.on_failed_canonical_forkchoice_update(&state, error)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(invalid_fcu_response) =
|
||||
self.ensure_consistent_state_with_status(state, &status)?
|
||||
{
|
||||
trace!(target: "consensus::engine", ?status, ?state, "Forkchoice state is inconsistent, returning invalid response");
|
||||
return Ok(invalid_fcu_response);
|
||||
}
|
||||
|
||||
trace!(target: "consensus::engine", ?status, ?state, "Returning forkchoice status");
|
||||
Ok(OnForkChoiceUpdated::valid(status))
|
||||
}
|
||||
|
||||
/// Invoked when we receive a new forkchoice update message.
|
||||
///
|
||||
/// Returns `true` if the engine now reached its maximum block number, See
|
||||
/// [EngineSyncController::has_reached_max_block].
|
||||
fn on_forkchoice_updated(
|
||||
&mut self,
|
||||
state: ForkchoiceState,
|
||||
attrs: Option<EngineT::PayloadAttributes>,
|
||||
tx: oneshot::Sender<Result<OnForkChoiceUpdated, RethError>>,
|
||||
) -> OnForkchoiceUpdateOutcome {
|
||||
self.metrics.forkchoice_updated_messages.increment(1);
|
||||
self.blockchain.on_forkchoice_update_received(&state);
|
||||
|
||||
let on_updated = match self.forkchoice_updated(state, attrs) {
|
||||
Ok(response) => response,
|
||||
Err(error) => {
|
||||
if let RethError::Execution(ref err) = error {
|
||||
if err.is_fatal() {
|
||||
// FCU resulted in a fatal error from which we can't recover
|
||||
let err = err.clone();
|
||||
let _ = tx.send(Err(error));
|
||||
return OnForkchoiceUpdateOutcome::Fatal(err);
|
||||
}
|
||||
}
|
||||
let _ = tx.send(Err(error));
|
||||
return OnForkchoiceUpdateOutcome::Processed;
|
||||
}
|
||||
};
|
||||
|
||||
let fcu_status = on_updated.forkchoice_status();
|
||||
|
||||
// update the forkchoice state tracker
|
||||
self.forkchoice_state_tracker.set_latest(state, fcu_status);
|
||||
|
||||
// send the response to the CL ASAP
|
||||
let _ = tx.send(Ok(on_updated));
|
||||
|
||||
match fcu_status {
|
||||
ForkchoiceStatus::Invalid => {}
|
||||
ForkchoiceStatus::Valid => {
|
||||
// FCU head is valid, we're no longer syncing
|
||||
self.sync_state_updater.update_sync_state(SyncState::Idle);
|
||||
// node's fully synced, clear active download requests
|
||||
self.sync.clear_block_download_requests();
|
||||
|
||||
// check if we reached the maximum configured block
|
||||
let tip_number = self.blockchain.canonical_tip().number;
|
||||
if self.sync.has_reached_max_block(tip_number) {
|
||||
// Terminate the sync early if it's reached the maximum user
|
||||
// configured block.
|
||||
return OnForkchoiceUpdateOutcome::ReachedMaxBlock;
|
||||
}
|
||||
}
|
||||
ForkchoiceStatus::Syncing => {
|
||||
// we're syncing
|
||||
self.sync_state_updater.update_sync_state(SyncState::Syncing);
|
||||
}
|
||||
}
|
||||
|
||||
// notify listeners about new processed FCU
|
||||
self.listeners.notify(BeaconConsensusEngineEvent::ForkchoiceUpdated(state, fcu_status));
|
||||
|
||||
OnForkchoiceUpdateOutcome::Processed
|
||||
}
|
||||
|
||||
/// Check if the pipeline is consistent (all stages have the checkpoint block numbers no less
|
||||
/// than the checkpoint of the first stage).
|
||||
///
|
||||
@ -370,7 +585,7 @@ where
|
||||
///
|
||||
/// The [`BeaconConsensusEngineHandle`] can be used to interact with this
|
||||
/// [`BeaconConsensusEngine`]
|
||||
pub fn handle(&self) -> BeaconConsensusEngineHandle {
|
||||
pub fn handle(&self) -> BeaconConsensusEngineHandle<EngineT> {
|
||||
self.handle.clone()
|
||||
}
|
||||
|
||||
@ -554,219 +769,6 @@ where
|
||||
Some(status)
|
||||
}
|
||||
|
||||
/// Invoked when we receive a new forkchoice update message.
|
||||
///
|
||||
/// Returns `true` if the engine now reached its maximum block number, See
|
||||
/// [EngineSyncController::has_reached_max_block].
|
||||
fn on_forkchoice_updated(
|
||||
&mut self,
|
||||
state: ForkchoiceState,
|
||||
attrs: Option<PayloadAttributes>,
|
||||
tx: oneshot::Sender<Result<OnForkChoiceUpdated, RethError>>,
|
||||
) -> OnForkchoiceUpdateOutcome {
|
||||
self.metrics.forkchoice_updated_messages.increment(1);
|
||||
self.blockchain.on_forkchoice_update_received(&state);
|
||||
|
||||
let on_updated = match self.forkchoice_updated(state, attrs) {
|
||||
Ok(response) => response,
|
||||
Err(error) => {
|
||||
if let RethError::Execution(ref err) = error {
|
||||
if err.is_fatal() {
|
||||
// FCU resulted in a fatal error from which we can't recover
|
||||
let err = err.clone();
|
||||
let _ = tx.send(Err(error));
|
||||
return OnForkchoiceUpdateOutcome::Fatal(err);
|
||||
}
|
||||
}
|
||||
let _ = tx.send(Err(error));
|
||||
return OnForkchoiceUpdateOutcome::Processed;
|
||||
}
|
||||
};
|
||||
|
||||
let fcu_status = on_updated.forkchoice_status();
|
||||
|
||||
// update the forkchoice state tracker
|
||||
self.forkchoice_state_tracker.set_latest(state, fcu_status);
|
||||
|
||||
// send the response to the CL ASAP
|
||||
let _ = tx.send(Ok(on_updated));
|
||||
|
||||
match fcu_status {
|
||||
ForkchoiceStatus::Invalid => {}
|
||||
ForkchoiceStatus::Valid => {
|
||||
// FCU head is valid, we're no longer syncing
|
||||
self.sync_state_updater.update_sync_state(SyncState::Idle);
|
||||
// node's fully synced, clear active download requests
|
||||
self.sync.clear_block_download_requests();
|
||||
|
||||
// check if we reached the maximum configured block
|
||||
let tip_number = self.blockchain.canonical_tip().number;
|
||||
if self.sync.has_reached_max_block(tip_number) {
|
||||
// Terminate the sync early if it's reached the maximum user
|
||||
// configured block.
|
||||
return OnForkchoiceUpdateOutcome::ReachedMaxBlock;
|
||||
}
|
||||
}
|
||||
ForkchoiceStatus::Syncing => {
|
||||
// we're syncing
|
||||
self.sync_state_updater.update_sync_state(SyncState::Syncing);
|
||||
}
|
||||
}
|
||||
|
||||
// notify listeners about new processed FCU
|
||||
self.listeners.notify(BeaconConsensusEngineEvent::ForkchoiceUpdated(state, fcu_status));
|
||||
|
||||
OnForkchoiceUpdateOutcome::Processed
|
||||
}
|
||||
|
||||
/// Called to resolve chain forks and ensure that the Execution layer is working with the latest
|
||||
/// valid chain.
|
||||
///
|
||||
/// These responses should adhere to the [Engine API Spec for
|
||||
/// `engine_forkchoiceUpdated`](https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#specification-1).
|
||||
///
|
||||
/// Returns an error if an internal error occurred like a database error.
|
||||
fn forkchoice_updated(
|
||||
&mut self,
|
||||
state: ForkchoiceState,
|
||||
attrs: Option<PayloadAttributes>,
|
||||
) -> RethResult<OnForkChoiceUpdated> {
|
||||
trace!(target: "consensus::engine", ?state, "Received new forkchoice state update");
|
||||
if state.head_block_hash.is_zero() {
|
||||
return Ok(OnForkChoiceUpdated::invalid_state());
|
||||
}
|
||||
|
||||
// check if the new head hash is connected to any ancestor that we previously marked as
|
||||
// invalid
|
||||
let lowest_buffered_ancestor_fcu = self.lowest_buffered_ancestor_or(state.head_block_hash);
|
||||
if let Some(status) = self.check_invalid_ancestor(lowest_buffered_ancestor_fcu) {
|
||||
return Ok(OnForkChoiceUpdated::with_invalid(status));
|
||||
}
|
||||
|
||||
if self.sync.is_pipeline_active() {
|
||||
// We can only process new forkchoice updates if the pipeline is idle, since it requires
|
||||
// exclusive access to the database
|
||||
trace!(target: "consensus::engine", "Pipeline is syncing, skipping forkchoice update");
|
||||
return Ok(OnForkChoiceUpdated::syncing());
|
||||
}
|
||||
|
||||
if let Some(hook) = self.hooks.active_db_write_hook() {
|
||||
// We can only process new forkchoice updates if no hook with db write is running,
|
||||
// since it requires exclusive access to the database
|
||||
warn!(
|
||||
target: "consensus::engine",
|
||||
hook = %hook.name(),
|
||||
"Hook is in progress, skipping forkchoice update. \
|
||||
This may affect the performance of your node as a validator."
|
||||
);
|
||||
return Ok(OnForkChoiceUpdated::syncing());
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
let make_canonical_result = self.blockchain.make_canonical(&state.head_block_hash);
|
||||
let elapsed = self.record_make_canonical_latency(start, &make_canonical_result);
|
||||
|
||||
let status = match make_canonical_result {
|
||||
Ok(outcome) => {
|
||||
match &outcome {
|
||||
CanonicalOutcome::AlreadyCanonical { header } => {
|
||||
// On Optimism, the proposers are allowed to reorg their own chain at will.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "optimism")] {
|
||||
if self.blockchain.chain_spec().is_optimism() {
|
||||
debug!(
|
||||
target: "consensus::engine",
|
||||
fcu_head_num=?header.number,
|
||||
current_head_num=?self.blockchain.canonical_tip().number,
|
||||
"[Optimism] Allowing beacon reorg to old head"
|
||||
);
|
||||
let _ = self.update_head(header.clone());
|
||||
self.listeners.notify(
|
||||
BeaconConsensusEngineEvent::CanonicalChainCommitted(
|
||||
Box::new(header.clone()),
|
||||
elapsed,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
target: "consensus::engine",
|
||||
fcu_head_num=?header.number,
|
||||
current_head_num=?self.blockchain.canonical_tip().number,
|
||||
"Ignoring beacon update to old head"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
target: "consensus::engine",
|
||||
fcu_head_num=?header.number,
|
||||
current_head_num=?self.blockchain.canonical_tip().number,
|
||||
"Ignoring beacon update to old head"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
CanonicalOutcome::Committed { head } => {
|
||||
debug!(
|
||||
target: "consensus::engine",
|
||||
hash=?state.head_block_hash,
|
||||
number=head.number,
|
||||
"Canonicalized new head"
|
||||
);
|
||||
|
||||
// new VALID update that moved the canonical chain forward
|
||||
let _ = self.update_head(head.clone());
|
||||
self.listeners.notify(BeaconConsensusEngineEvent::CanonicalChainCommitted(
|
||||
Box::new(head.clone()),
|
||||
elapsed,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(attrs) = attrs {
|
||||
// if we return early then we wouldn't perform these consistency checks, so we
|
||||
// need to do them here, and should do them before we process any payload
|
||||
// attributes
|
||||
if let Some(invalid_fcu_response) = self.ensure_consistent_state(state)? {
|
||||
trace!(target: "consensus::engine", ?state, head=?state.head_block_hash, "Forkchoice state is inconsistent, returning invalid response");
|
||||
return Ok(invalid_fcu_response);
|
||||
}
|
||||
|
||||
// the CL requested to build a new payload on top of this new VALID head
|
||||
let payload_response = self.process_payload_attributes(
|
||||
attrs,
|
||||
outcome.into_header().unseal(),
|
||||
state,
|
||||
);
|
||||
|
||||
trace!(target: "consensus::engine", status = ?payload_response, ?state, "Returning forkchoice status");
|
||||
return Ok(payload_response);
|
||||
}
|
||||
|
||||
PayloadStatus::new(PayloadStatusEnum::Valid, Some(state.head_block_hash))
|
||||
}
|
||||
Err(error) => {
|
||||
if let RethError::Canonical(ref err) = error {
|
||||
if err.is_fatal() {
|
||||
tracing::error!(target: "consensus::engine", ?err, "Encountered fatal error");
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
|
||||
self.on_failed_canonical_forkchoice_update(&state, error)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(invalid_fcu_response) =
|
||||
self.ensure_consistent_state_with_status(state, &status)?
|
||||
{
|
||||
trace!(target: "consensus::engine", ?status, ?state, "Forkchoice state is inconsistent, returning invalid response");
|
||||
return Ok(invalid_fcu_response);
|
||||
}
|
||||
|
||||
trace!(target: "consensus::engine", ?status, ?state, "Returning forkchoice status");
|
||||
Ok(OnForkChoiceUpdated::valid(status))
|
||||
}
|
||||
|
||||
/// Record latency metrics for one call to make a block canonical
|
||||
/// Takes start time of the call and result of the make canonical call
|
||||
///
|
||||
@ -1051,55 +1053,6 @@ where
|
||||
.unwrap_or_else(|| hash)
|
||||
}
|
||||
|
||||
/// Validates the payload attributes with respect to the header and fork choice state.
|
||||
///
|
||||
/// Note: At this point, the fork choice update is considered to be VALID, however, we can still
|
||||
/// return an error if the payload attributes are invalid.
|
||||
fn process_payload_attributes(
|
||||
&self,
|
||||
attrs: PayloadAttributes,
|
||||
head: Header,
|
||||
state: ForkchoiceState,
|
||||
) -> OnForkChoiceUpdated {
|
||||
// 7. Client software MUST ensure that payloadAttributes.timestamp is greater than timestamp
|
||||
// of a block referenced by forkchoiceState.headBlockHash. If this condition isn't held
|
||||
// client software MUST respond with -38003: `Invalid payload attributes` and MUST NOT
|
||||
// begin a payload build process. In such an event, the forkchoiceState update MUST NOT
|
||||
// be rolled back.
|
||||
if attrs.timestamp <= head.timestamp {
|
||||
return OnForkChoiceUpdated::invalid_payload_attributes();
|
||||
}
|
||||
|
||||
// 8. Client software MUST begin a payload build process building on top of
|
||||
// forkchoiceState.headBlockHash and identified via buildProcessId value if
|
||||
// payloadAttributes is not null and the forkchoice state has been updated successfully.
|
||||
// The build process is specified in the Payload building section.
|
||||
match PayloadBuilderAttributes::try_new(state.head_block_hash, attrs) {
|
||||
Ok(attributes) => {
|
||||
// send the payload to the builder and return the receiver for the pending payload
|
||||
// id, initiating payload job is handled asynchronously
|
||||
let pending_payload_id = self.payload_builder.send_new_payload(attributes);
|
||||
|
||||
// Client software MUST respond to this method call in the following way:
|
||||
// {
|
||||
// payloadStatus: {
|
||||
// status: VALID,
|
||||
// latestValidHash: forkchoiceState.headBlockHash,
|
||||
// validationError: null
|
||||
// },
|
||||
// payloadId: buildProcessId
|
||||
// }
|
||||
//
|
||||
// if the payload is deemed VALID and the build process has begun.
|
||||
OnForkChoiceUpdated::updated_with_pending_payload_id(
|
||||
PayloadStatus::new(PayloadStatusEnum::Valid, Some(state.head_block_hash)),
|
||||
pending_payload_id,
|
||||
)
|
||||
}
|
||||
Err(_) => OnForkChoiceUpdated::invalid_payload_attributes(),
|
||||
}
|
||||
}
|
||||
|
||||
/// When the Consensus layer receives a new block via the consensus gossip protocol,
|
||||
/// the transactions in the block are sent to the execution layer in the form of a
|
||||
/// [`ExecutionPayload`]. The Execution layer executes the transactions and validates the
|
||||
@ -1224,6 +1177,58 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the payload attributes with respect to the header and fork choice state.
|
||||
///
|
||||
/// Note: At this point, the fork choice update is considered to be VALID, however, we can still
|
||||
/// return an error if the payload attributes are invalid.
|
||||
fn process_payload_attributes(
|
||||
&self,
|
||||
attrs: EngineT::PayloadAttributes,
|
||||
head: Header,
|
||||
state: ForkchoiceState,
|
||||
) -> OnForkChoiceUpdated {
|
||||
// 7. Client software MUST ensure that payloadAttributes.timestamp is greater than timestamp
|
||||
// of a block referenced by forkchoiceState.headBlockHash. If this condition isn't held
|
||||
// client software MUST respond with -38003: `Invalid payload attributes` and MUST NOT
|
||||
// begin a payload build process. In such an event, the forkchoiceState update MUST NOT
|
||||
// be rolled back.
|
||||
if attrs.timestamp() <= head.timestamp {
|
||||
return OnForkChoiceUpdated::invalid_payload_attributes();
|
||||
}
|
||||
|
||||
// 8. Client software MUST begin a payload build process building on top of
|
||||
// forkchoiceState.headBlockHash and identified via buildProcessId value if
|
||||
// payloadAttributes is not null and the forkchoice state has been updated successfully.
|
||||
// The build process is specified in the Payload building section.
|
||||
match <EngineT::PayloadBuilderAttributes as PayloadBuilderAttributes>::try_new(
|
||||
state.head_block_hash,
|
||||
attrs,
|
||||
) {
|
||||
Ok(attributes) => {
|
||||
// send the payload to the builder and return the receiver for the pending payload
|
||||
// id, initiating payload job is handled asynchronously
|
||||
let pending_payload_id = self.payload_builder.send_new_payload(attributes);
|
||||
|
||||
// Client software MUST respond to this method call in the following way:
|
||||
// {
|
||||
// payloadStatus: {
|
||||
// status: VALID,
|
||||
// latestValidHash: forkchoiceState.headBlockHash,
|
||||
// validationError: null
|
||||
// },
|
||||
// payloadId: buildProcessId
|
||||
// }
|
||||
//
|
||||
// if the payload is deemed VALID and the build process has begun.
|
||||
OnForkChoiceUpdated::updated_with_pending_payload_id(
|
||||
PayloadStatus::new(PayloadStatusEnum::Valid, Some(state.head_block_hash)),
|
||||
pending_payload_id,
|
||||
)
|
||||
}
|
||||
Err(_) => OnForkChoiceUpdated::invalid_payload_attributes(),
|
||||
}
|
||||
}
|
||||
|
||||
/// When the pipeline is active, the tree is unable to commit any additional blocks since the
|
||||
/// pipeline holds exclusive access to the database.
|
||||
///
|
||||
@ -1735,7 +1740,7 @@ where
|
||||
/// local forkchoice state, it will launch the pipeline to sync to the head hash.
|
||||
/// While the pipeline is syncing, the consensus engine will keep processing messages from the
|
||||
/// receiver and forwarding them to the blockchain tree.
|
||||
impl<DB, BT, Client> Future for BeaconConsensusEngine<DB, BT, Client>
|
||||
impl<DB, BT, Client, EngineT> Future for BeaconConsensusEngine<DB, BT, Client, EngineT>
|
||||
where
|
||||
DB: Database + Unpin + 'static,
|
||||
Client: HeadersClient + BodiesClient + Clone + Unpin + 'static,
|
||||
@ -1747,6 +1752,7 @@ where
|
||||
+ ChainSpecProvider
|
||||
+ Unpin
|
||||
+ 'static,
|
||||
EngineT: EngineTypes + Unpin + 'static,
|
||||
{
|
||||
type Output = Result<(), BeaconConsensusEngineError>;
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ use reth_interfaces::{
|
||||
sync::NoopSyncStateUpdater,
|
||||
test_utils::{NoopFullBlockClient, TestConsensus},
|
||||
};
|
||||
use reth_node_builder::EthEngineTypes;
|
||||
use reth_payload_builder::test_utils::spawn_test_payload_service;
|
||||
use reth_primitives::{BlockNumber, ChainSpec, PruneModes, Receipt, B256, U256};
|
||||
use reth_provider::{
|
||||
@ -49,6 +50,7 @@ type TestBeaconConsensusEngine<Client> = BeaconConsensusEngine<
|
||||
>,
|
||||
>,
|
||||
Arc<EitherDownloader<Client, NoopFullBlockClient>>,
|
||||
EthEngineTypes,
|
||||
>;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -57,14 +59,14 @@ pub struct TestEnv<DB> {
|
||||
// Keep the tip receiver around, so it's not dropped.
|
||||
#[allow(dead_code)]
|
||||
tip_rx: watch::Receiver<B256>,
|
||||
engine_handle: BeaconConsensusEngineHandle,
|
||||
engine_handle: BeaconConsensusEngineHandle<EthEngineTypes>,
|
||||
}
|
||||
|
||||
impl<DB> TestEnv<DB> {
|
||||
fn new(
|
||||
db: DB,
|
||||
tip_rx: watch::Receiver<B256>,
|
||||
engine_handle: BeaconConsensusEngineHandle,
|
||||
engine_handle: BeaconConsensusEngineHandle<EthEngineTypes>,
|
||||
) -> Self {
|
||||
Self { db, tip_rx, engine_handle }
|
||||
}
|
||||
@ -453,7 +455,7 @@ where
|
||||
}
|
||||
TestConsensusConfig::Test => Arc::new(TestConsensus::default()),
|
||||
};
|
||||
let payload_builder = spawn_test_payload_service();
|
||||
let payload_builder = spawn_test_payload_service::<EthEngineTypes>();
|
||||
|
||||
// use either noop client or a user provided client (for example TestFullBlockClient)
|
||||
let client = Arc::new(
|
||||
@ -552,9 +554,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_consensus_engine<Client: HeadersClient + BodiesClient + 'static>(
|
||||
pub fn spawn_consensus_engine<Client>(
|
||||
engine: TestBeaconConsensusEngine<Client>,
|
||||
) -> oneshot::Receiver<Result<(), BeaconConsensusEngineError>> {
|
||||
) -> oneshot::Receiver<Result<(), BeaconConsensusEngineError>>
|
||||
where
|
||||
Client: HeadersClient + BodiesClient + 'static,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
tokio::spawn(async move {
|
||||
let result = engine.await;
|
||||
|
||||
24
crates/node-api/Cargo.toml
Normal file
24
crates/node-api/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "reth-node-api"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-primitives.workspace = true
|
||||
reth-rpc-types.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
# io
|
||||
serde.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
# for examples
|
||||
reth-payload-builder.workspace = true
|
||||
41
crates/node-api/src/engine/error.rs
Normal file
41
crates/node-api/src/engine/error.rs
Normal file
@ -0,0 +1,41 @@
|
||||
//! Defines a payload validation error type
|
||||
use thiserror::Error;
|
||||
|
||||
/// Thrown when the payload or attributes are known to be invalid before processing.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AttributesValidationError {
|
||||
/// Thrown if `PayloadAttributes` provided in engine_forkchoiceUpdated before V3 contains a
|
||||
/// parent beacon block root
|
||||
#[error("parent beacon block root not supported before V3")]
|
||||
ParentBeaconBlockRootNotSupportedBeforeV3,
|
||||
/// Thrown if engine_forkchoiceUpdatedV1 contains withdrawals
|
||||
#[error("withdrawals not supported in V1")]
|
||||
WithdrawalsNotSupportedInV1,
|
||||
/// Thrown if engine_forkchoiceUpdated contains no withdrawals after Shanghai
|
||||
#[error("no withdrawals post-Shanghai")]
|
||||
NoWithdrawalsPostShanghai,
|
||||
/// Thrown if engine_forkchoiceUpdated contains withdrawals before Shanghai
|
||||
#[error("withdrawals pre-Shanghai")]
|
||||
HasWithdrawalsPreShanghai,
|
||||
/// Thrown if the `PayloadAttributes` provided in engine_forkchoiceUpdated contains no parent
|
||||
/// beacon block root after Cancun
|
||||
#[error("no parent beacon block root post-cancun")]
|
||||
NoParentBeaconBlockRootPostCancun,
|
||||
/// Thrown if `PayloadAttributes` were provided with a timestamp, but the version of the engine
|
||||
/// method called is meant for a fork that occurs after the provided timestamp.
|
||||
#[error("Unsupported fork")]
|
||||
UnsupportedFork,
|
||||
/// Another type of error that is not covered by the above variants.
|
||||
#[error("Invalid params: {0}")]
|
||||
InvalidParams(#[from] Box<dyn std::error::Error + Send + Sync>),
|
||||
}
|
||||
|
||||
impl AttributesValidationError {
|
||||
/// Creates an instance of the `InvalidParams` variant with the given error.
|
||||
pub fn invalid_params<E>(error: E) -> Self
|
||||
where
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
Self::InvalidParams(Box::new(error))
|
||||
}
|
||||
}
|
||||
323
crates/node-api/src/engine/mod.rs
Normal file
323
crates/node-api/src/engine/mod.rs
Normal file
@ -0,0 +1,323 @@
|
||||
//! This contains the [EngineTypes] trait and implementations for ethereum mainnet types.
|
||||
//!
|
||||
//! The [EngineTypes] trait can be implemented to configure the engine to work with custom types,
|
||||
//! as long as those types implement certain traits.
|
||||
//!
|
||||
//! Custom payload attributes can be supported by implementing two main traits:
|
||||
//!
|
||||
//! [PayloadAttributes] can be implemented for payload attributes types that are used as
|
||||
//! arguments to the `engine_forkchoiceUpdated` method. This type should be used to define and
|
||||
//! _spawn_ payload jobs.
|
||||
//!
|
||||
//! [PayloadBuilderAttributes] can be implemented for payload attributes types that _describe_
|
||||
//! running payload jobs.
|
||||
//!
|
||||
//! Once traits are implemented and custom types are defined, the [EngineTypes] trait can be
|
||||
//! implemented:
|
||||
//! ```no_run
|
||||
//! # use reth_rpc_types::engine::{PayloadAttributes as EthPayloadAttributes, PayloadId, Withdrawal};
|
||||
//! # use reth_primitives::{B256, ChainSpec, Address};
|
||||
//! # use reth_node_api::{EngineTypes, EngineApiMessageVersion, validate_version_specific_fields, AttributesValidationError, PayloadAttributes, PayloadBuilderAttributes};
|
||||
//! # use reth_payload_builder::EthPayloadBuilderAttributes;
|
||||
//! # use serde::{Deserialize, Serialize};
|
||||
//! # use thiserror::Error;
|
||||
//! # use std::convert::Infallible;
|
||||
//!
|
||||
//! /// A custom payload attributes type.
|
||||
//! #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
//! pub struct CustomPayloadAttributes {
|
||||
//! /// An inner payload type
|
||||
//! #[serde(flatten)]
|
||||
//! pub inner: EthPayloadAttributes,
|
||||
//! /// A custom field
|
||||
//! pub custom: u64,
|
||||
//! }
|
||||
//!
|
||||
//! /// Custom error type used in payload attributes validation
|
||||
//! #[derive(Debug, Error)]
|
||||
//! pub enum CustomError {
|
||||
//! #[error("Custom field is not zero")]
|
||||
//! CustomFieldIsNotZero,
|
||||
//! }
|
||||
//!
|
||||
//! impl PayloadAttributes for CustomPayloadAttributes {
|
||||
//! fn timestamp(&self) -> u64 {
|
||||
//! self.inner.timestamp()
|
||||
//! }
|
||||
//!
|
||||
//! fn withdrawals(&self) -> Option<&Vec<Withdrawal>> {
|
||||
//! self.inner.withdrawals()
|
||||
//! }
|
||||
//!
|
||||
//! fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
//! self.inner.parent_beacon_block_root()
|
||||
//! }
|
||||
//!
|
||||
//! fn ensure_well_formed_attributes(
|
||||
//! &self,
|
||||
//! chain_spec: &ChainSpec,
|
||||
//! version: EngineApiMessageVersion,
|
||||
//! ) -> Result<(), AttributesValidationError> {
|
||||
//! validate_version_specific_fields(chain_spec, version, &self.into())?;
|
||||
//!
|
||||
//! // custom validation logic - ensure that the custom field is not zero
|
||||
//! if self.custom == 0 {
|
||||
//! return Err(AttributesValidationError::invalid_params(
|
||||
//! CustomError::CustomFieldIsNotZero,
|
||||
//! ))
|
||||
//! }
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! /// Newtype around the payload builder attributes type
|
||||
//! #[derive(Clone, Debug, PartialEq, Eq)]
|
||||
//! pub struct CustomPayloadBuilderAttributes(EthPayloadBuilderAttributes);
|
||||
//!
|
||||
//! impl PayloadBuilderAttributes for CustomPayloadBuilderAttributes {
|
||||
//! type RpcPayloadAttributes = CustomPayloadAttributes;
|
||||
//! type Error = Infallible;
|
||||
//!
|
||||
//! fn try_new(parent: B256, attributes: CustomPayloadAttributes) -> Result<Self, Infallible> {
|
||||
//! Ok(Self(EthPayloadBuilderAttributes::new(parent, attributes.inner)))
|
||||
//! }
|
||||
//!
|
||||
//! fn parent(&self) -> B256 {
|
||||
//! self.0.parent
|
||||
//! }
|
||||
//!
|
||||
//! fn payload_id(&self) -> PayloadId {
|
||||
//! self.0.id
|
||||
//! }
|
||||
//!
|
||||
//! fn timestamp(&self) -> u64 {
|
||||
//! self.0.timestamp
|
||||
//! }
|
||||
//!
|
||||
//! fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
//! self.0.parent_beacon_block_root
|
||||
//! }
|
||||
//!
|
||||
//! fn suggested_fee_recipient(&self) -> Address {
|
||||
//! self.0.suggested_fee_recipient
|
||||
//! }
|
||||
//!
|
||||
//! fn prev_randao(&self) -> B256 {
|
||||
//! self.0.prev_randao
|
||||
//! }
|
||||
//!
|
||||
//! fn withdrawals(&self) -> &Vec<reth_primitives::Withdrawal> {
|
||||
//! &self.0.withdrawals
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! /// Custom engine types - uses a custom payload attributes RPC type, but uses the default
|
||||
//! /// payload builder attributes type.
|
||||
//! #[derive(Clone, Debug, Default)]
|
||||
//! #[non_exhaustive]
|
||||
//! pub struct CustomEngineTypes;
|
||||
//!
|
||||
//! impl EngineTypes for CustomEngineTypes {
|
||||
//! type PayloadAttributes = CustomPayloadAttributes;
|
||||
//! type PayloadBuilderAttributes = CustomPayloadBuilderAttributes;
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use reth_primitives::{ChainSpec, Hardfork};
|
||||
|
||||
/// Contains traits to abstract over payload attributes types and default implementations of the
|
||||
/// [PayloadAttributes] trait for ethereum mainnet and optimism types.
|
||||
pub mod traits;
|
||||
pub use traits::{PayloadAttributes, PayloadBuilderAttributes};
|
||||
|
||||
/// Contains error types used in the traits defined in this crate.
|
||||
pub mod error;
|
||||
pub use error::AttributesValidationError;
|
||||
|
||||
/// Contains types used in implementations of the [PayloadAttributes] trait.
|
||||
pub mod payload;
|
||||
pub use payload::PayloadOrAttributes;
|
||||
|
||||
/// The types that are used by the engine.
|
||||
pub trait EngineTypes: Send + Sync {
|
||||
/// The RPC payload attributes type the CL node emits via the engine API.
|
||||
type PayloadAttributes: PayloadAttributes + Unpin;
|
||||
|
||||
/// The payload attributes type that contains information about a running payload job.
|
||||
type PayloadBuilderAttributes: PayloadBuilderAttributes<RpcPayloadAttributes = Self::PayloadAttributes>
|
||||
+ Clone
|
||||
+ Unpin;
|
||||
|
||||
// TODO: payload type
|
||||
}
|
||||
|
||||
/// Validates the timestamp depending on the version called:
|
||||
///
|
||||
/// * If V2, this ensure that the payload timestamp is pre-Cancun.
|
||||
/// * If V3, this ensures that the payload timestamp is within the Cancun timestamp.
|
||||
///
|
||||
/// Otherwise, this will return [AttributesValidationError::UnsupportedFork].
|
||||
pub fn validate_payload_timestamp(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
timestamp: u64,
|
||||
) -> Result<(), AttributesValidationError> {
|
||||
let is_cancun = chain_spec.is_cancun_active_at_timestamp(timestamp);
|
||||
if version == EngineApiMessageVersion::V2 && is_cancun {
|
||||
// From the Engine API spec:
|
||||
//
|
||||
// ### Update the methods of previous forks
|
||||
//
|
||||
// This document defines how Cancun payload should be handled by the [`Shanghai
|
||||
// API`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md).
|
||||
//
|
||||
// For the following methods:
|
||||
//
|
||||
// - [`engine_forkchoiceUpdatedV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_forkchoiceupdatedv2)
|
||||
// - [`engine_newPayloadV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_newpayloadV2)
|
||||
// - [`engine_getPayloadV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_getpayloadv2)
|
||||
//
|
||||
// a validation **MUST** be added:
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// payload or payloadAttributes greater or equal to the Cancun activation timestamp.
|
||||
return Err(AttributesValidationError::UnsupportedFork)
|
||||
}
|
||||
|
||||
if version == EngineApiMessageVersion::V3 && !is_cancun {
|
||||
// From the Engine API spec:
|
||||
// <https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/cancun.md#specification-2>
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// the built payload does not fall within the time frame of the Cancun fork.
|
||||
return Err(AttributesValidationError::UnsupportedFork)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates the presence of the `withdrawals` field according to the payload timestamp.
|
||||
/// After Shanghai, withdrawals field must be [Some].
|
||||
/// Before Shanghai, withdrawals field must be [None];
|
||||
pub fn validate_withdrawals_presence(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
timestamp: u64,
|
||||
has_withdrawals: bool,
|
||||
) -> Result<(), AttributesValidationError> {
|
||||
let is_shanghai = chain_spec.fork(Hardfork::Shanghai).active_at_timestamp(timestamp);
|
||||
|
||||
match version {
|
||||
EngineApiMessageVersion::V1 => {
|
||||
if has_withdrawals {
|
||||
return Err(AttributesValidationError::WithdrawalsNotSupportedInV1)
|
||||
}
|
||||
if is_shanghai {
|
||||
return Err(AttributesValidationError::NoWithdrawalsPostShanghai)
|
||||
}
|
||||
}
|
||||
EngineApiMessageVersion::V2 | EngineApiMessageVersion::V3 => {
|
||||
if is_shanghai && !has_withdrawals {
|
||||
return Err(AttributesValidationError::NoWithdrawalsPostShanghai)
|
||||
}
|
||||
if !is_shanghai && has_withdrawals {
|
||||
return Err(AttributesValidationError::HasWithdrawalsPreShanghai)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate the presence of the `parentBeaconBlockRoot` field according to the payload
|
||||
/// timestamp.
|
||||
///
|
||||
/// After Cancun, `parentBeaconBlockRoot` field must be [Some].
|
||||
/// Before Cancun, `parentBeaconBlockRoot` field must be [None].
|
||||
///
|
||||
/// If the engine API message version is V1 or V2, and the payload attribute's timestamp is
|
||||
/// post-Cancun, then this will return [AttributesValidationError::UnsupportedFork].
|
||||
///
|
||||
/// If the payload attribute's timestamp is before the Cancun fork and the engine API message
|
||||
/// version is V3, then this will return [AttributesValidationError::UnsupportedFork].
|
||||
///
|
||||
/// If the engine API message version is V3, but the `parentBeaconBlockRoot` is [None], then
|
||||
/// this will return [AttributesValidationError::NoParentBeaconBlockRootPostCancun].
|
||||
///
|
||||
/// This implements the following Engine API spec rules:
|
||||
///
|
||||
/// 1. Client software **MUST** check that provided set of parameters and their fields strictly
|
||||
/// matches the expected one and return `-32602: Invalid params` error if this check fails. Any
|
||||
/// field having `null` value **MUST** be considered as not provided.
|
||||
///
|
||||
/// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `payloadAttributes`
|
||||
/// is set and the `payloadAttributes.timestamp` does not fall within the time frame of the
|
||||
/// Cancun fork.
|
||||
pub fn validate_parent_beacon_block_root_presence(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
timestamp: u64,
|
||||
has_parent_beacon_block_root: bool,
|
||||
) -> Result<(), AttributesValidationError> {
|
||||
// 1. Client software **MUST** check that provided set of parameters and their fields strictly
|
||||
// matches the expected one and return `-32602: Invalid params` error if this check fails.
|
||||
// Any field having `null` value **MUST** be considered as not provided.
|
||||
match version {
|
||||
EngineApiMessageVersion::V1 | EngineApiMessageVersion::V2 => {
|
||||
if has_parent_beacon_block_root {
|
||||
return Err(AttributesValidationError::ParentBeaconBlockRootNotSupportedBeforeV3)
|
||||
}
|
||||
}
|
||||
EngineApiMessageVersion::V3 => {
|
||||
if !has_parent_beacon_block_root {
|
||||
return Err(AttributesValidationError::NoParentBeaconBlockRootPostCancun)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `payloadAttributes` is set and the `payloadAttributes.timestamp` does not fall within the
|
||||
// time frame of the Cancun fork.
|
||||
validate_payload_timestamp(chain_spec, version, timestamp)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates the presence or exclusion of fork-specific fields based on the payload attributes
|
||||
/// and the message version.
|
||||
pub fn validate_version_specific_fields<Type>(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
payload_or_attrs: &PayloadOrAttributes<'_, Type>,
|
||||
) -> Result<(), AttributesValidationError>
|
||||
where
|
||||
Type: PayloadAttributes,
|
||||
{
|
||||
validate_withdrawals_presence(
|
||||
chain_spec,
|
||||
version,
|
||||
payload_or_attrs.timestamp(),
|
||||
payload_or_attrs.withdrawals().is_some(),
|
||||
)?;
|
||||
validate_parent_beacon_block_root_presence(
|
||||
chain_spec,
|
||||
version,
|
||||
payload_or_attrs.timestamp(),
|
||||
payload_or_attrs.parent_beacon_block_root().is_some(),
|
||||
)
|
||||
}
|
||||
|
||||
/// The version of Engine API message.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EngineApiMessageVersion {
|
||||
/// Version 1
|
||||
V1,
|
||||
/// Version 2
|
||||
///
|
||||
/// Added for shanghai hardfork.
|
||||
V2,
|
||||
/// Version 3
|
||||
///
|
||||
/// Added for cancun hardfork.
|
||||
V3,
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
use crate::PayloadAttributes;
|
||||
use reth_primitives::B256;
|
||||
use reth_rpc_types::engine::ExecutionPayload;
|
||||
|
||||
use reth_rpc_types::engine::{ExecutionPayload, PayloadAttributes};
|
||||
/// Either an [ExecutionPayload] or a [PayloadAttributes].
|
||||
pub(crate) enum PayloadOrAttributes<'a> {
|
||||
/// Either an [ExecutionPayload] or a types that implements the [PayloadAttributes] trait.
|
||||
#[derive(Debug)]
|
||||
pub enum PayloadOrAttributes<'a, AttributesType> {
|
||||
/// An [ExecutionPayload] and optional parent beacon block root.
|
||||
ExecutionPayload {
|
||||
/// The inner execution payload
|
||||
@ -10,14 +12,17 @@ pub(crate) enum PayloadOrAttributes<'a> {
|
||||
/// The parent beacon block root
|
||||
parent_beacon_block_root: Option<B256>,
|
||||
},
|
||||
/// A [PayloadAttributes].
|
||||
PayloadAttributes(&'a PayloadAttributes),
|
||||
/// A payload attributes type.
|
||||
PayloadAttributes(&'a AttributesType),
|
||||
}
|
||||
|
||||
impl<'a> PayloadOrAttributes<'a> {
|
||||
impl<'a, AttributesType> PayloadOrAttributes<'a, AttributesType>
|
||||
where
|
||||
AttributesType: PayloadAttributes,
|
||||
{
|
||||
/// Construct a [PayloadOrAttributes] from an [ExecutionPayload] and optional parent beacon
|
||||
/// block root.
|
||||
pub(crate) fn from_execution_payload(
|
||||
pub fn from_execution_payload(
|
||||
payload: &'a ExecutionPayload,
|
||||
parent_beacon_block_root: Option<B256>,
|
||||
) -> Self {
|
||||
@ -25,32 +30,35 @@ impl<'a> PayloadOrAttributes<'a> {
|
||||
}
|
||||
|
||||
/// Return the withdrawals for the payload or attributes.
|
||||
pub(crate) fn withdrawals(&self) -> Option<&Vec<reth_rpc_types::engine::payload::Withdrawal>> {
|
||||
pub fn withdrawals(&self) -> Option<&Vec<reth_rpc_types::engine::payload::Withdrawal>> {
|
||||
match self {
|
||||
Self::ExecutionPayload { payload, .. } => payload.withdrawals(),
|
||||
Self::PayloadAttributes(attributes) => attributes.withdrawals.as_ref(),
|
||||
Self::PayloadAttributes(attributes) => attributes.withdrawals(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the timestamp for the payload or attributes.
|
||||
pub(crate) fn timestamp(&self) -> u64 {
|
||||
pub fn timestamp(&self) -> u64 {
|
||||
match self {
|
||||
Self::ExecutionPayload { payload, .. } => payload.timestamp(),
|
||||
Self::PayloadAttributes(attributes) => attributes.timestamp,
|
||||
Self::PayloadAttributes(attributes) => attributes.timestamp(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the parent beacon block root for the payload or attributes.
|
||||
pub(crate) fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
pub fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
match self {
|
||||
Self::ExecutionPayload { parent_beacon_block_root, .. } => *parent_beacon_block_root,
|
||||
Self::PayloadAttributes(attributes) => attributes.parent_beacon_block_root,
|
||||
Self::PayloadAttributes(attributes) => attributes.parent_beacon_block_root(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a PayloadAttributes> for PayloadOrAttributes<'a> {
|
||||
fn from(attributes: &'a PayloadAttributes) -> Self {
|
||||
impl<'a, AttributesType> From<&'a AttributesType> for PayloadOrAttributes<'a, AttributesType>
|
||||
where
|
||||
AttributesType: PayloadAttributes,
|
||||
{
|
||||
fn from(attributes: &'a AttributesType) -> Self {
|
||||
Self::PayloadAttributes(attributes)
|
||||
}
|
||||
}
|
||||
189
crates/node-api/src/engine/traits.rs
Normal file
189
crates/node-api/src/engine/traits.rs
Normal file
@ -0,0 +1,189 @@
|
||||
use crate::{validate_version_specific_fields, AttributesValidationError, EngineApiMessageVersion};
|
||||
use reth_primitives::{
|
||||
revm::config::revm_spec_by_timestamp_after_merge,
|
||||
revm_primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, SpecId},
|
||||
Address, ChainSpec, Header, B256, U256,
|
||||
};
|
||||
use reth_rpc_types::engine::{
|
||||
OptimismPayloadAttributes, PayloadAttributes as EthPayloadAttributes, PayloadId, Withdrawal,
|
||||
};
|
||||
|
||||
/// This can be implemented by types that describe a currently running payload job.
|
||||
///
|
||||
/// This is used as a conversion type, transforming a payload attributes type that the engine API
|
||||
/// receives, into a type that the payload builder can use.
|
||||
pub trait PayloadBuilderAttributes: Send + Sync + std::fmt::Debug {
|
||||
/// The payload attributes that can be used to construct this type. Used as the argument in
|
||||
/// [PayloadBuilderAttributes::try_new].
|
||||
type RpcPayloadAttributes;
|
||||
/// The error type used in [PayloadBuilderAttributes::try_new].
|
||||
type Error: std::error::Error;
|
||||
|
||||
/// Creates a new payload builder for the given parent block and the attributes.
|
||||
///
|
||||
/// Derives the unique [PayloadId] for the given parent and attributes
|
||||
fn try_new(
|
||||
parent: B256,
|
||||
rpc_payload_attributes: Self::RpcPayloadAttributes,
|
||||
) -> Result<Self, Self::Error>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Returns the [PayloadId] for the running payload job.
|
||||
fn payload_id(&self) -> PayloadId;
|
||||
|
||||
/// Returns the parent block hash for the running payload job.
|
||||
fn parent(&self) -> B256;
|
||||
|
||||
/// Returns the timestmap for the running payload job.
|
||||
fn timestamp(&self) -> u64;
|
||||
|
||||
/// Returns the parent beacon block root for the running payload job, if it exists.
|
||||
fn parent_beacon_block_root(&self) -> Option<B256>;
|
||||
|
||||
/// Returns the suggested fee recipient for the running payload job.
|
||||
fn suggested_fee_recipient(&self) -> Address;
|
||||
|
||||
/// Returns the prevrandao field for the running payload job.
|
||||
fn prev_randao(&self) -> B256;
|
||||
|
||||
/// Returns the withdrawals for the running payload job.
|
||||
fn withdrawals(&self) -> &Vec<reth_primitives::Withdrawal>;
|
||||
|
||||
/// Returns the configured [CfgEnv] and [BlockEnv] for the targeted payload (that has the
|
||||
/// `parent` as its parent).
|
||||
///
|
||||
/// The `chain_spec` is used to determine the correct chain id and hardfork for the payload
|
||||
/// based on its timestamp.
|
||||
///
|
||||
/// Block related settings are derived from the `parent` block and the configured attributes.
|
||||
///
|
||||
/// NOTE: This is only intended for beacon consensus (after merge).
|
||||
fn cfg_and_block_env(&self, chain_spec: &ChainSpec, parent: &Header) -> (CfgEnv, BlockEnv) {
|
||||
// TODO: should be different once revm has configurable cfgenv
|
||||
// configure evm env based on parent block
|
||||
let mut cfg = CfgEnv::default();
|
||||
cfg.chain_id = chain_spec.chain().id();
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
cfg.optimism = chain_spec.is_optimism();
|
||||
}
|
||||
|
||||
// ensure we're not missing any timestamp based hardforks
|
||||
cfg.spec_id = revm_spec_by_timestamp_after_merge(chain_spec, self.timestamp());
|
||||
|
||||
// if the parent block did not have excess blob gas (i.e. it was pre-cancun), but it is
|
||||
// cancun now, we need to set the excess blob gas to the default value
|
||||
let blob_excess_gas_and_price = parent
|
||||
.next_block_excess_blob_gas()
|
||||
.map_or_else(
|
||||
|| {
|
||||
if cfg.spec_id == SpecId::CANCUN {
|
||||
// default excess blob gas is zero
|
||||
Some(0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
Some,
|
||||
)
|
||||
.map(BlobExcessGasAndPrice::new);
|
||||
|
||||
let block_env = BlockEnv {
|
||||
number: U256::from(parent.number + 1),
|
||||
coinbase: self.suggested_fee_recipient(),
|
||||
timestamp: U256::from(self.timestamp()),
|
||||
difficulty: U256::ZERO,
|
||||
prevrandao: Some(self.prev_randao()),
|
||||
gas_limit: U256::from(parent.gas_limit),
|
||||
// calculate basefee based on parent block's gas usage
|
||||
basefee: U256::from(
|
||||
parent
|
||||
.next_block_base_fee(chain_spec.base_fee_params(self.timestamp()))
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
// calculate excess gas based on parent block's blob gas usage
|
||||
blob_excess_gas_and_price,
|
||||
};
|
||||
|
||||
(cfg, block_env)
|
||||
}
|
||||
}
|
||||
|
||||
/// The execution payload attribute type the CL node emits via the engine API.
|
||||
/// This trait should be implemented by types that could be used to spawn a payload job.
|
||||
///
|
||||
/// This type is emitted as part of the forkchoiceUpdated call
|
||||
pub trait PayloadAttributes:
|
||||
serde::de::DeserializeOwned + serde::Serialize + std::fmt::Debug + Clone + Send + Sync + 'static
|
||||
{
|
||||
/// Returns the timestamp to be used in the payload job.
|
||||
fn timestamp(&self) -> u64;
|
||||
|
||||
/// Returns the withdrawals for the given payload attributes.
|
||||
fn withdrawals(&self) -> Option<&Vec<Withdrawal>>;
|
||||
|
||||
/// Return the parent beacon block root for the payload attributes.
|
||||
fn parent_beacon_block_root(&self) -> Option<B256>;
|
||||
|
||||
/// Ensures that the payload attributes are valid for the given [ChainSpec] and
|
||||
/// [EngineApiMessageVersion].
|
||||
fn ensure_well_formed_attributes(
|
||||
&self,
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
) -> Result<(), AttributesValidationError>;
|
||||
}
|
||||
|
||||
impl PayloadAttributes for EthPayloadAttributes {
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.timestamp
|
||||
}
|
||||
|
||||
fn withdrawals(&self) -> Option<&Vec<Withdrawal>> {
|
||||
self.withdrawals.as_ref()
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
self.parent_beacon_block_root
|
||||
}
|
||||
|
||||
fn ensure_well_formed_attributes(
|
||||
&self,
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
) -> Result<(), AttributesValidationError> {
|
||||
validate_version_specific_fields(chain_spec, version, &self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl PayloadAttributes for OptimismPayloadAttributes {
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.payload_attributes.timestamp
|
||||
}
|
||||
|
||||
fn withdrawals(&self) -> Option<&Vec<Withdrawal>> {
|
||||
self.payload_attributes.withdrawals.as_ref()
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
self.payload_attributes.parent_beacon_block_root
|
||||
}
|
||||
|
||||
fn ensure_well_formed_attributes(
|
||||
&self,
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
) -> Result<(), AttributesValidationError> {
|
||||
validate_version_specific_fields(chain_spec, version, &self.into())?;
|
||||
|
||||
if self.gas_limit.is_none() && chain_spec.is_optimism() {
|
||||
return Err(AttributesValidationError::InvalidParams(
|
||||
"MissingGasLimitInPayloadAttributes".to_string().into(),
|
||||
))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
18
crates/node-api/src/lib.rs
Normal file
18
crates/node-api/src/lib.rs
Normal file
@ -0,0 +1,18 @@
|
||||
//! Standalone crate for Reth configuration traits and builder types.
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
|
||||
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
|
||||
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
/// Traits, validation methods, and helper types used to abstract over engine types.
|
||||
///
|
||||
/// Notably contains the [EngineTypes] trait and implementations for ethereum mainnet types.
|
||||
pub mod engine;
|
||||
pub use engine::{
|
||||
validate_payload_timestamp, validate_version_specific_fields, validate_withdrawals_presence,
|
||||
AttributesValidationError, EngineApiMessageVersion, EngineTypes, PayloadAttributes,
|
||||
PayloadBuilderAttributes, PayloadOrAttributes,
|
||||
};
|
||||
21
crates/node-builder/Cargo.toml
Normal file
21
crates/node-builder/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "reth-node-builder"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-primitives.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-rpc-types.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
# io
|
||||
serde.workspace = true
|
||||
23
crates/node-builder/src/engine.rs
Normal file
23
crates/node-builder/src/engine.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_payload_builder::{EthPayloadBuilderAttributes, OptimismPayloadBuilderAttributes};
|
||||
use reth_rpc_types::engine::{OptimismPayloadAttributes, PayloadAttributes};
|
||||
|
||||
/// The types used in the default mainnet ethereum beacon consensus engine.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub struct EthEngineTypes;
|
||||
|
||||
impl EngineTypes for EthEngineTypes {
|
||||
type PayloadAttributes = PayloadAttributes;
|
||||
type PayloadBuilderAttributes = EthPayloadBuilderAttributes;
|
||||
}
|
||||
|
||||
/// The types used in the optimism beacon consensus engine.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub struct OptimismEngineTypes;
|
||||
|
||||
impl EngineTypes for OptimismEngineTypes {
|
||||
type PayloadAttributes = OptimismPayloadAttributes;
|
||||
type PayloadBuilderAttributes = OptimismPayloadBuilderAttributes;
|
||||
}
|
||||
13
crates/node-builder/src/lib.rs
Normal file
13
crates/node-builder/src/lib.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! Standalone crate for Reth configuration and builder types.
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
|
||||
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
|
||||
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
/// Exports commonly used concrete instances of the [EngineTypes](reth_node_api::EngineTypes)
|
||||
/// trait.
|
||||
pub mod engine;
|
||||
pub use engine::{EthEngineTypes, OptimismEngineTypes};
|
||||
@ -20,6 +20,7 @@ reth-provider.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-interfaces.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
# ethereum
|
||||
alloy-rlp.workspace = true
|
||||
|
||||
@ -7,13 +7,15 @@
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
use crate::metrics::PayloadBuilderMetrics;
|
||||
use alloy_rlp::Encodable;
|
||||
use futures_core::ready;
|
||||
use futures_util::FutureExt;
|
||||
use reth_interfaces::RethResult;
|
||||
use reth_node_api::PayloadBuilderAttributes;
|
||||
use reth_payload_builder::{
|
||||
database::CachedReads, error::PayloadBuilderError, BuiltPayload, KeepPayloadJobAlive,
|
||||
PayloadBuilderAttributes, PayloadId, PayloadJob, PayloadJobGenerator,
|
||||
PayloadId, PayloadJob, PayloadJobGenerator,
|
||||
};
|
||||
use reth_primitives::{
|
||||
bytes::BytesMut,
|
||||
@ -52,8 +54,6 @@ use tokio::{
|
||||
};
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
use crate::metrics::PayloadBuilderMetrics;
|
||||
|
||||
mod metrics;
|
||||
|
||||
/// The [`PayloadJobGenerator`] that creates [`BasicPayloadJob`]s.
|
||||
@ -153,27 +153,28 @@ where
|
||||
Pool: TransactionPool + Unpin + 'static,
|
||||
Tasks: TaskSpawner + Clone + Unpin + 'static,
|
||||
Builder: PayloadBuilder<Pool, Client> + Unpin + 'static,
|
||||
<Builder as PayloadBuilder<Pool, Client>>::Attributes: Unpin + Clone,
|
||||
{
|
||||
type Job = BasicPayloadJob<Client, Pool, Tasks, Builder>;
|
||||
|
||||
fn new_payload_job(
|
||||
&self,
|
||||
attributes: PayloadBuilderAttributes,
|
||||
attributes: <Self::Job as PayloadJob>::PayloadAttributes,
|
||||
) -> Result<Self::Job, PayloadBuilderError> {
|
||||
let parent_block = if attributes.parent.is_zero() {
|
||||
let parent_block = if attributes.parent().is_zero() {
|
||||
// use latest block if parent is zero: genesis block
|
||||
self.client
|
||||
.block_by_number_or_tag(BlockNumberOrTag::Latest)?
|
||||
.ok_or_else(|| PayloadBuilderError::MissingParentBlock(attributes.parent))?
|
||||
.ok_or_else(|| PayloadBuilderError::MissingParentBlock(attributes.parent()))?
|
||||
.seal_slow()
|
||||
} else {
|
||||
let block = self
|
||||
.client
|
||||
.find_block_by_hash(attributes.parent, BlockSource::Any)?
|
||||
.ok_or_else(|| PayloadBuilderError::MissingParentBlock(attributes.parent))?;
|
||||
.find_block_by_hash(attributes.parent(), BlockSource::Any)?
|
||||
.ok_or_else(|| PayloadBuilderError::MissingParentBlock(attributes.parent()))?;
|
||||
|
||||
// we already know the hash, so we can seal it
|
||||
block.seal(attributes.parent)
|
||||
block.seal(attributes.parent())
|
||||
};
|
||||
|
||||
let config = PayloadConfig::new(
|
||||
@ -183,7 +184,7 @@ where
|
||||
Arc::clone(&self.chain_spec),
|
||||
);
|
||||
|
||||
let until = self.job_deadline(config.attributes.timestamp);
|
||||
let until = self.job_deadline(config.attributes.timestamp());
|
||||
let deadline = Box::pin(tokio::time::sleep_until(until));
|
||||
|
||||
let cached_reads = self.maybe_pre_cached(config.parent_block.hash());
|
||||
@ -325,9 +326,12 @@ impl Default for BasicPayloadJobGeneratorConfig {
|
||||
|
||||
/// A basic payload job that continuously builds a payload with the best transactions from the pool.
|
||||
#[derive(Debug)]
|
||||
pub struct BasicPayloadJob<Client, Pool, Tasks, Builder> {
|
||||
pub struct BasicPayloadJob<Client, Pool, Tasks, Builder>
|
||||
where
|
||||
Builder: PayloadBuilder<Pool, Client>,
|
||||
{
|
||||
/// The configuration for how the payload will be created.
|
||||
config: PayloadConfig,
|
||||
config: PayloadConfig<Builder::Attributes>,
|
||||
/// The client that can interact with the chain.
|
||||
client: Client,
|
||||
/// The transaction pool.
|
||||
@ -363,6 +367,7 @@ where
|
||||
Pool: TransactionPool + Unpin + 'static,
|
||||
Tasks: TaskSpawner + Clone + 'static,
|
||||
Builder: PayloadBuilder<Pool, Client> + Unpin + 'static,
|
||||
<Builder as PayloadBuilder<Pool, Client>>::Attributes: Unpin + Clone,
|
||||
{
|
||||
type Output = Result<(), PayloadBuilderError>;
|
||||
|
||||
@ -452,7 +457,9 @@ where
|
||||
Pool: TransactionPool + Unpin + 'static,
|
||||
Tasks: TaskSpawner + Clone + 'static,
|
||||
Builder: PayloadBuilder<Pool, Client> + Unpin + 'static,
|
||||
<Builder as PayloadBuilder<Pool, Client>>::Attributes: PayloadBuilderAttributes + Unpin + Clone,
|
||||
{
|
||||
type PayloadAttributes = Builder::Attributes;
|
||||
type ResolvePayloadFuture = ResolveBestPayload;
|
||||
|
||||
fn best_payload(&self) -> Result<Arc<BuiltPayload>, PayloadBuilderError> {
|
||||
@ -469,7 +476,7 @@ where
|
||||
build_empty_payload(&self.client, self.config.clone()).map(Arc::new)
|
||||
}
|
||||
|
||||
fn payload_attributes(&self) -> Result<PayloadBuilderAttributes, PayloadBuilderError> {
|
||||
fn payload_attributes(&self) -> Result<Self::PayloadAttributes, PayloadBuilderError> {
|
||||
Ok(self.config.attributes.clone())
|
||||
}
|
||||
|
||||
@ -490,6 +497,9 @@ where
|
||||
best_payload: None,
|
||||
};
|
||||
|
||||
// TODO: create optimism payload job, that wraps this type, that implements PayloadJob
|
||||
// with this branch. remove this branch from the non-op code. remove
|
||||
// `on_missing_payload` requirement from builder trait
|
||||
if let Some(payload) = self.builder.on_missing_payload(args) {
|
||||
return (
|
||||
ResolveBestPayload { best_payload: Some(payload), maybe_better, empty_payload },
|
||||
@ -618,7 +628,7 @@ impl Drop for Cancelled {
|
||||
|
||||
/// Static config for how to build a payload.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PayloadConfig {
|
||||
pub struct PayloadConfig<Attributes> {
|
||||
/// Pre-configured block environment.
|
||||
pub initialized_block_env: BlockEnv,
|
||||
/// Configuration for the environment.
|
||||
@ -628,29 +638,27 @@ pub struct PayloadConfig {
|
||||
/// Block extra data.
|
||||
pub extra_data: Bytes,
|
||||
/// Requested attributes for the payload.
|
||||
pub attributes: PayloadBuilderAttributes,
|
||||
pub attributes: Attributes,
|
||||
/// The chain spec.
|
||||
pub chain_spec: Arc<ChainSpec>,
|
||||
}
|
||||
|
||||
impl PayloadConfig {
|
||||
impl<Attributes> PayloadConfig<Attributes> {
|
||||
/// Returns an owned instance of the [PayloadConfig]'s extra_data bytes.
|
||||
pub fn extra_data(&self) -> Bytes {
|
||||
self.extra_data.clone()
|
||||
}
|
||||
|
||||
/// Returns the payload id.
|
||||
pub fn payload_id(&self) -> PayloadId {
|
||||
self.attributes.id
|
||||
}
|
||||
}
|
||||
|
||||
impl PayloadConfig {
|
||||
impl<Attributes> PayloadConfig<Attributes>
|
||||
where
|
||||
Attributes: PayloadBuilderAttributes,
|
||||
{
|
||||
/// Create new payload config.
|
||||
pub fn new(
|
||||
parent_block: Arc<SealedBlock>,
|
||||
extra_data: Bytes,
|
||||
attributes: PayloadBuilderAttributes,
|
||||
attributes: Attributes,
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
) -> Self {
|
||||
// configure evm env based on parent block
|
||||
@ -666,6 +674,11 @@ impl PayloadConfig {
|
||||
chain_spec,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the payload id.
|
||||
pub fn payload_id(&self) -> PayloadId {
|
||||
self.attributes.payload_id()
|
||||
}
|
||||
}
|
||||
|
||||
/// The possible outcomes of a payload building attempt.
|
||||
@ -695,7 +708,7 @@ pub enum BuildOutcome {
|
||||
/// building process. It holds references to the Ethereum client, transaction pool, cached reads,
|
||||
/// payload configuration, cancellation status, and the best payload achieved so far.
|
||||
#[derive(Debug)]
|
||||
pub struct BuildArguments<Pool, Client> {
|
||||
pub struct BuildArguments<Pool, Client, Attributes> {
|
||||
/// How to interact with the chain.
|
||||
pub client: Client,
|
||||
/// The transaction pool.
|
||||
@ -703,20 +716,20 @@ pub struct BuildArguments<Pool, Client> {
|
||||
/// Previously cached disk reads
|
||||
pub cached_reads: CachedReads,
|
||||
/// How to configure the payload.
|
||||
pub config: PayloadConfig,
|
||||
pub config: PayloadConfig<Attributes>,
|
||||
/// A marker that can be used to cancel the job.
|
||||
pub cancel: Cancelled,
|
||||
/// The best payload achieved so far.
|
||||
pub best_payload: Option<Arc<BuiltPayload>>,
|
||||
}
|
||||
|
||||
impl<Pool, Client> BuildArguments<Pool, Client> {
|
||||
impl<Pool, Client, Attributes> BuildArguments<Pool, Client, Attributes> {
|
||||
/// Create new build arguments.
|
||||
pub fn new(
|
||||
client: Client,
|
||||
pool: Pool,
|
||||
cached_reads: CachedReads,
|
||||
config: PayloadConfig,
|
||||
config: PayloadConfig<Attributes>,
|
||||
cancel: Cancelled,
|
||||
best_payload: Option<Arc<BuiltPayload>>,
|
||||
) -> Self {
|
||||
@ -733,6 +746,9 @@ impl<Pool, Client> BuildArguments<Pool, Client> {
|
||||
/// Generic parameters `Pool` and `Client` represent the transaction pool and
|
||||
/// Ethereum client types.
|
||||
pub trait PayloadBuilder<Pool, Client>: Send + Sync + Clone {
|
||||
/// The payload attributes type to accept for building.
|
||||
type Attributes: PayloadBuilderAttributes + Send + Sync + std::fmt::Debug;
|
||||
|
||||
/// Tries to build a transaction payload using provided arguments.
|
||||
///
|
||||
/// Constructs a transaction payload based on the given arguments,
|
||||
@ -747,7 +763,7 @@ pub trait PayloadBuilder<Pool, Client>: Send + Sync + Clone {
|
||||
/// A `Result` indicating the build outcome or an error.
|
||||
fn try_build(
|
||||
&self,
|
||||
args: BuildArguments<Pool, Client>,
|
||||
args: BuildArguments<Pool, Client, Self::Attributes>,
|
||||
) -> Result<BuildOutcome, PayloadBuilderError>;
|
||||
|
||||
/// Invoked when the payload job is being resolved and there is no payload yet.
|
||||
@ -755,19 +771,23 @@ pub trait PayloadBuilder<Pool, Client>: Send + Sync + Clone {
|
||||
/// If this returns a payload, it will be used as the final payload for the job.
|
||||
///
|
||||
/// TODO(mattsse): This needs to be refined a bit because this only exists for OP atm
|
||||
fn on_missing_payload(&self, args: BuildArguments<Pool, Client>) -> Option<Arc<BuiltPayload>> {
|
||||
fn on_missing_payload(
|
||||
&self,
|
||||
args: BuildArguments<Pool, Client, Self::Attributes>,
|
||||
) -> Option<Arc<BuiltPayload>> {
|
||||
let _args = args;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an empty payload without any transactions.
|
||||
fn build_empty_payload<Client>(
|
||||
fn build_empty_payload<Client, Attributes>(
|
||||
client: &Client,
|
||||
config: PayloadConfig,
|
||||
config: PayloadConfig<Attributes>,
|
||||
) -> Result<BuiltPayload, PayloadBuilderError>
|
||||
where
|
||||
Client: StateProviderFactory,
|
||||
Attributes: PayloadBuilderAttributes,
|
||||
{
|
||||
let extra_data = config.extra_data();
|
||||
let PayloadConfig {
|
||||
@ -808,7 +828,7 @@ where
|
||||
})?;
|
||||
|
||||
let WithdrawalsOutcome { withdrawals_root, withdrawals } =
|
||||
commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals).map_err(|err| {
|
||||
commit_withdrawals(&mut db, &chain_spec, attributes.timestamp(), attributes.withdrawals().clone()).map_err(|err| {
|
||||
warn!(target: "payload_builder", parent_hash=%parent_block.hash,?err, "failed to commit withdrawals for empty payload");
|
||||
err
|
||||
})?;
|
||||
@ -834,8 +854,8 @@ where
|
||||
withdrawals_root,
|
||||
receipts_root: EMPTY_RECEIPTS,
|
||||
logs_bloom: Default::default(),
|
||||
timestamp: attributes.timestamp,
|
||||
mix_hash: attributes.prev_randao,
|
||||
timestamp: attributes.timestamp(),
|
||||
mix_hash: attributes.prev_randao(),
|
||||
nonce: BEACON_NONCE,
|
||||
base_fee_per_gas: Some(base_fee),
|
||||
number: parent_block.number + 1,
|
||||
@ -845,13 +865,13 @@ where
|
||||
extra_data,
|
||||
blob_gas_used: None,
|
||||
excess_blob_gas: None,
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root,
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root(),
|
||||
};
|
||||
|
||||
let block = Block { header, body: vec![], ommers: vec![], withdrawals };
|
||||
let sealed_block = block.seal_slow();
|
||||
|
||||
Ok(BuiltPayload::new(attributes.id, sealed_block, U256::ZERO))
|
||||
Ok(BuiltPayload::new(attributes.payload_id(), sealed_block, U256::ZERO))
|
||||
}
|
||||
|
||||
/// Represents the outcome of committing withdrawals to the runtime database and post state.
|
||||
@ -919,16 +939,17 @@ pub fn commit_withdrawals<DB: Database<Error = ProviderError>>(
|
||||
///
|
||||
/// This uses [apply_beacon_root_contract_call] to ultimately apply the beacon root contract state
|
||||
/// change.
|
||||
pub fn pre_block_beacon_root_contract_call<DB: Database + DatabaseCommit>(
|
||||
pub fn pre_block_beacon_root_contract_call<DB: Database + DatabaseCommit, Attributes>(
|
||||
db: &mut DB,
|
||||
chain_spec: &ChainSpec,
|
||||
block_number: u64,
|
||||
initialized_cfg: &CfgEnv,
|
||||
initialized_block_env: &BlockEnv,
|
||||
attributes: &PayloadBuilderAttributes,
|
||||
attributes: &Attributes,
|
||||
) -> Result<(), PayloadBuilderError>
|
||||
where
|
||||
DB::Error: std::fmt::Display,
|
||||
Attributes: PayloadBuilderAttributes,
|
||||
{
|
||||
// Configure the environment for the block.
|
||||
let env = Env {
|
||||
@ -944,9 +965,9 @@ where
|
||||
// initialize a block from the env, because the pre block call needs the block itself
|
||||
apply_beacon_root_contract_call(
|
||||
chain_spec,
|
||||
attributes.timestamp,
|
||||
attributes.timestamp(),
|
||||
block_number,
|
||||
attributes.parent_beacon_block_root,
|
||||
attributes.parent_beacon_block_root(),
|
||||
&mut evm_pre_block,
|
||||
)
|
||||
.map_err(|err| PayloadBuilderError::Internal(err.into()))
|
||||
|
||||
@ -20,6 +20,7 @@ reth-interfaces.workspace = true
|
||||
reth-rpc-types-compat.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
# ethereum
|
||||
alloy-rlp.workspace = true
|
||||
@ -29,6 +30,7 @@ revm-primitives.workspace = true
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
tokio-stream.workspace = true
|
||||
futures-util.workspace = true
|
||||
async-trait.workspace = true
|
||||
|
||||
# metrics
|
||||
reth-metrics.workspace = true
|
||||
@ -39,6 +41,9 @@ thiserror.workspace = true
|
||||
sha2 = { version = "0.10", default-features = false }
|
||||
tracing.workspace = true
|
||||
|
||||
# serde, for traits
|
||||
serde.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
revm.workspace = true
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
//! use std::pin::Pin;
|
||||
//! use std::sync::Arc;
|
||||
//! use std::task::{Context, Poll};
|
||||
//! use reth_payload_builder::{BuiltPayload, KeepPayloadJobAlive, PayloadBuilderAttributes, PayloadJob, PayloadJobGenerator};
|
||||
//! use reth_payload_builder::{BuiltPayload, KeepPayloadJobAlive, EthPayloadBuilderAttributes, PayloadJob, PayloadJobGenerator};
|
||||
//! use reth_payload_builder::error::PayloadBuilderError;
|
||||
//! use reth_primitives::{Block, Header, U256};
|
||||
//!
|
||||
@ -39,7 +39,7 @@
|
||||
//! type Job = EmptyBlockPayloadJob;
|
||||
//!
|
||||
//! /// This is invoked when the node receives payload attributes from the beacon node via `engine_forkchoiceUpdatedV1`
|
||||
//! fn new_payload_job(&self, attr: PayloadBuilderAttributes) -> Result<Self::Job, PayloadBuilderError> {
|
||||
//! fn new_payload_job(&self, attr: EthPayloadBuilderAttributes) -> Result<Self::Job, PayloadBuilderError> {
|
||||
//! Ok(EmptyBlockPayloadJob{ attributes: attr,})
|
||||
//! }
|
||||
//!
|
||||
@ -47,10 +47,11 @@
|
||||
//!
|
||||
//! /// A [PayloadJob] that builds empty blocks.
|
||||
//! pub struct EmptyBlockPayloadJob {
|
||||
//! attributes: PayloadBuilderAttributes,
|
||||
//! attributes: EthPayloadBuilderAttributes,
|
||||
//! }
|
||||
//!
|
||||
//! impl PayloadJob for EmptyBlockPayloadJob {
|
||||
//! type PayloadAttributes = EthPayloadBuilderAttributes;
|
||||
//! type ResolvePayloadFuture = futures_util::future::Ready<Result<Arc<BuiltPayload>, PayloadBuilderError>>;
|
||||
//!
|
||||
//! fn best_payload(&self) -> Result<Arc<BuiltPayload>, PayloadBuilderError> {
|
||||
@ -68,7 +69,7 @@
|
||||
//! Ok(Arc::new(payload))
|
||||
//! }
|
||||
//!
|
||||
//! fn payload_attributes(&self) -> Result<PayloadBuilderAttributes, PayloadBuilderError> {
|
||||
//! fn payload_attributes(&self) -> Result<EthPayloadBuilderAttributes, PayloadBuilderError> {
|
||||
//! Ok(self.attributes.clone())
|
||||
//! }
|
||||
//!
|
||||
@ -111,7 +112,7 @@ pub mod noop;
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
pub mod test_utils;
|
||||
|
||||
pub use payload::{BuiltPayload, PayloadBuilderAttributes};
|
||||
pub use payload::{BuiltPayload, EthPayloadBuilderAttributes, OptimismPayloadBuilderAttributes};
|
||||
pub use reth_rpc_types::engine::PayloadId;
|
||||
pub use service::{PayloadBuilderHandle, PayloadBuilderService, PayloadStore};
|
||||
pub use traits::{KeepPayloadJobAlive, PayloadJob, PayloadJobGenerator};
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::{service::PayloadServiceCommand, PayloadBuilderHandle};
|
||||
use futures_util::{ready, StreamExt};
|
||||
use reth_node_api::{EngineTypes, PayloadBuilderAttributes};
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
@ -12,21 +13,27 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
|
||||
/// A service task that does not build any payloads.
|
||||
#[derive(Debug)]
|
||||
pub struct NoopPayloadBuilderService {
|
||||
pub struct NoopPayloadBuilderService<Engine: EngineTypes> {
|
||||
/// Receiver half of the command channel.
|
||||
command_rx: UnboundedReceiverStream<PayloadServiceCommand>,
|
||||
command_rx: UnboundedReceiverStream<PayloadServiceCommand<Engine::PayloadBuilderAttributes>>,
|
||||
}
|
||||
|
||||
impl NoopPayloadBuilderService {
|
||||
impl<Engine> NoopPayloadBuilderService<Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
/// Creates a new [NoopPayloadBuilderService].
|
||||
pub fn new() -> (Self, PayloadBuilderHandle) {
|
||||
pub fn new() -> (Self, PayloadBuilderHandle<Engine>) {
|
||||
let (service_tx, command_rx) = mpsc::unbounded_channel();
|
||||
let handle = PayloadBuilderHandle::new(service_tx);
|
||||
(Self { command_rx: UnboundedReceiverStream::new(command_rx) }, handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for NoopPayloadBuilderService {
|
||||
impl<Engine> Future for NoopPayloadBuilderService<Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
//! Contains types required for building a payload.
|
||||
|
||||
use std::convert::Infallible;
|
||||
|
||||
use alloy_rlp::{Encodable, Error as DecodeError};
|
||||
use reth_node_api::PayloadBuilderAttributes;
|
||||
use reth_primitives::{
|
||||
revm::config::revm_spec_by_timestamp_after_merge,
|
||||
revm_primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, SpecId},
|
||||
Address, BlobTransactionSidecar, ChainSpec, Header, SealedBlock, Withdrawal, B256, U256,
|
||||
Address, BlobTransactionSidecar, ChainSpec, Header, SealedBlock, TransactionSigned, Withdrawal,
|
||||
B256, U256,
|
||||
};
|
||||
use reth_rpc_types::engine::{
|
||||
ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadV1, PayloadAttributes,
|
||||
PayloadId,
|
||||
ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadV1,
|
||||
OptimismPayloadAttributes, PayloadAttributes, PayloadId,
|
||||
};
|
||||
|
||||
use reth_rpc_types_compat::engine::payload::{
|
||||
block_to_payload_v3, convert_block_to_payload_field_v2,
|
||||
convert_standalone_withdraw_to_withdrawal, try_block_to_payload_v1,
|
||||
};
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
use reth_primitives::TransactionSigned;
|
||||
|
||||
/// Contains the built payload.
|
||||
///
|
||||
/// According to the [engine API specification](https://github.com/ethereum/execution-apis/blob/main/src/engine/README.md) the execution layer should build the initial version of the payload with an empty transaction set and then keep update it in order to maximize the revenue.
|
||||
@ -123,7 +123,7 @@ impl From<BuiltPayload> for ExecutionPayloadEnvelopeV3 {
|
||||
|
||||
/// Container type for all components required to build a payload.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PayloadBuilderAttributes {
|
||||
pub struct EthPayloadBuilderAttributes {
|
||||
/// Id of the payload
|
||||
pub id: PayloadId,
|
||||
/// Parent block to build the payload on top
|
||||
@ -140,71 +140,11 @@ pub struct PayloadBuilderAttributes {
|
||||
pub withdrawals: Vec<Withdrawal>,
|
||||
/// Root of the parent beacon block
|
||||
pub parent_beacon_block_root: Option<B256>,
|
||||
/// Optimism Payload Builder Attributes
|
||||
#[cfg(feature = "optimism")]
|
||||
pub optimism_payload_attributes: OptimismPayloadBuilderAttributes,
|
||||
}
|
||||
|
||||
/// Optimism Payload Builder Attributes
|
||||
#[cfg(feature = "optimism")]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OptimismPayloadBuilderAttributes {
|
||||
/// NoTxPool option for the generated payload
|
||||
pub no_tx_pool: bool,
|
||||
/// Transactions for the generated payload
|
||||
pub transactions: Vec<TransactionSigned>,
|
||||
/// The gas limit for the generated payload
|
||||
pub gas_limit: Option<u64>,
|
||||
}
|
||||
// === impl EthPayloadBuilderAttributes ===
|
||||
|
||||
// === impl PayloadBuilderAttributes ===
|
||||
|
||||
impl PayloadBuilderAttributes {
|
||||
/// Creates a new payload builder for the given parent block and the attributes.
|
||||
///
|
||||
/// Derives the unique [PayloadId] for the given parent and attributes
|
||||
pub fn try_new(parent: B256, attributes: PayloadAttributes) -> Result<Self, DecodeError> {
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
let id = payload_id(&parent, &attributes);
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
let (id, transactions) = {
|
||||
let transactions: Vec<_> = attributes
|
||||
.optimism_payload_attributes
|
||||
.transactions
|
||||
.as_deref()
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.map(|tx| TransactionSigned::decode_enveloped(&mut tx.as_ref()))
|
||||
.collect::<Result<_, _>>()?;
|
||||
(payload_id(&parent, &attributes, &transactions), transactions)
|
||||
};
|
||||
|
||||
let withdraw = attributes.withdrawals.map(
|
||||
|withdrawals: Vec<reth_rpc_types::engine::payload::Withdrawal>| {
|
||||
withdrawals
|
||||
.into_iter()
|
||||
.map(convert_standalone_withdraw_to_withdrawal) // Removed the parentheses here
|
||||
.collect::<Vec<_>>()
|
||||
},
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
parent,
|
||||
timestamp: attributes.timestamp,
|
||||
suggested_fee_recipient: attributes.suggested_fee_recipient,
|
||||
prev_randao: attributes.prev_randao,
|
||||
withdrawals: withdraw.unwrap_or_default(),
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root,
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism_payload_attributes: OptimismPayloadBuilderAttributes {
|
||||
no_tx_pool: attributes.optimism_payload_attributes.no_tx_pool.unwrap_or_default(),
|
||||
transactions,
|
||||
gas_limit: attributes.optimism_payload_attributes.gas_limit,
|
||||
},
|
||||
})
|
||||
}
|
||||
impl EthPayloadBuilderAttributes {
|
||||
/// Returns the configured [CfgEnv] and [BlockEnv] for the targeted payload (that has the
|
||||
/// `parent` as its parent).
|
||||
///
|
||||
@ -268,16 +208,205 @@ impl PayloadBuilderAttributes {
|
||||
pub fn payload_id(&self) -> PayloadId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Creates a new payload builder for the given parent block and the attributes.
|
||||
///
|
||||
/// Derives the unique [PayloadId] for the given parent and attributes
|
||||
pub fn new(parent: B256, attributes: PayloadAttributes) -> Self {
|
||||
let id = payload_id(&parent, &attributes);
|
||||
|
||||
let withdraw = attributes.withdrawals.map(
|
||||
|withdrawals: Vec<reth_rpc_types::engine::payload::Withdrawal>| {
|
||||
withdrawals
|
||||
.into_iter()
|
||||
.map(convert_standalone_withdraw_to_withdrawal) // Removed the parentheses here
|
||||
.collect::<Vec<_>>()
|
||||
},
|
||||
);
|
||||
|
||||
Self {
|
||||
id,
|
||||
parent,
|
||||
timestamp: attributes.timestamp,
|
||||
suggested_fee_recipient: attributes.suggested_fee_recipient,
|
||||
prev_randao: attributes.prev_randao,
|
||||
withdrawals: withdraw.unwrap_or_default(),
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the payload id for the configured payload
|
||||
impl PayloadBuilderAttributes for EthPayloadBuilderAttributes {
|
||||
type RpcPayloadAttributes = PayloadAttributes;
|
||||
type Error = Infallible;
|
||||
|
||||
/// Creates a new payload builder for the given parent block and the attributes.
|
||||
///
|
||||
/// Derives the unique [PayloadId] for the given parent and attributes
|
||||
fn try_new(parent: B256, attributes: PayloadAttributes) -> Result<Self, Infallible> {
|
||||
Ok(Self::new(parent, attributes))
|
||||
}
|
||||
|
||||
fn parent(&self) -> B256 {
|
||||
self.parent
|
||||
}
|
||||
|
||||
fn payload_id(&self) -> PayloadId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.timestamp
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
self.parent_beacon_block_root
|
||||
}
|
||||
|
||||
fn suggested_fee_recipient(&self) -> Address {
|
||||
self.suggested_fee_recipient
|
||||
}
|
||||
|
||||
fn prev_randao(&self) -> B256 {
|
||||
self.prev_randao
|
||||
}
|
||||
|
||||
fn withdrawals(&self) -> &Vec<Withdrawal> {
|
||||
&self.withdrawals
|
||||
}
|
||||
}
|
||||
|
||||
/// Optimism Payload Builder Attributes
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OptimismPayloadBuilderAttributes {
|
||||
/// Inner ethereum payload builder attributes
|
||||
pub payload_attributes: EthPayloadBuilderAttributes,
|
||||
/// NoTxPool option for the generated payload
|
||||
pub no_tx_pool: bool,
|
||||
/// Transactions for the generated payload
|
||||
pub transactions: Vec<TransactionSigned>,
|
||||
/// The gas limit for the generated payload
|
||||
pub gas_limit: Option<u64>,
|
||||
}
|
||||
|
||||
impl PayloadBuilderAttributes for OptimismPayloadBuilderAttributes {
|
||||
type RpcPayloadAttributes = OptimismPayloadAttributes;
|
||||
type Error = DecodeError;
|
||||
|
||||
/// Creates a new payload builder for the given parent block and the attributes.
|
||||
///
|
||||
/// Derives the unique [PayloadId] for the given parent and attributes
|
||||
fn try_new(parent: B256, attributes: OptimismPayloadAttributes) -> Result<Self, DecodeError> {
|
||||
let (id, transactions) = {
|
||||
let transactions: Vec<_> = attributes
|
||||
.transactions
|
||||
.as_deref()
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.map(|tx| TransactionSigned::decode_enveloped(&mut tx.as_ref()))
|
||||
.collect::<Result<_, _>>()?;
|
||||
(payload_id_optimism(&parent, &attributes, &transactions), transactions)
|
||||
};
|
||||
|
||||
let withdraw = attributes.payload_attributes.withdrawals.map(
|
||||
|withdrawals: Vec<reth_rpc_types::engine::payload::Withdrawal>| {
|
||||
withdrawals
|
||||
.into_iter()
|
||||
.map(convert_standalone_withdraw_to_withdrawal) // Removed the parentheses here
|
||||
.collect::<Vec<_>>()
|
||||
},
|
||||
);
|
||||
|
||||
let payload_attributes = EthPayloadBuilderAttributes {
|
||||
id,
|
||||
parent,
|
||||
timestamp: attributes.payload_attributes.timestamp,
|
||||
suggested_fee_recipient: attributes.payload_attributes.suggested_fee_recipient,
|
||||
prev_randao: attributes.payload_attributes.prev_randao,
|
||||
withdrawals: withdraw.unwrap_or_default(),
|
||||
parent_beacon_block_root: attributes.payload_attributes.parent_beacon_block_root,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
payload_attributes,
|
||||
no_tx_pool: attributes.no_tx_pool.unwrap_or_default(),
|
||||
transactions,
|
||||
gas_limit: attributes.gas_limit,
|
||||
})
|
||||
}
|
||||
|
||||
fn parent(&self) -> B256 {
|
||||
self.payload_attributes.parent
|
||||
}
|
||||
|
||||
fn payload_id(&self) -> PayloadId {
|
||||
self.payload_attributes.id
|
||||
}
|
||||
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.payload_attributes.timestamp
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
self.payload_attributes.parent_beacon_block_root
|
||||
}
|
||||
|
||||
fn suggested_fee_recipient(&self) -> Address {
|
||||
self.payload_attributes.suggested_fee_recipient
|
||||
}
|
||||
|
||||
fn prev_randao(&self) -> B256 {
|
||||
self.payload_attributes.prev_randao
|
||||
}
|
||||
|
||||
fn withdrawals(&self) -> &Vec<Withdrawal> {
|
||||
&self.payload_attributes.withdrawals
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the payload id for the configured payload from the [OptimismPayloadAttributes].
|
||||
///
|
||||
/// Returns an 8-byte identifier by hashing the payload components with sha256 hash.
|
||||
pub(crate) fn payload_id(
|
||||
pub(crate) fn payload_id_optimism(
|
||||
parent: &B256,
|
||||
attributes: &PayloadAttributes,
|
||||
#[cfg(feature = "optimism")] txs: &[TransactionSigned],
|
||||
attributes: &OptimismPayloadAttributes,
|
||||
txs: &[TransactionSigned],
|
||||
) -> PayloadId {
|
||||
use sha2::Digest;
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(parent.as_slice());
|
||||
hasher.update(&attributes.payload_attributes.timestamp.to_be_bytes()[..]);
|
||||
hasher.update(attributes.payload_attributes.prev_randao.as_slice());
|
||||
hasher.update(attributes.payload_attributes.suggested_fee_recipient.as_slice());
|
||||
if let Some(withdrawals) = &attributes.payload_attributes.withdrawals {
|
||||
let mut buf = Vec::new();
|
||||
withdrawals.encode(&mut buf);
|
||||
hasher.update(buf);
|
||||
}
|
||||
|
||||
if let Some(parent_beacon_block) = attributes.payload_attributes.parent_beacon_block_root {
|
||||
hasher.update(parent_beacon_block);
|
||||
}
|
||||
|
||||
let no_tx_pool = attributes.no_tx_pool.unwrap_or_default();
|
||||
if no_tx_pool || !txs.is_empty() {
|
||||
hasher.update([no_tx_pool as u8]);
|
||||
hasher.update(txs.len().to_be_bytes());
|
||||
txs.iter().for_each(|tx| hasher.update(tx.hash()));
|
||||
}
|
||||
|
||||
if let Some(gas_limit) = attributes.gas_limit {
|
||||
hasher.update(gas_limit.to_be_bytes());
|
||||
}
|
||||
|
||||
let out = hasher.finalize();
|
||||
PayloadId::new(out.as_slice()[..8].try_into().expect("sufficient length"))
|
||||
}
|
||||
|
||||
/// Generates the payload id for the configured payload from the [PayloadAttributes].
|
||||
///
|
||||
/// Returns an 8-byte identifier by hashing the payload components with sha256 hash.
|
||||
pub(crate) fn payload_id(parent: &B256, attributes: &PayloadAttributes) -> PayloadId {
|
||||
use sha2::Digest;
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(parent.as_slice());
|
||||
@ -294,20 +423,6 @@ pub(crate) fn payload_id(
|
||||
hasher.update(parent_beacon_block);
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
let no_tx_pool = attributes.optimism_payload_attributes.no_tx_pool.unwrap_or_default();
|
||||
if no_tx_pool || !txs.is_empty() {
|
||||
hasher.update([no_tx_pool as u8]);
|
||||
hasher.update(txs.len().to_be_bytes());
|
||||
txs.iter().for_each(|tx| hasher.update(tx.hash()));
|
||||
}
|
||||
|
||||
if let Some(gas_limit) = attributes.optimism_payload_attributes.gas_limit {
|
||||
hasher.update(gas_limit.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
let out = hasher.finalize();
|
||||
PayloadId::new(out.as_slice()[..8].try_into().expect("sufficient length"))
|
||||
}
|
||||
|
||||
@ -5,9 +5,10 @@
|
||||
|
||||
use crate::{
|
||||
error::PayloadBuilderError, metrics::PayloadBuilderServiceMetrics, traits::PayloadJobGenerator,
|
||||
BuiltPayload, KeepPayloadJobAlive, PayloadBuilderAttributes, PayloadJob,
|
||||
BuiltPayload, KeepPayloadJobAlive, PayloadJob,
|
||||
};
|
||||
use futures_util::{future::FutureExt, Stream, StreamExt};
|
||||
use reth_node_api::{EngineTypes, PayloadBuilderAttributes};
|
||||
use reth_provider::CanonStateNotification;
|
||||
use reth_rpc_types::engine::PayloadId;
|
||||
use std::{
|
||||
@ -23,13 +24,16 @@ use tracing::{debug, info, trace, warn};
|
||||
|
||||
/// A communication channel to the [PayloadBuilderService] that can retrieve payloads.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PayloadStore {
|
||||
inner: PayloadBuilderHandle,
|
||||
pub struct PayloadStore<Engine: EngineTypes> {
|
||||
inner: PayloadBuilderHandle<Engine>,
|
||||
}
|
||||
|
||||
// === impl PayloadStore ===
|
||||
|
||||
impl PayloadStore {
|
||||
impl<Engine> PayloadStore<Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
/// Resolves the payload job and returns the best payload that has been built so far.
|
||||
///
|
||||
/// Note: depending on the installed [PayloadJobGenerator], this may or may not terminate the
|
||||
@ -57,13 +61,16 @@ impl PayloadStore {
|
||||
pub async fn payload_attributes(
|
||||
&self,
|
||||
id: PayloadId,
|
||||
) -> Option<Result<PayloadBuilderAttributes, PayloadBuilderError>> {
|
||||
) -> Option<Result<Engine::PayloadBuilderAttributes, PayloadBuilderError>> {
|
||||
self.inner.payload_attributes(id).await
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PayloadBuilderHandle> for PayloadStore {
|
||||
fn from(inner: PayloadBuilderHandle) -> Self {
|
||||
impl<Engine> From<PayloadBuilderHandle<Engine>> for PayloadStore<Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
fn from(inner: PayloadBuilderHandle<Engine>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
@ -72,18 +79,24 @@ impl From<PayloadBuilderHandle> for PayloadStore {
|
||||
///
|
||||
/// This is the API used to create new payloads and to get the current state of existing ones.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PayloadBuilderHandle {
|
||||
pub struct PayloadBuilderHandle<Engine: EngineTypes> {
|
||||
/// Sender half of the message channel to the [PayloadBuilderService].
|
||||
to_service: mpsc::UnboundedSender<PayloadServiceCommand>,
|
||||
to_service: mpsc::UnboundedSender<PayloadServiceCommand<Engine::PayloadBuilderAttributes>>,
|
||||
}
|
||||
|
||||
// === impl PayloadBuilderHandle ===
|
||||
|
||||
impl PayloadBuilderHandle {
|
||||
impl<Engine> PayloadBuilderHandle<Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
{
|
||||
/// Creates a new payload builder handle for the given channel.
|
||||
///
|
||||
/// Note: this is only used internally by the [PayloadBuilderService] to manage the payload
|
||||
/// building flow See [PayloadBuilderService::poll] for implementation details.
|
||||
pub fn new(to_service: mpsc::UnboundedSender<PayloadServiceCommand>) -> Self {
|
||||
pub fn new(
|
||||
to_service: mpsc::UnboundedSender<PayloadServiceCommand<Engine::PayloadBuilderAttributes>>,
|
||||
) -> Self {
|
||||
Self { to_service }
|
||||
}
|
||||
|
||||
@ -91,7 +104,7 @@ impl PayloadBuilderHandle {
|
||||
///
|
||||
/// Note: depending on the installed [PayloadJobGenerator], this may or may not terminate the
|
||||
/// job, See [PayloadJob::resolve].
|
||||
pub async fn resolve(
|
||||
async fn resolve(
|
||||
&self,
|
||||
id: PayloadId,
|
||||
) -> Option<Result<Arc<BuiltPayload>, PayloadBuilderError>> {
|
||||
@ -104,7 +117,7 @@ impl PayloadBuilderHandle {
|
||||
}
|
||||
|
||||
/// Returns the best payload for the given identifier.
|
||||
pub async fn best_payload(
|
||||
async fn best_payload(
|
||||
&self,
|
||||
id: PayloadId,
|
||||
) -> Option<Result<Arc<BuiltPayload>, PayloadBuilderError>> {
|
||||
@ -116,10 +129,10 @@ impl PayloadBuilderHandle {
|
||||
/// Returns the payload attributes associated with the given identifier.
|
||||
///
|
||||
/// Note: this returns the attributes of the payload and does not resolve the job.
|
||||
pub async fn payload_attributes(
|
||||
async fn payload_attributes(
|
||||
&self,
|
||||
id: PayloadId,
|
||||
) -> Option<Result<PayloadBuilderAttributes, PayloadBuilderError>> {
|
||||
) -> Option<Result<Engine::PayloadBuilderAttributes, PayloadBuilderError>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.to_service.send(PayloadServiceCommand::PayloadAttributes(id, tx)).ok()?;
|
||||
rx.await.ok()?
|
||||
@ -131,7 +144,7 @@ impl PayloadBuilderHandle {
|
||||
/// returns the receiver instead
|
||||
pub fn send_new_payload(
|
||||
&self,
|
||||
attr: PayloadBuilderAttributes,
|
||||
attr: Engine::PayloadBuilderAttributes,
|
||||
) -> oneshot::Receiver<Result<PayloadId, PayloadBuilderError>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.to_service.send(PayloadServiceCommand::BuildNewPayload(attr, tx));
|
||||
@ -145,7 +158,7 @@ impl PayloadBuilderHandle {
|
||||
/// Note: if there's already payload in progress with same identifier, it will be returned.
|
||||
pub async fn new_payload(
|
||||
&self,
|
||||
attr: PayloadBuilderAttributes,
|
||||
attr: Engine::PayloadBuilderAttributes,
|
||||
) -> Result<PayloadId, PayloadBuilderError> {
|
||||
self.send_new_payload(attr).await?
|
||||
}
|
||||
@ -161,18 +174,20 @@ impl PayloadBuilderHandle {
|
||||
/// does know nothing about how to build them, it just drives their jobs to completion.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct PayloadBuilderService<Gen, St>
|
||||
pub struct PayloadBuilderService<Gen, St, Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
Gen: PayloadJobGenerator,
|
||||
Gen::Job: PayloadJob<PayloadAttributes = Engine::PayloadBuilderAttributes>,
|
||||
{
|
||||
/// The type that knows how to create new payloads.
|
||||
generator: Gen,
|
||||
/// All active payload jobs.
|
||||
payload_jobs: Vec<(Gen::Job, PayloadId)>,
|
||||
/// Copy of the sender half, so new [`PayloadBuilderHandle`] can be created on demand.
|
||||
service_tx: mpsc::UnboundedSender<PayloadServiceCommand>,
|
||||
service_tx: mpsc::UnboundedSender<PayloadServiceCommand<Engine::PayloadBuilderAttributes>>,
|
||||
/// Receiver half of the command channel.
|
||||
command_rx: UnboundedReceiverStream<PayloadServiceCommand>,
|
||||
command_rx: UnboundedReceiverStream<PayloadServiceCommand<Engine::PayloadBuilderAttributes>>,
|
||||
/// Metrics for the payload builder service
|
||||
metrics: PayloadBuilderServiceMetrics,
|
||||
/// Chain events notification stream
|
||||
@ -181,16 +196,18 @@ where
|
||||
|
||||
// === impl PayloadBuilderService ===
|
||||
|
||||
impl<Gen, St> PayloadBuilderService<Gen, St>
|
||||
impl<Gen, St, Engine> PayloadBuilderService<Gen, St, Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
Gen: PayloadJobGenerator,
|
||||
Gen::Job: PayloadJob<PayloadAttributes = Engine::PayloadBuilderAttributes>,
|
||||
{
|
||||
/// Creates a new payload builder service and returns the [PayloadBuilderHandle] to interact
|
||||
/// with it.
|
||||
///
|
||||
/// This also takes a stream of chain events that will be forwarded to the generator to apply
|
||||
/// additional logic when new state is committed. See also [PayloadJobGenerator::on_new_state].
|
||||
pub fn new(generator: Gen, chain_events: St) -> (Self, PayloadBuilderHandle) {
|
||||
pub fn new(generator: Gen, chain_events: St) -> (Self, PayloadBuilderHandle<Engine>) {
|
||||
let (service_tx, command_rx) = mpsc::unbounded_channel();
|
||||
let service = Self {
|
||||
generator,
|
||||
@ -206,7 +223,7 @@ where
|
||||
}
|
||||
|
||||
/// Returns a handle to the service.
|
||||
pub fn handle(&self) -> PayloadBuilderHandle {
|
||||
pub fn handle(&self) -> PayloadBuilderHandle<Engine> {
|
||||
PayloadBuilderHandle::new(self.service_tx.clone())
|
||||
}
|
||||
|
||||
@ -232,24 +249,6 @@ where
|
||||
res
|
||||
}
|
||||
|
||||
/// Returns the payload attributes for the given payload.
|
||||
fn payload_attributes(
|
||||
&self,
|
||||
id: PayloadId,
|
||||
) -> Option<Result<PayloadBuilderAttributes, PayloadBuilderError>> {
|
||||
let attributes = self
|
||||
.payload_jobs
|
||||
.iter()
|
||||
.find(|(_, job_id)| *job_id == id)
|
||||
.map(|(j, _)| j.payload_attributes());
|
||||
|
||||
if attributes.is_none() {
|
||||
trace!(%id, "no matching payload job found to get attributes for");
|
||||
}
|
||||
|
||||
attributes
|
||||
}
|
||||
|
||||
/// Returns the best payload for the given identifier that has been built so far and terminates
|
||||
/// the job if requested.
|
||||
fn resolve(&mut self, id: PayloadId) -> Option<PayloadFuture> {
|
||||
@ -279,11 +278,38 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Gen, St> Future for PayloadBuilderService<Gen, St>
|
||||
impl<Gen, St, Engine> PayloadBuilderService<Gen, St, Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
Gen: PayloadJobGenerator,
|
||||
Gen::Job: PayloadJob<PayloadAttributes = Engine::PayloadBuilderAttributes>,
|
||||
{
|
||||
/// Returns the payload attributes for the given payload.
|
||||
fn payload_attributes(
|
||||
&self,
|
||||
id: PayloadId,
|
||||
) -> Option<Result<<Gen::Job as PayloadJob>::PayloadAttributes, PayloadBuilderError>> {
|
||||
let attributes = self
|
||||
.payload_jobs
|
||||
.iter()
|
||||
.find(|(_, job_id)| *job_id == id)
|
||||
.map(|(j, _)| j.payload_attributes());
|
||||
|
||||
if attributes.is_none() {
|
||||
trace!(%id, "no matching payload job found to get attributes for");
|
||||
}
|
||||
|
||||
attributes
|
||||
}
|
||||
}
|
||||
|
||||
impl<Gen, St, Engine> Future for PayloadBuilderService<Gen, St, Engine>
|
||||
where
|
||||
Engine: EngineTypes,
|
||||
Gen: PayloadJobGenerator + Unpin + 'static,
|
||||
<Gen as PayloadJobGenerator>::Job: Unpin + 'static,
|
||||
St: Stream<Item = CanonStateNotification> + Send + Unpin + 'static,
|
||||
Gen::Job: PayloadJob<PayloadAttributes = Engine::PayloadBuilderAttributes>,
|
||||
{
|
||||
type Output = ();
|
||||
|
||||
@ -330,10 +356,10 @@ where
|
||||
let mut res = Ok(id);
|
||||
|
||||
if this.contains_payload(id) {
|
||||
debug!(%id, parent = %attr.parent, "Payload job already in progress, ignoring.");
|
||||
debug!(%id, parent = %attr.parent(), "Payload job already in progress, ignoring.");
|
||||
} else {
|
||||
// no job for this payload yet, create one
|
||||
let parent = attr.parent;
|
||||
let parent = attr.parent();
|
||||
match this.generator.new_payload_job(attr) {
|
||||
Ok(job) => {
|
||||
info!(%id, %parent, "New payload job created");
|
||||
@ -371,28 +397,26 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make generic over built payload type
|
||||
type PayloadFuture =
|
||||
Pin<Box<dyn Future<Output = Result<Arc<BuiltPayload>, PayloadBuilderError>> + Send + Sync>>;
|
||||
|
||||
/// Message type for the [PayloadBuilderService].
|
||||
pub enum PayloadServiceCommand {
|
||||
pub enum PayloadServiceCommand<T> {
|
||||
/// Start building a new payload.
|
||||
BuildNewPayload(
|
||||
PayloadBuilderAttributes,
|
||||
oneshot::Sender<Result<PayloadId, PayloadBuilderError>>,
|
||||
),
|
||||
BuildNewPayload(T, oneshot::Sender<Result<PayloadId, PayloadBuilderError>>),
|
||||
/// Get the best payload so far
|
||||
BestPayload(PayloadId, oneshot::Sender<Option<Result<Arc<BuiltPayload>, PayloadBuilderError>>>),
|
||||
/// Get the payload attributes for the given payload
|
||||
PayloadAttributes(
|
||||
PayloadId,
|
||||
oneshot::Sender<Option<Result<PayloadBuilderAttributes, PayloadBuilderError>>>,
|
||||
),
|
||||
PayloadAttributes(PayloadId, oneshot::Sender<Option<Result<T, PayloadBuilderError>>>),
|
||||
/// Resolve the payload and return the payload
|
||||
Resolve(PayloadId, oneshot::Sender<Option<PayloadFuture>>),
|
||||
}
|
||||
|
||||
impl fmt::Debug for PayloadServiceCommand {
|
||||
impl<T> fmt::Debug for PayloadServiceCommand<T>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
PayloadServiceCommand::BuildNewPayload(f0, f1) => {
|
||||
|
||||
@ -2,9 +2,10 @@
|
||||
|
||||
use crate::{
|
||||
error::PayloadBuilderError, traits::KeepPayloadJobAlive, BuiltPayload,
|
||||
PayloadBuilderAttributes, PayloadBuilderHandle, PayloadBuilderService, PayloadJob,
|
||||
EthPayloadBuilderAttributes, PayloadBuilderHandle, PayloadBuilderService, PayloadJob,
|
||||
PayloadJobGenerator,
|
||||
};
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_primitives::{Block, U256};
|
||||
use reth_provider::CanonStateNotification;
|
||||
use std::{
|
||||
@ -15,18 +16,25 @@ use std::{
|
||||
};
|
||||
|
||||
/// Creates a new [PayloadBuilderService] for testing purposes.
|
||||
pub fn test_payload_service() -> (
|
||||
pub fn test_payload_service<Engine>() -> (
|
||||
PayloadBuilderService<
|
||||
TestPayloadJobGenerator,
|
||||
futures_util::stream::Empty<CanonStateNotification>,
|
||||
Engine,
|
||||
>,
|
||||
PayloadBuilderHandle,
|
||||
) {
|
||||
PayloadBuilderHandle<Engine>,
|
||||
)
|
||||
where
|
||||
Engine: EngineTypes<PayloadBuilderAttributes = EthPayloadBuilderAttributes>,
|
||||
{
|
||||
PayloadBuilderService::new(Default::default(), futures_util::stream::empty())
|
||||
}
|
||||
|
||||
/// Creates a new [PayloadBuilderService] for testing purposes and spawns it in the background.
|
||||
pub fn spawn_test_payload_service() -> PayloadBuilderHandle {
|
||||
pub fn spawn_test_payload_service<Engine>() -> PayloadBuilderHandle<Engine>
|
||||
where
|
||||
Engine: EngineTypes<PayloadBuilderAttributes = EthPayloadBuilderAttributes> + 'static,
|
||||
{
|
||||
let (service, handle) = test_payload_service();
|
||||
tokio::spawn(service);
|
||||
handle
|
||||
@ -42,7 +50,7 @@ impl PayloadJobGenerator for TestPayloadJobGenerator {
|
||||
|
||||
fn new_payload_job(
|
||||
&self,
|
||||
attr: PayloadBuilderAttributes,
|
||||
attr: EthPayloadBuilderAttributes,
|
||||
) -> Result<Self::Job, PayloadBuilderError> {
|
||||
Ok(TestPayloadJob { attr })
|
||||
}
|
||||
@ -51,7 +59,7 @@ impl PayloadJobGenerator for TestPayloadJobGenerator {
|
||||
/// A [PayloadJobGenerator] for testing purposes
|
||||
#[derive(Debug)]
|
||||
pub struct TestPayloadJob {
|
||||
attr: PayloadBuilderAttributes,
|
||||
attr: EthPayloadBuilderAttributes,
|
||||
}
|
||||
|
||||
impl Future for TestPayloadJob {
|
||||
@ -63,6 +71,7 @@ impl Future for TestPayloadJob {
|
||||
}
|
||||
|
||||
impl PayloadJob for TestPayloadJob {
|
||||
type PayloadAttributes = EthPayloadBuilderAttributes;
|
||||
type ResolvePayloadFuture =
|
||||
futures_util::future::Ready<Result<Arc<BuiltPayload>, PayloadBuilderError>>;
|
||||
|
||||
@ -74,7 +83,7 @@ impl PayloadJob for TestPayloadJob {
|
||||
)))
|
||||
}
|
||||
|
||||
fn payload_attributes(&self) -> Result<PayloadBuilderAttributes, PayloadBuilderError> {
|
||||
fn payload_attributes(&self) -> Result<EthPayloadBuilderAttributes, PayloadBuilderError> {
|
||||
Ok(self.attr.clone())
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
//! Trait abstractions used by the payload crate.
|
||||
|
||||
use crate::{error::PayloadBuilderError, BuiltPayload};
|
||||
use reth_node_api::PayloadBuilderAttributes;
|
||||
use reth_provider::CanonStateNotification;
|
||||
|
||||
use crate::{error::PayloadBuilderError, BuiltPayload, PayloadBuilderAttributes};
|
||||
use std::{future::Future, sync::Arc};
|
||||
|
||||
/// A type that can build a payload.
|
||||
@ -17,6 +17,8 @@ use std::{future::Future, sync::Arc};
|
||||
///
|
||||
/// Note: A `PayloadJob` need to be cancel safe because it might be dropped after the CL has requested the payload via `engine_getPayloadV1` (see also [engine API docs](https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_getpayloadv1))
|
||||
pub trait PayloadJob: Future<Output = Result<(), PayloadBuilderError>> + Send + Sync {
|
||||
/// Represents the payload attributes type that is used to spawn this payload job.
|
||||
type PayloadAttributes: PayloadBuilderAttributes + std::fmt::Debug;
|
||||
/// Represents the future that resolves the block that's returned to the CL.
|
||||
type ResolvePayloadFuture: Future<Output = Result<Arc<BuiltPayload>, PayloadBuilderError>>
|
||||
+ Send
|
||||
@ -29,7 +31,7 @@ pub trait PayloadJob: Future<Output = Result<(), PayloadBuilderError>> + Send +
|
||||
fn best_payload(&self) -> Result<Arc<BuiltPayload>, PayloadBuilderError>;
|
||||
|
||||
/// Returns the payload attributes for the payload being built.
|
||||
fn payload_attributes(&self) -> Result<PayloadBuilderAttributes, PayloadBuilderError>;
|
||||
fn payload_attributes(&self) -> Result<Self::PayloadAttributes, PayloadBuilderError>;
|
||||
|
||||
/// Called when the payload is requested by the CL.
|
||||
///
|
||||
@ -80,7 +82,7 @@ pub trait PayloadJobGenerator: Send + Sync {
|
||||
/// returned directly.
|
||||
fn new_payload_job(
|
||||
&self,
|
||||
attr: PayloadBuilderAttributes,
|
||||
attr: <Self::Job as PayloadJob>::PayloadAttributes,
|
||||
) -> Result<Self::Job, PayloadBuilderError>;
|
||||
|
||||
/// Handles new chain state events
|
||||
|
||||
@ -16,7 +16,9 @@ mod builder {
|
||||
commit_withdrawals, is_better_payload, pre_block_beacon_root_contract_call, BuildArguments,
|
||||
BuildOutcome, PayloadBuilder, PayloadConfig, WithdrawalsOutcome,
|
||||
};
|
||||
use reth_payload_builder::{error::PayloadBuilderError, BuiltPayload};
|
||||
use reth_payload_builder::{
|
||||
error::PayloadBuilderError, BuiltPayload, EthPayloadBuilderAttributes,
|
||||
};
|
||||
use reth_primitives::{
|
||||
constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE},
|
||||
eip4844::calculate_excess_blob_gas,
|
||||
@ -45,9 +47,11 @@ mod builder {
|
||||
Client: StateProviderFactory,
|
||||
Pool: TransactionPool,
|
||||
{
|
||||
type Attributes = EthPayloadBuilderAttributes;
|
||||
|
||||
fn try_build(
|
||||
&self,
|
||||
args: BuildArguments<Pool, Client>,
|
||||
args: BuildArguments<Pool, Client, EthPayloadBuilderAttributes>,
|
||||
) -> Result<BuildOutcome, PayloadBuilderError> {
|
||||
default_ethereum_payload_builder(args)
|
||||
}
|
||||
@ -60,7 +64,7 @@ mod builder {
|
||||
/// a result indicating success with the payload or an error in case of failure.
|
||||
#[inline]
|
||||
pub fn default_ethereum_payload_builder<Pool, Client>(
|
||||
args: BuildArguments<Pool, Client>,
|
||||
args: BuildArguments<Pool, Client, EthPayloadBuilderAttributes>,
|
||||
) -> Result<BuildOutcome, PayloadBuilderError>
|
||||
where
|
||||
Client: StateProviderFactory,
|
||||
|
||||
@ -19,6 +19,7 @@ reth-transaction-pool.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-basic-payload-builder.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
# ethereum
|
||||
revm.workspace = true
|
||||
|
||||
@ -16,7 +16,10 @@ pub mod error;
|
||||
mod builder {
|
||||
use crate::error::OptimismPayloadBuilderError;
|
||||
use reth_basic_payload_builder::*;
|
||||
use reth_payload_builder::{error::PayloadBuilderError, BuiltPayload};
|
||||
use reth_node_api::PayloadBuilderAttributes;
|
||||
use reth_payload_builder::{
|
||||
error::PayloadBuilderError, BuiltPayload, OptimismPayloadBuilderAttributes,
|
||||
};
|
||||
use reth_primitives::{
|
||||
constants::BEACON_NONCE,
|
||||
proofs,
|
||||
@ -68,22 +71,24 @@ mod builder {
|
||||
Client: StateProviderFactory,
|
||||
Pool: TransactionPool,
|
||||
{
|
||||
type Attributes = OptimismPayloadBuilderAttributes;
|
||||
|
||||
fn try_build(
|
||||
&self,
|
||||
args: BuildArguments<Pool, Client>,
|
||||
args: BuildArguments<Pool, Client, OptimismPayloadBuilderAttributes>,
|
||||
) -> Result<BuildOutcome, PayloadBuilderError> {
|
||||
optimism_payload_builder(args, self.compute_pending_block)
|
||||
}
|
||||
|
||||
fn on_missing_payload(
|
||||
&self,
|
||||
args: BuildArguments<Pool, Client>,
|
||||
args: BuildArguments<Pool, Client, OptimismPayloadBuilderAttributes>,
|
||||
) -> Option<Arc<BuiltPayload>> {
|
||||
// In Optimism, the PayloadAttributes can specify a `no_tx_pool` option that implies we
|
||||
// should not pull transactions from the tx pool. In this case, we build the payload
|
||||
// upfront with the list of transactions sent in the attributes without caring about
|
||||
// the results of the polling job, if a best payload has not already been built.
|
||||
if args.config.attributes.optimism_payload_attributes.no_tx_pool {
|
||||
if args.config.attributes.no_tx_pool {
|
||||
if let Ok(BuildOutcome::Better { payload, .. }) = self.try_build(args) {
|
||||
trace!(target: "payload_builder", "[OPTIMISM] Forced best payload");
|
||||
let payload = Arc::new(payload);
|
||||
@ -105,7 +110,7 @@ mod builder {
|
||||
/// a result indicating success with the payload or an error in case of failure.
|
||||
#[inline]
|
||||
pub(crate) fn optimism_payload_builder<Pool, Client>(
|
||||
args: BuildArguments<Pool, Client>,
|
||||
args: BuildArguments<Pool, Client, OptimismPayloadBuilderAttributes>,
|
||||
_compute_pending_block: bool,
|
||||
) -> Result<BuildOutcome, PayloadBuilderError>
|
||||
where
|
||||
@ -130,10 +135,9 @@ mod builder {
|
||||
..
|
||||
} = config;
|
||||
|
||||
debug!(target: "payload_builder", id=%attributes.id, parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload");
|
||||
debug!(target: "payload_builder", id=%attributes.payload_id(), parent_hash = ?parent_block.hash, parent_number = parent_block.number, "building new payload");
|
||||
let mut cumulative_gas_used = 0;
|
||||
let block_gas_limit: u64 = attributes
|
||||
.optimism_payload_attributes
|
||||
.gas_limit
|
||||
.unwrap_or(initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX));
|
||||
let base_fee = initialized_block_env.basefee.to::<u64>();
|
||||
@ -146,7 +150,7 @@ mod builder {
|
||||
let block_number = initialized_block_env.number.to::<u64>();
|
||||
|
||||
let is_regolith =
|
||||
chain_spec.is_fork_active_at_timestamp(Hardfork::Regolith, attributes.timestamp);
|
||||
chain_spec.is_fork_active_at_timestamp(Hardfork::Regolith, attributes.timestamp());
|
||||
|
||||
// Ensure that the create2deployer is force-deployed at the canyon transition. Optimism
|
||||
// blocks will always have at least a single transaction in them (the L1 info transaction),
|
||||
@ -154,7 +158,7 @@ mod builder {
|
||||
// the above check for empty blocks will never be hit on OP chains.
|
||||
reth_revm::optimism::ensure_create2_deployer(
|
||||
chain_spec.clone(),
|
||||
attributes.timestamp,
|
||||
attributes.timestamp(),
|
||||
&mut db,
|
||||
)
|
||||
.map_err(|_| {
|
||||
@ -162,7 +166,7 @@ mod builder {
|
||||
})?;
|
||||
|
||||
let mut receipts = Vec::new();
|
||||
for sequencer_tx in attributes.optimism_payload_attributes.transactions {
|
||||
for sequencer_tx in &attributes.transactions {
|
||||
// Check if the job was cancelled, if so we can exit early.
|
||||
if cancel.is_cancelled() {
|
||||
return Ok(BuildOutcome::Cancelled)
|
||||
@ -238,7 +242,7 @@ mod builder {
|
||||
// receipt hashes should be computed when set. The state transition process
|
||||
// ensures this is only set for post-Canyon deposit transactions.
|
||||
deposit_receipt_version: chain_spec
|
||||
.is_fork_active_at_timestamp(Hardfork::Canyon, attributes.timestamp)
|
||||
.is_fork_active_at_timestamp(Hardfork::Canyon, attributes.timestamp())
|
||||
.then_some(1),
|
||||
}));
|
||||
|
||||
@ -246,7 +250,7 @@ mod builder {
|
||||
executed_txs.push(sequencer_tx.into_signed());
|
||||
}
|
||||
|
||||
if !attributes.optimism_payload_attributes.no_tx_pool {
|
||||
if !attributes.no_tx_pool {
|
||||
while let Some(pool_tx) = best_txs.next() {
|
||||
// ensure we still have capacity for this transaction
|
||||
if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit {
|
||||
@ -336,8 +340,12 @@ mod builder {
|
||||
return Ok(BuildOutcome::Aborted { fees: total_fees, cached_reads })
|
||||
}
|
||||
|
||||
let WithdrawalsOutcome { withdrawals_root, withdrawals } =
|
||||
commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, attributes.withdrawals)?;
|
||||
let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals(
|
||||
&mut db,
|
||||
&chain_spec,
|
||||
attributes.timestamp(),
|
||||
attributes.withdrawals().clone(),
|
||||
)?;
|
||||
|
||||
// merge all transitions into bundle state, this would apply the withdrawal balance changes
|
||||
// and 4788 contract call
|
||||
@ -349,7 +357,7 @@ mod builder {
|
||||
block_number,
|
||||
);
|
||||
let receipts_root = bundle
|
||||
.receipts_root_slow(block_number, chain_spec.as_ref(), attributes.timestamp)
|
||||
.receipts_root_slow(block_number, chain_spec.as_ref(), attributes.timestamp())
|
||||
.expect("Number is in range");
|
||||
let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range");
|
||||
|
||||
@ -373,8 +381,8 @@ mod builder {
|
||||
receipts_root,
|
||||
withdrawals_root,
|
||||
logs_bloom,
|
||||
timestamp: attributes.timestamp,
|
||||
mix_hash: attributes.prev_randao,
|
||||
timestamp: attributes.timestamp(),
|
||||
mix_hash: attributes.prev_randao(),
|
||||
nonce: BEACON_NONCE,
|
||||
base_fee_per_gas: Some(base_fee),
|
||||
number: parent_block.number + 1,
|
||||
@ -382,7 +390,7 @@ mod builder {
|
||||
difficulty: U256::ZERO,
|
||||
gas_used: cumulative_gas_used,
|
||||
extra_data,
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root,
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root(),
|
||||
blob_gas_used,
|
||||
excess_blob_gas,
|
||||
};
|
||||
@ -393,7 +401,7 @@ mod builder {
|
||||
let sealed_block = block.seal_slow();
|
||||
debug!(target: "payload_builder", ?sealed_block, "sealed built block");
|
||||
|
||||
let mut payload = BuiltPayload::new(attributes.id, sealed_block, total_fees);
|
||||
let mut payload = BuiltPayload::new(attributes.payload_id(), sealed_block, total_fees);
|
||||
|
||||
// extend the payload with the blob sidecars from the executed txs
|
||||
payload.extend_sidecars(blob_sidecars);
|
||||
|
||||
@ -15,6 +15,8 @@ workspace = true
|
||||
# reth
|
||||
reth-primitives.workspace = true
|
||||
reth-rpc-types.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
# misc
|
||||
jsonrpsee = { workspace = true, features = ["server", "macros"] }
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_primitives::{Address, BlockHash, BlockId, BlockNumberOrTag, Bytes, B256, U256, U64};
|
||||
use reth_rpc_types::{
|
||||
engine::{
|
||||
ExecutionPayloadBodiesV1, ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3,
|
||||
ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, ForkchoiceState,
|
||||
ForkchoiceUpdated, PayloadAttributes, PayloadId, PayloadStatus, TransitionConfiguration,
|
||||
ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration,
|
||||
},
|
||||
state::StateOverride,
|
||||
BlockOverrides, CallRequest, Filter, Log, RichBlock, SyncStatus,
|
||||
};
|
||||
|
||||
#[cfg_attr(not(feature = "client"), rpc(server, namespace = "engine"))]
|
||||
#[cfg_attr(feature = "client", rpc(server, client, namespace = "engine"))]
|
||||
pub trait EngineApi {
|
||||
#[cfg_attr(not(feature = "client"), rpc(server, namespace = "engine"), server_bounds(Engine::PayloadAttributes: jsonrpsee::core::DeserializeOwned))]
|
||||
#[cfg_attr(feature = "client", rpc(server, client, namespace = "engine", client_bounds(Engine::PayloadAttributes: jsonrpsee::core::Serialize + Clone), server_bounds(Engine::PayloadAttributes: jsonrpsee::core::DeserializeOwned)))]
|
||||
pub trait EngineApi<Engine: EngineTypes> {
|
||||
/// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_newpayloadv1>
|
||||
/// Caution: This should not accept the `withdrawals` field
|
||||
#[method(name = "newPayloadV1")]
|
||||
@ -40,13 +41,13 @@ pub trait EngineApi {
|
||||
async fn fork_choice_updated_v1(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
payload_attributes: Option<Engine::PayloadAttributes>,
|
||||
) -> RpcResult<ForkchoiceUpdated>;
|
||||
|
||||
/// Post Shanghai forkchoice update handler
|
||||
///
|
||||
/// This is the same as `forkchoiceUpdatedV1`, but expects an additional `withdrawals` field in
|
||||
/// the [PayloadAttributes], if payload attributes are provided.
|
||||
/// the `payloadAttributes`, if payload attributes are provided.
|
||||
///
|
||||
/// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#engine_forkchoiceupdatedv2>
|
||||
///
|
||||
@ -56,21 +57,21 @@ pub trait EngineApi {
|
||||
async fn fork_choice_updated_v2(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
payload_attributes: Option<Engine::PayloadAttributes>,
|
||||
) -> RpcResult<ForkchoiceUpdated>;
|
||||
|
||||
/// Post Cancun forkchoice update handler
|
||||
///
|
||||
/// This is the same as `forkchoiceUpdatedV2`, but expects an additional
|
||||
/// `parentBeaconBlockRoot` field in the the [PayloadAttributes], if payload attributes are
|
||||
/// provided.
|
||||
/// `parentBeaconBlockRoot` field in the the `payloadAttributes`, if payload attributes
|
||||
/// are provided.
|
||||
///
|
||||
/// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#engine_forkchoiceupdatedv3>
|
||||
#[method(name = "forkchoiceUpdatedV3")]
|
||||
async fn fork_choice_updated_v3(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
payload_attributes: Option<Engine::PayloadAttributes>,
|
||||
) -> RpcResult<ForkchoiceUpdated>;
|
||||
|
||||
/// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_getpayloadv1>
|
||||
@ -147,6 +148,13 @@ pub trait EngineApi {
|
||||
async fn exchange_capabilities(&self, capabilities: Vec<String>) -> RpcResult<Vec<String>>;
|
||||
}
|
||||
|
||||
// NOTE: We can't use associated types in the `EngineApi` trait because of jsonrpsee, so we use a
|
||||
// generic here. It would be nice if the rpc macro would understand which types need to have serde.
|
||||
// By default, if the trait has a generic, the rpc macro will add e.g. `Engine: DeserializeOwned` to
|
||||
// the trait bounds, which is not what we want, because `Types` is not used directly in any of the
|
||||
// trait methods. Instead, we have to add the bounds manually. This would be disastrous if we had
|
||||
// more than one associated type used in the trait methods.
|
||||
|
||||
/// A subset of the ETH rpc interface: <https://ethereum.github.io/execution-apis/api-documentation/>
|
||||
///
|
||||
/// Specifically for the engine auth server: <https://github.com/ethereum/execution-apis/blob/main/src/engine/common.md#underlying-protocol>
|
||||
|
||||
@ -25,6 +25,7 @@ reth-rpc-types.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-transaction-pool.workspace = true
|
||||
reth-rpc-types-compat.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
# rpc/net
|
||||
jsonrpsee = { workspace = true, features = ["server"] }
|
||||
@ -51,6 +52,7 @@ reth-network-api.workspace = true
|
||||
reth-interfaces = { workspace = true, features = ["test-utils"] }
|
||||
reth-beacon-consensus.workspace = true
|
||||
reth-payload-builder = { workspace = true, features = ["test-utils"] }
|
||||
reth-node-builder.workspace = true
|
||||
|
||||
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }
|
||||
serde_json.workspace = true
|
||||
|
||||
@ -11,6 +11,7 @@ use jsonrpsee::{
|
||||
server::{RpcModule, ServerHandle},
|
||||
};
|
||||
use reth_network_api::{NetworkInfo, Peers};
|
||||
use reth_node_api::EngineTypes;
|
||||
use reth_provider::{
|
||||
BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, HeaderProvider, ReceiptProviderIdExt,
|
||||
StateProviderFactory,
|
||||
@ -33,7 +34,7 @@ use std::{
|
||||
|
||||
/// Configure and launch a _standalone_ auth server with `engine` and a _new_ `eth` namespace.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn launch<Provider, Pool, Network, Tasks, EngineApi>(
|
||||
pub async fn launch<Provider, Pool, Network, Tasks, EngineApi, EngineT>(
|
||||
provider: Provider,
|
||||
pool: Pool,
|
||||
network: Network,
|
||||
@ -55,7 +56,8 @@ where
|
||||
Pool: TransactionPool + Clone + 'static,
|
||||
Network: NetworkInfo + Peers + Clone + 'static,
|
||||
Tasks: TaskSpawner + Clone + 'static,
|
||||
EngineApi: EngineApiServer,
|
||||
EngineT: EngineTypes,
|
||||
EngineApi: EngineApiServer<EngineT>,
|
||||
{
|
||||
// spawn a new cache task
|
||||
let eth_cache =
|
||||
@ -85,7 +87,7 @@ where
|
||||
}
|
||||
|
||||
/// Configure and launch a _standalone_ auth server with existing EthApi implementation.
|
||||
pub async fn launch_with_eth_api<Provider, Pool, Network, EngineApi>(
|
||||
pub async fn launch_with_eth_api<Provider, Pool, Network, EngineApi, EngineT>(
|
||||
eth_api: EthApi<Provider, Pool, Network>,
|
||||
eth_filter: EthFilter<Provider, Pool>,
|
||||
engine_api: EngineApi,
|
||||
@ -103,7 +105,8 @@ where
|
||||
+ 'static,
|
||||
Pool: TransactionPool + Clone + 'static,
|
||||
Network: NetworkInfo + Peers + Clone + 'static,
|
||||
EngineApi: EngineApiServer,
|
||||
EngineT: EngineTypes,
|
||||
EngineApi: EngineApiServer<EngineT>,
|
||||
{
|
||||
// Configure the module and start the server.
|
||||
let mut module = RpcModule::new(());
|
||||
@ -253,9 +256,10 @@ pub struct AuthRpcModule {
|
||||
|
||||
impl AuthRpcModule {
|
||||
/// Create a new `AuthRpcModule` with the given `engine_api`.
|
||||
pub fn new<EngineApi>(engine: EngineApi) -> Self
|
||||
pub fn new<EngineApi, EngineT>(engine: EngineApi) -> Self
|
||||
where
|
||||
EngineApi: EngineApiServer,
|
||||
EngineT: EngineTypes,
|
||||
EngineApi: EngineApiServer<EngineT>,
|
||||
{
|
||||
let mut module = RpcModule::new(());
|
||||
module.merge(engine.into_rpc()).expect("No conflicting methods");
|
||||
|
||||
@ -69,6 +69,7 @@
|
||||
//!
|
||||
//! ```
|
||||
//! use reth_network_api::{NetworkInfo, Peers};
|
||||
//! use reth_node_api::EngineTypes;
|
||||
//! use reth_provider::{
|
||||
//! AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider,
|
||||
//! ChangeSetReader, EvmEnvProvider, StateProviderFactory,
|
||||
@ -82,7 +83,7 @@
|
||||
//! use reth_tasks::TokioTaskExecutor;
|
||||
//! use reth_transaction_pool::TransactionPool;
|
||||
//! use tokio::try_join;
|
||||
//! pub async fn launch<Provider, Pool, Network, Events, EngineApi>(
|
||||
//! pub async fn launch<Provider, Pool, Network, Events, EngineApi, EngineT>(
|
||||
//! provider: Provider,
|
||||
//! pool: Pool,
|
||||
//! network: Network,
|
||||
@ -101,7 +102,8 @@
|
||||
//! Pool: TransactionPool + Clone + 'static,
|
||||
//! Network: NetworkInfo + Peers + Clone + 'static,
|
||||
//! Events: CanonStateSubscriptions + Clone + 'static,
|
||||
//! EngineApi: EngineApiServer,
|
||||
//! EngineApi: EngineApiServer<EngineT>,
|
||||
//! EngineT: EngineTypes,
|
||||
//! {
|
||||
//! // configure the rpc module per transport
|
||||
//! let transports = TransportRpcModuleConfig::default().with_http(vec![
|
||||
@ -148,6 +150,7 @@ use jsonrpsee::{
|
||||
server::{IdProvider, Server, ServerHandle},
|
||||
Methods, RpcModule,
|
||||
};
|
||||
use reth_node_api::EngineTypes;
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use strum::{AsRefStr, EnumIter, EnumVariantNames, IntoStaticStr, ParseError, VariantNames};
|
||||
use tower::layer::util::{Identity, Stack};
|
||||
@ -379,7 +382,7 @@ where
|
||||
///
|
||||
/// This behaves exactly as [RpcModuleBuilder::build] for the [TransportRpcModules], but also
|
||||
/// configures the auth (engine api) server, which exposes a subset of the `eth_` namespace.
|
||||
pub fn build_with_auth_server<EngineApi>(
|
||||
pub fn build_with_auth_server<EngineApi, EngineT: EngineTypes>(
|
||||
self,
|
||||
module_config: TransportRpcModuleConfig,
|
||||
engine: EngineApi,
|
||||
@ -389,7 +392,7 @@ where
|
||||
RethModuleRegistry<Provider, Pool, Network, Tasks, Events>,
|
||||
)
|
||||
where
|
||||
EngineApi: EngineApiServer,
|
||||
EngineApi: EngineApiServer<EngineT>,
|
||||
{
|
||||
let mut modules = TransportRpcModules::default();
|
||||
|
||||
@ -1020,9 +1023,10 @@ where
|
||||
/// * `api_` namespace
|
||||
///
|
||||
/// Note: This does _not_ register the `engine_` in this registry.
|
||||
pub fn create_auth_module<EngineApi>(&mut self, engine_api: EngineApi) -> AuthRpcModule
|
||||
pub fn create_auth_module<EngineApi, EngineT>(&mut self, engine_api: EngineApi) -> AuthRpcModule
|
||||
where
|
||||
EngineApi: EngineApiServer,
|
||||
EngineT: EngineTypes,
|
||||
EngineApi: EngineApiServer<EngineT>,
|
||||
{
|
||||
let eth_handlers = self.eth_handlers();
|
||||
let mut module = RpcModule::new(());
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::utils::launch_auth;
|
||||
use jsonrpsee::core::client::{ClientT, SubscriptionClientT};
|
||||
use reth_node_builder::EthEngineTypes;
|
||||
use reth_primitives::{Block, U64};
|
||||
use reth_rpc::JwtSecret;
|
||||
use reth_rpc_api::clients::EngineApiClient;
|
||||
@ -13,6 +14,7 @@ use reth_rpc_types_compat::engine::payload::{
|
||||
async fn test_basic_engine_calls<C>(client: &C)
|
||||
where
|
||||
C: ClientT + SubscriptionClientT + Sync,
|
||||
C: EngineApiClient<EthEngineTypes>,
|
||||
{
|
||||
let block = Block::default().seal_slow();
|
||||
EngineApiClient::new_payload_v1(client, try_block_to_payload_v1(block.clone())).await;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use reth_beacon_consensus::BeaconConsensusEngineHandle;
|
||||
use reth_network_api::noop::NoopNetwork;
|
||||
use reth_node_builder::EthEngineTypes;
|
||||
use reth_payload_builder::test_utils::spawn_test_payload_service;
|
||||
use reth_primitives::MAINNET;
|
||||
use reth_provider::test_utils::{NoopProvider, TestCanonStateSubscriptions};
|
||||
@ -24,7 +25,7 @@ pub fn test_address() -> SocketAddr {
|
||||
pub async fn launch_auth(secret: JwtSecret) -> AuthServerHandle {
|
||||
let config = AuthServerConfig::builder(secret).socket_addr(test_address()).build();
|
||||
let (tx, _rx) = unbounded_channel();
|
||||
let beacon_engine_handle = BeaconConsensusEngineHandle::new(tx);
|
||||
let beacon_engine_handle = BeaconConsensusEngineHandle::<EthEngineTypes>::new(tx);
|
||||
let engine_api = EngineApi::new(
|
||||
NoopProvider::default(),
|
||||
MAINNET.clone(),
|
||||
|
||||
@ -22,6 +22,8 @@ reth-beacon-consensus.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-rpc-types-compat.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
# async
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
|
||||
@ -39,6 +41,7 @@ serde.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
alloy-rlp.workspace = true
|
||||
reth-node-builder.workspace = true
|
||||
reth-interfaces = { workspace = true, features = ["test-utils"] }
|
||||
reth-provider = { workspace = true, features = ["test-utils"] }
|
||||
reth-payload-builder = { workspace = true, features = ["test-utils"] }
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
use crate::{
|
||||
metrics::EngineApiMetrics, payload::PayloadOrAttributes, EngineApiError,
|
||||
EngineApiMessageVersion, EngineApiResult,
|
||||
};
|
||||
use crate::{metrics::EngineApiMetrics, EngineApiError, EngineApiResult};
|
||||
use async_trait::async_trait;
|
||||
use jsonrpsee_core::RpcResult;
|
||||
use reth_beacon_consensus::BeaconConsensusEngineHandle;
|
||||
use reth_interfaces::consensus::ForkchoiceState;
|
||||
use reth_payload_builder::{PayloadBuilderAttributes, PayloadStore};
|
||||
use reth_node_api::{
|
||||
validate_payload_timestamp, validate_version_specific_fields, EngineApiMessageVersion,
|
||||
EngineTypes, PayloadAttributes, PayloadBuilderAttributes, PayloadOrAttributes,
|
||||
};
|
||||
use reth_payload_builder::PayloadStore;
|
||||
use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Hardfork, B256, U64};
|
||||
use reth_provider::{BlockReader, EvmEnvProvider, HeaderProvider, StateProviderFactory};
|
||||
use reth_rpc_api::EngineApiServer;
|
||||
use reth_rpc_types::engine::{
|
||||
CancunPayloadFields, ExecutionPayload, ExecutionPayloadBodiesV1, ExecutionPayloadEnvelopeV2,
|
||||
ExecutionPayloadEnvelopeV3, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3,
|
||||
ForkchoiceUpdated, PayloadAttributes, PayloadId, PayloadStatus, TransitionConfiguration,
|
||||
CAPABILITIES,
|
||||
ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration, CAPABILITIES,
|
||||
};
|
||||
use reth_rpc_types_compat::engine::payload::{
|
||||
convert_payload_input_v2_to_payload, convert_to_payload_body_v1,
|
||||
@ -32,35 +32,37 @@ const MAX_PAYLOAD_BODIES_LIMIT: u64 = 1024;
|
||||
|
||||
/// The Engine API implementation that grants the Consensus layer access to data and
|
||||
/// functions in the Execution layer that are crucial for the consensus process.
|
||||
pub struct EngineApi<Provider> {
|
||||
inner: Arc<EngineApiInner<Provider>>,
|
||||
pub struct EngineApi<Provider, EngineT: EngineTypes> {
|
||||
inner: Arc<EngineApiInner<Provider, EngineT>>,
|
||||
}
|
||||
|
||||
struct EngineApiInner<Provider> {
|
||||
struct EngineApiInner<Provider, EngineT: EngineTypes> {
|
||||
/// The provider to interact with the chain.
|
||||
provider: Provider,
|
||||
/// Consensus configuration
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
/// The channel to send messages to the beacon consensus engine.
|
||||
beacon_consensus: BeaconConsensusEngineHandle,
|
||||
beacon_consensus: BeaconConsensusEngineHandle<EngineT>,
|
||||
/// The type that can communicate with the payload service to retrieve payloads.
|
||||
payload_store: PayloadStore,
|
||||
payload_store: PayloadStore<EngineT>,
|
||||
/// For spawning and executing async tasks
|
||||
task_spawner: Box<dyn TaskSpawner>,
|
||||
/// The metrics for engine api calls
|
||||
metrics: EngineApiMetrics,
|
||||
}
|
||||
|
||||
impl<Provider> EngineApi<Provider>
|
||||
impl<Provider, EngineT> EngineApi<Provider, EngineT>
|
||||
where
|
||||
Provider: HeaderProvider + BlockReader + StateProviderFactory + EvmEnvProvider + 'static,
|
||||
EngineT: EngineTypes + 'static,
|
||||
EngineT::PayloadBuilderAttributes: Send,
|
||||
{
|
||||
/// Create new instance of [EngineApi].
|
||||
pub fn new(
|
||||
provider: Provider,
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
beacon_consensus: BeaconConsensusEngineHandle,
|
||||
payload_store: PayloadStore,
|
||||
beacon_consensus: BeaconConsensusEngineHandle<EngineT>,
|
||||
payload_store: PayloadStore<EngineT>,
|
||||
task_spawner: Box<dyn TaskSpawner>,
|
||||
) -> Self {
|
||||
let inner = Arc::new(EngineApiInner {
|
||||
@ -78,7 +80,7 @@ where
|
||||
async fn get_payload_attributes(
|
||||
&self,
|
||||
payload_id: PayloadId,
|
||||
) -> EngineApiResult<PayloadBuilderAttributes> {
|
||||
) -> EngineApiResult<EngineT::PayloadBuilderAttributes> {
|
||||
Ok(self
|
||||
.inner
|
||||
.payload_store
|
||||
@ -94,8 +96,15 @@ where
|
||||
payload: ExecutionPayloadV1,
|
||||
) -> EngineApiResult<PayloadStatus> {
|
||||
let payload = ExecutionPayload::from(payload);
|
||||
let payload_or_attrs = PayloadOrAttributes::from_execution_payload(&payload, None);
|
||||
self.validate_version_specific_fields(EngineApiMessageVersion::V1, &payload_or_attrs)?;
|
||||
let payload_or_attrs =
|
||||
PayloadOrAttributes::<'_, EngineT::PayloadAttributes>::from_execution_payload(
|
||||
&payload, None,
|
||||
);
|
||||
validate_version_specific_fields(
|
||||
&self.inner.chain_spec,
|
||||
EngineApiMessageVersion::V1,
|
||||
&payload_or_attrs,
|
||||
)?;
|
||||
Ok(self.inner.beacon_consensus.new_payload(payload, None).await?)
|
||||
}
|
||||
|
||||
@ -105,8 +114,15 @@ where
|
||||
payload: ExecutionPayloadInputV2,
|
||||
) -> EngineApiResult<PayloadStatus> {
|
||||
let payload = convert_payload_input_v2_to_payload(payload);
|
||||
let payload_or_attrs = PayloadOrAttributes::from_execution_payload(&payload, None);
|
||||
self.validate_version_specific_fields(EngineApiMessageVersion::V2, &payload_or_attrs)?;
|
||||
let payload_or_attrs =
|
||||
PayloadOrAttributes::<'_, EngineT::PayloadAttributes>::from_execution_payload(
|
||||
&payload, None,
|
||||
);
|
||||
validate_version_specific_fields(
|
||||
&self.inner.chain_spec,
|
||||
EngineApiMessageVersion::V2,
|
||||
&payload_or_attrs,
|
||||
)?;
|
||||
Ok(self.inner.beacon_consensus.new_payload(payload, None).await?)
|
||||
}
|
||||
|
||||
@ -119,8 +135,15 @@ where
|
||||
) -> EngineApiResult<PayloadStatus> {
|
||||
let payload = ExecutionPayload::from(payload);
|
||||
let payload_or_attrs =
|
||||
PayloadOrAttributes::from_execution_payload(&payload, Some(parent_beacon_block_root));
|
||||
self.validate_version_specific_fields(EngineApiMessageVersion::V3, &payload_or_attrs)?;
|
||||
PayloadOrAttributes::<'_, EngineT::PayloadAttributes>::from_execution_payload(
|
||||
&payload,
|
||||
Some(parent_beacon_block_root),
|
||||
);
|
||||
validate_version_specific_fields(
|
||||
&self.inner.chain_spec,
|
||||
EngineApiMessageVersion::V3,
|
||||
&payload_or_attrs,
|
||||
)?;
|
||||
|
||||
let cancun_fields = CancunPayloadFields { versioned_hashes, parent_beacon_block_root };
|
||||
|
||||
@ -136,7 +159,7 @@ where
|
||||
pub async fn fork_choice_updated_v1(
|
||||
&self,
|
||||
state: ForkchoiceState,
|
||||
payload_attrs: Option<PayloadAttributes>,
|
||||
payload_attrs: Option<EngineT::PayloadAttributes>,
|
||||
) -> EngineApiResult<ForkchoiceUpdated> {
|
||||
self.validate_and_execute_forkchoice(EngineApiMessageVersion::V1, state, payload_attrs)
|
||||
.await
|
||||
@ -149,7 +172,7 @@ where
|
||||
pub async fn fork_choice_updated_v2(
|
||||
&self,
|
||||
state: ForkchoiceState,
|
||||
payload_attrs: Option<PayloadAttributes>,
|
||||
payload_attrs: Option<EngineT::PayloadAttributes>,
|
||||
) -> EngineApiResult<ForkchoiceUpdated> {
|
||||
self.validate_and_execute_forkchoice(EngineApiMessageVersion::V2, state, payload_attrs)
|
||||
.await
|
||||
@ -162,7 +185,7 @@ where
|
||||
pub async fn fork_choice_updated_v3(
|
||||
&self,
|
||||
state: ForkchoiceState,
|
||||
payload_attrs: Option<PayloadAttributes>,
|
||||
payload_attrs: Option<EngineT::PayloadAttributes>,
|
||||
) -> EngineApiResult<ForkchoiceUpdated> {
|
||||
self.validate_and_execute_forkchoice(EngineApiMessageVersion::V3, state, payload_attrs)
|
||||
.await
|
||||
@ -205,7 +228,11 @@ where
|
||||
let attributes = self.get_payload_attributes(payload_id).await?;
|
||||
|
||||
// validate timestamp according to engine rules
|
||||
self.validate_payload_timestamp(EngineApiMessageVersion::V2, attributes.timestamp)?;
|
||||
validate_payload_timestamp(
|
||||
&self.inner.chain_spec,
|
||||
EngineApiMessageVersion::V2,
|
||||
attributes.timestamp(),
|
||||
)?;
|
||||
|
||||
// Now resolve the payload
|
||||
Ok(self
|
||||
@ -232,7 +259,11 @@ where
|
||||
let attributes = self.get_payload_attributes(payload_id).await?;
|
||||
|
||||
// validate timestamp according to engine rules
|
||||
self.validate_payload_timestamp(EngineApiMessageVersion::V3, attributes.timestamp)?;
|
||||
validate_payload_timestamp(
|
||||
&self.inner.chain_spec,
|
||||
EngineApiMessageVersion::V3,
|
||||
attributes.timestamp(),
|
||||
)?;
|
||||
|
||||
// Now resolve the payload
|
||||
Ok(self
|
||||
@ -385,159 +416,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the timestamp depending on the version called:
|
||||
///
|
||||
/// * If V2, this ensure that the payload timestamp is pre-Cancun.
|
||||
/// * If V3, this ensures that the payload timestamp is within the Cancun timestamp.
|
||||
///
|
||||
/// Otherwise, this will return [EngineApiError::UnsupportedFork].
|
||||
fn validate_payload_timestamp(
|
||||
&self,
|
||||
version: EngineApiMessageVersion,
|
||||
timestamp: u64,
|
||||
) -> EngineApiResult<()> {
|
||||
let is_cancun = self.inner.chain_spec.is_cancun_active_at_timestamp(timestamp);
|
||||
if version == EngineApiMessageVersion::V2 && is_cancun {
|
||||
// From the Engine API spec:
|
||||
//
|
||||
// ### Update the methods of previous forks
|
||||
//
|
||||
// This document defines how Cancun payload should be handled by the [`Shanghai
|
||||
// API`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md).
|
||||
//
|
||||
// For the following methods:
|
||||
//
|
||||
// - [`engine_forkchoiceUpdatedV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_forkchoiceupdatedv2)
|
||||
// - [`engine_newPayloadV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_newpayloadV2)
|
||||
// - [`engine_getPayloadV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_getpayloadv2)
|
||||
//
|
||||
// a validation **MUST** be added:
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `timestamp` of payload or payloadAttributes greater or equal to the Cancun
|
||||
// activation timestamp.
|
||||
return Err(EngineApiError::UnsupportedFork)
|
||||
}
|
||||
|
||||
if version == EngineApiMessageVersion::V3 && !is_cancun {
|
||||
// From the Engine API spec:
|
||||
// <https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/cancun.md#specification-2>
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `timestamp` of the built payload does not fall within the time frame of the Cancun
|
||||
// fork.
|
||||
return Err(EngineApiError::UnsupportedFork)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates the presence of the `withdrawals` field according to the payload timestamp.
|
||||
/// After Shanghai, withdrawals field must be [Some].
|
||||
/// Before Shanghai, withdrawals field must be [None];
|
||||
fn validate_withdrawals_presence(
|
||||
&self,
|
||||
version: EngineApiMessageVersion,
|
||||
timestamp: u64,
|
||||
has_withdrawals: bool,
|
||||
) -> EngineApiResult<()> {
|
||||
let is_shanghai =
|
||||
self.inner.chain_spec.fork(Hardfork::Shanghai).active_at_timestamp(timestamp);
|
||||
|
||||
match version {
|
||||
EngineApiMessageVersion::V1 => {
|
||||
if has_withdrawals {
|
||||
return Err(EngineApiError::WithdrawalsNotSupportedInV1)
|
||||
}
|
||||
if is_shanghai {
|
||||
return Err(EngineApiError::NoWithdrawalsPostShanghai)
|
||||
}
|
||||
}
|
||||
EngineApiMessageVersion::V2 | EngineApiMessageVersion::V3 => {
|
||||
if is_shanghai && !has_withdrawals {
|
||||
return Err(EngineApiError::NoWithdrawalsPostShanghai)
|
||||
}
|
||||
if !is_shanghai && has_withdrawals {
|
||||
return Err(EngineApiError::HasWithdrawalsPreShanghai)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate the presence of the `parentBeaconBlockRoot` field according to the payload
|
||||
/// timestamp.
|
||||
///
|
||||
/// After Cancun, `parentBeaconBlockRoot` field must be [Some].
|
||||
/// Before Cancun, `parentBeaconBlockRoot` field must be [None].
|
||||
///
|
||||
/// If the engine API message version is V1 or V2, and the payload attribute's timestamp is
|
||||
/// post-Cancun, then this will return [EngineApiError::UnsupportedFork].
|
||||
///
|
||||
/// If the payload attribute's timestamp is before the Cancun fork and the engine API message
|
||||
/// version is V3, then this will return [EngineApiError::UnsupportedFork].
|
||||
///
|
||||
/// If the engine API message version is V3, but the `parentBeaconBlockRoot` is [None], then
|
||||
/// this will return [EngineApiError::NoParentBeaconBlockRootPostCancun].
|
||||
///
|
||||
/// This implements the following Engine API spec rules:
|
||||
///
|
||||
/// 1. Client software **MUST** check that provided set of parameters and their fields strictly
|
||||
/// matches the expected one and return `-32602: Invalid params` error if this check fails.
|
||||
/// Any field having `null` value **MUST** be considered as not provided.
|
||||
///
|
||||
/// 2. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
/// `payloadAttributes` is set and the `payloadAttributes.timestamp` does not fall within the
|
||||
/// time frame of the Cancun fork.
|
||||
fn validate_parent_beacon_block_root_presence(
|
||||
&self,
|
||||
version: EngineApiMessageVersion,
|
||||
timestamp: u64,
|
||||
has_parent_beacon_block_root: bool,
|
||||
) -> EngineApiResult<()> {
|
||||
// 1. Client software **MUST** check that provided set of parameters and their fields
|
||||
// strictly matches the expected one and return `-32602: Invalid params` error if this
|
||||
// check fails. Any field having `null` value **MUST** be considered as not provided.
|
||||
match version {
|
||||
EngineApiMessageVersion::V1 | EngineApiMessageVersion::V2 => {
|
||||
if has_parent_beacon_block_root {
|
||||
return Err(EngineApiError::ParentBeaconBlockRootNotSupportedBeforeV3)
|
||||
}
|
||||
}
|
||||
EngineApiMessageVersion::V3 => {
|
||||
if !has_parent_beacon_block_root {
|
||||
return Err(EngineApiError::NoParentBeaconBlockRootPostCancun)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `payloadAttributes` is set and the `payloadAttributes.timestamp` does not fall within
|
||||
// the time frame of the Cancun fork.
|
||||
self.validate_payload_timestamp(version, timestamp)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates the presence or exclusion of fork-specific fields based on the payload attributes
|
||||
/// and the message version.
|
||||
fn validate_version_specific_fields(
|
||||
&self,
|
||||
version: EngineApiMessageVersion,
|
||||
payload_or_attrs: &PayloadOrAttributes<'_>,
|
||||
) -> EngineApiResult<()> {
|
||||
self.validate_withdrawals_presence(
|
||||
version,
|
||||
payload_or_attrs.timestamp(),
|
||||
payload_or_attrs.withdrawals().is_some(),
|
||||
)?;
|
||||
self.validate_parent_beacon_block_root_presence(
|
||||
version,
|
||||
payload_or_attrs.timestamp(),
|
||||
payload_or_attrs.parent_beacon_block_root().is_some(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Validates the `engine_forkchoiceUpdated` payload attributes and executes the forkchoice
|
||||
/// update.
|
||||
///
|
||||
@ -555,17 +433,11 @@ where
|
||||
&self,
|
||||
version: EngineApiMessageVersion,
|
||||
state: ForkchoiceState,
|
||||
payload_attrs: Option<PayloadAttributes>,
|
||||
payload_attrs: Option<EngineT::PayloadAttributes>,
|
||||
) -> EngineApiResult<ForkchoiceUpdated> {
|
||||
if let Some(ref attrs) = payload_attrs {
|
||||
let attr_validation_res = self.validate_version_specific_fields(version, &attrs.into());
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
if attrs.optimism_payload_attributes.gas_limit.is_none() &&
|
||||
self.inner.chain_spec.is_optimism()
|
||||
{
|
||||
return Err(EngineApiError::MissingGasLimitInPayloadAttributes)
|
||||
}
|
||||
let attr_validation_res =
|
||||
attrs.ensure_well_formed_attributes(&self.inner.chain_spec, version);
|
||||
|
||||
// From the engine API spec:
|
||||
//
|
||||
@ -587,7 +459,7 @@ where
|
||||
if fcu_res.is_invalid() {
|
||||
return Ok(fcu_res)
|
||||
}
|
||||
return Err(err)
|
||||
return Err(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,9 +468,12 @@ where
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<Provider> EngineApiServer for EngineApi<Provider>
|
||||
impl<Provider, EngineT> EngineApiServer<EngineT> for EngineApi<Provider, EngineT>
|
||||
where
|
||||
Provider: HeaderProvider + BlockReader + StateProviderFactory + EvmEnvProvider + 'static,
|
||||
EngineT: EngineTypes + 'static + Send,
|
||||
EngineT::PayloadAttributes: Send,
|
||||
EngineT::PayloadBuilderAttributes: Send,
|
||||
{
|
||||
/// Handler for `engine_newPayloadV1`
|
||||
/// See also <https://github.com/ethereum/execution-apis/blob/3d627c95a4d3510a8187dd02e0250ecb4331d27e/src/engine/paris.md#engine_newpayloadv1>
|
||||
@ -645,7 +520,7 @@ where
|
||||
async fn fork_choice_updated_v1(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
payload_attributes: Option<EngineT::PayloadAttributes>,
|
||||
) -> RpcResult<ForkchoiceUpdated> {
|
||||
trace!(target: "rpc::engine", "Serving engine_forkchoiceUpdatedV1");
|
||||
let start = Instant::now();
|
||||
@ -660,7 +535,7 @@ where
|
||||
async fn fork_choice_updated_v2(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
payload_attributes: Option<EngineT::PayloadAttributes>,
|
||||
) -> RpcResult<ForkchoiceUpdated> {
|
||||
trace!(target: "rpc::engine", "Serving engine_forkchoiceUpdatedV2");
|
||||
let start = Instant::now();
|
||||
@ -676,7 +551,7 @@ where
|
||||
async fn fork_choice_updated_v3(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
payload_attributes: Option<EngineT::PayloadAttributes>,
|
||||
) -> RpcResult<ForkchoiceUpdated> {
|
||||
trace!(target: "rpc::engine", "Serving engine_forkchoiceUpdatedV3");
|
||||
let start = Instant::now();
|
||||
@ -800,7 +675,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Provider> std::fmt::Debug for EngineApi<Provider> {
|
||||
impl<Provider, EngineT> std::fmt::Debug for EngineApi<Provider, EngineT>
|
||||
where
|
||||
EngineT: EngineTypes,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("EngineApi").finish_non_exhaustive()
|
||||
}
|
||||
@ -812,6 +690,7 @@ mod tests {
|
||||
use assert_matches::assert_matches;
|
||||
use reth_beacon_consensus::BeaconEngineMessage;
|
||||
use reth_interfaces::test_utils::generators::random_block;
|
||||
use reth_node_builder::EthEngineTypes;
|
||||
use reth_payload_builder::test_utils::spawn_test_payload_service;
|
||||
use reth_primitives::{SealedBlock, B256, MAINNET};
|
||||
use reth_provider::test_utils::MockEthProvider;
|
||||
@ -820,7 +699,8 @@ mod tests {
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
|
||||
|
||||
fn setup_engine_api() -> (EngineApiTestHandle, EngineApi<Arc<MockEthProvider>>) {
|
||||
fn setup_engine_api() -> (EngineApiTestHandle, EngineApi<Arc<MockEthProvider>, EthEngineTypes>)
|
||||
{
|
||||
let chain_spec: Arc<ChainSpec> = MAINNET.clone();
|
||||
let provider = Arc::new(MockEthProvider::default());
|
||||
let payload_store = spawn_test_payload_service();
|
||||
@ -840,7 +720,7 @@ mod tests {
|
||||
struct EngineApiTestHandle {
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
provider: Arc<MockEthProvider>,
|
||||
from_api: UnboundedReceiver<BeaconEngineMessage>,
|
||||
from_api: UnboundedReceiver<BeaconEngineMessage<EthEngineTypes>>,
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@ -2,6 +2,7 @@ use jsonrpsee_types::error::{
|
||||
INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE, INVALID_PARAMS_MSG, SERVER_ERROR_MSG,
|
||||
};
|
||||
use reth_beacon_consensus::{BeaconForkChoiceUpdateError, BeaconOnNewPayloadError};
|
||||
use reth_node_api::AttributesValidationError;
|
||||
use reth_payload_builder::error::PayloadBuilderError;
|
||||
use reth_primitives::{B256, U256};
|
||||
use thiserror::Error;
|
||||
@ -43,27 +44,6 @@ pub enum EngineApiError {
|
||||
/// Requested number of items
|
||||
count: u64,
|
||||
},
|
||||
/// Thrown if `PayloadAttributes` provided in engine_forkchoiceUpdated before V3 contains a
|
||||
/// parent beacon block root
|
||||
#[error("parent beacon block root not supported before V3")]
|
||||
ParentBeaconBlockRootNotSupportedBeforeV3,
|
||||
/// Thrown if engine_forkchoiceUpdatedV1 contains withdrawals
|
||||
#[error("withdrawals not supported in V1")]
|
||||
WithdrawalsNotSupportedInV1,
|
||||
/// Thrown if engine_forkchoiceUpdated contains no withdrawals after Shanghai
|
||||
#[error("no withdrawals post-Shanghai")]
|
||||
NoWithdrawalsPostShanghai,
|
||||
/// Thrown if engine_forkchoiceUpdated contains withdrawals before Shanghai
|
||||
#[error("withdrawals pre-Shanghai")]
|
||||
HasWithdrawalsPreShanghai,
|
||||
/// Thrown if the `PayloadAttributes` provided in engine_forkchoiceUpdated contains no parent
|
||||
/// beacon block root after Cancun
|
||||
#[error("no parent beacon block root post-cancun")]
|
||||
NoParentBeaconBlockRootPostCancun,
|
||||
/// Thrown if `PayloadAttributes` were provided with a timestamp, but the version of the engine
|
||||
/// method called is meant for a fork that occurs after the provided timestamp.
|
||||
#[error("Unsupported fork")]
|
||||
UnsupportedFork,
|
||||
/// Terminal total difficulty mismatch during transition configuration exchange.
|
||||
#[error(
|
||||
"invalid transition terminal total difficulty: \
|
||||
@ -98,6 +78,9 @@ pub enum EngineApiError {
|
||||
/// Fetching the payload failed
|
||||
#[error(transparent)]
|
||||
GetPayloadError(#[from] PayloadBuilderError),
|
||||
/// The payload or attributes are known to be malformed before processing.
|
||||
#[error(transparent)]
|
||||
AttributesValidationError(#[from] AttributesValidationError),
|
||||
/// If the optimism feature flag is enabled, the payload attributes must have a present
|
||||
/// gas limit for the forkchoice updated method.
|
||||
#[cfg(feature = "optimism")]
|
||||
@ -123,11 +106,24 @@ impl From<EngineApiError> for jsonrpsee_types::error::ErrorObject<'static> {
|
||||
fn from(error: EngineApiError) -> Self {
|
||||
match error {
|
||||
EngineApiError::InvalidBodiesRange { .. } |
|
||||
EngineApiError::WithdrawalsNotSupportedInV1 |
|
||||
EngineApiError::ParentBeaconBlockRootNotSupportedBeforeV3 |
|
||||
EngineApiError::NoParentBeaconBlockRootPostCancun |
|
||||
EngineApiError::NoWithdrawalsPostShanghai |
|
||||
EngineApiError::HasWithdrawalsPreShanghai => {
|
||||
EngineApiError::AttributesValidationError(
|
||||
AttributesValidationError::WithdrawalsNotSupportedInV1,
|
||||
) |
|
||||
EngineApiError::AttributesValidationError(
|
||||
AttributesValidationError::ParentBeaconBlockRootNotSupportedBeforeV3,
|
||||
) |
|
||||
EngineApiError::AttributesValidationError(
|
||||
AttributesValidationError::NoParentBeaconBlockRootPostCancun,
|
||||
) |
|
||||
EngineApiError::AttributesValidationError(
|
||||
AttributesValidationError::NoWithdrawalsPostShanghai,
|
||||
) |
|
||||
EngineApiError::AttributesValidationError(
|
||||
AttributesValidationError::HasWithdrawalsPreShanghai,
|
||||
) |
|
||||
EngineApiError::AttributesValidationError(
|
||||
AttributesValidationError::InvalidParams(_),
|
||||
) => {
|
||||
// Note: the data field is not required by the spec, but is also included by other
|
||||
// clients
|
||||
jsonrpsee_types::error::ErrorObject::owned(
|
||||
@ -148,7 +144,9 @@ impl From<EngineApiError> for jsonrpsee_types::error::ErrorObject<'static> {
|
||||
Some(ErrorData::new(error)),
|
||||
)
|
||||
}
|
||||
EngineApiError::UnsupportedFork => jsonrpsee_types::error::ErrorObject::owned(
|
||||
EngineApiError::AttributesValidationError(
|
||||
AttributesValidationError::UnsupportedFork,
|
||||
) => jsonrpsee_types::error::ErrorObject::owned(
|
||||
UNSUPPORTED_FORK_CODE,
|
||||
error.to_string(),
|
||||
None::<()>,
|
||||
@ -238,7 +236,7 @@ mod tests {
|
||||
ensure_engine_rpc_error(
|
||||
UNSUPPORTED_FORK_CODE,
|
||||
"Unsupported fork",
|
||||
EngineApiError::UnsupportedFork,
|
||||
EngineApiError::AttributesValidationError(AttributesValidationError::UnsupportedFork),
|
||||
);
|
||||
|
||||
ensure_engine_rpc_error(
|
||||
|
||||
@ -14,9 +14,6 @@ mod engine_api;
|
||||
/// The Engine API message type.
|
||||
mod message;
|
||||
|
||||
/// An type representing either an execution payload or payload attributes.
|
||||
mod payload;
|
||||
|
||||
/// Engine API error.
|
||||
mod error;
|
||||
|
||||
|
||||
@ -81,16 +81,14 @@ struct BeaconPayloadAttributes {
|
||||
withdrawals: Option<Vec<Withdrawal>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
parent_beacon_block_root: Option<B256>,
|
||||
#[cfg(feature = "optimism")]
|
||||
#[serde(flatten)]
|
||||
optimism_payload_attributes: BeaconOptimismPayloadAttributes,
|
||||
}
|
||||
|
||||
/// Optimism Payload Attributes
|
||||
#[cfg(feature = "optimism")]
|
||||
#[serde_as]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct BeaconOptimismPayloadAttributes {
|
||||
#[serde(flatten)]
|
||||
payload_attributes: BeaconPayloadAttributes,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
transactions: Option<Vec<Bytes>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
@ -100,6 +98,69 @@ struct BeaconOptimismPayloadAttributes {
|
||||
gas_limit: Option<u64>,
|
||||
}
|
||||
|
||||
/// A helper module for serializing and deserializing optimism payload attributes for the beacon
|
||||
/// API.
|
||||
///
|
||||
/// See docs for [beacon_api_payload_attributes].
|
||||
pub mod beacon_api_payload_attributes_optimism {
|
||||
use super::*;
|
||||
use crate::engine::{OptimismPayloadAttributes, PayloadAttributes};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
/// Serialize the payload attributes for the beacon API.
|
||||
pub fn serialize<S>(
|
||||
payload_attributes: &OptimismPayloadAttributes,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let beacon_api_payload_attributes = BeaconPayloadAttributes {
|
||||
timestamp: payload_attributes.payload_attributes.timestamp,
|
||||
prev_randao: payload_attributes.payload_attributes.prev_randao,
|
||||
suggested_fee_recipient: payload_attributes.payload_attributes.suggested_fee_recipient,
|
||||
withdrawals: payload_attributes.payload_attributes.withdrawals.clone(),
|
||||
parent_beacon_block_root: payload_attributes
|
||||
.payload_attributes
|
||||
.parent_beacon_block_root,
|
||||
};
|
||||
|
||||
let op_beacon_api_payload_attributes = BeaconOptimismPayloadAttributes {
|
||||
payload_attributes: beacon_api_payload_attributes,
|
||||
transactions: payload_attributes.transactions.clone(),
|
||||
no_tx_pool: payload_attributes.no_tx_pool,
|
||||
gas_limit: payload_attributes.gas_limit,
|
||||
};
|
||||
|
||||
op_beacon_api_payload_attributes.serialize(serializer)
|
||||
}
|
||||
|
||||
/// Deserialize the payload attributes for the beacon API.
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<OptimismPayloadAttributes, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let beacon_api_payload_attributes =
|
||||
BeaconOptimismPayloadAttributes::deserialize(deserializer)?;
|
||||
Ok(OptimismPayloadAttributes {
|
||||
payload_attributes: PayloadAttributes {
|
||||
timestamp: beacon_api_payload_attributes.payload_attributes.timestamp,
|
||||
prev_randao: beacon_api_payload_attributes.payload_attributes.prev_randao,
|
||||
suggested_fee_recipient: beacon_api_payload_attributes
|
||||
.payload_attributes
|
||||
.suggested_fee_recipient,
|
||||
withdrawals: beacon_api_payload_attributes.payload_attributes.withdrawals,
|
||||
parent_beacon_block_root: beacon_api_payload_attributes
|
||||
.payload_attributes
|
||||
.parent_beacon_block_root,
|
||||
},
|
||||
transactions: beacon_api_payload_attributes.transactions,
|
||||
no_tx_pool: beacon_api_payload_attributes.no_tx_pool,
|
||||
gas_limit: beacon_api_payload_attributes.gas_limit,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper module for serializing and deserializing the payload attributes for the beacon API.
|
||||
///
|
||||
/// The beacon API encoded object has equivalent fields to the
|
||||
@ -125,12 +186,6 @@ pub mod beacon_api_payload_attributes {
|
||||
suggested_fee_recipient: payload_attributes.suggested_fee_recipient,
|
||||
withdrawals: payload_attributes.withdrawals.clone(),
|
||||
parent_beacon_block_root: payload_attributes.parent_beacon_block_root,
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism_payload_attributes: BeaconOptimismPayloadAttributes {
|
||||
transactions: payload_attributes.optimism_payload_attributes.transactions.clone(),
|
||||
no_tx_pool: payload_attributes.optimism_payload_attributes.no_tx_pool,
|
||||
gas_limit: payload_attributes.optimism_payload_attributes.gas_limit,
|
||||
},
|
||||
};
|
||||
beacon_api_payload_attributes.serialize(serializer)
|
||||
}
|
||||
@ -147,14 +202,6 @@ pub mod beacon_api_payload_attributes {
|
||||
suggested_fee_recipient: beacon_api_payload_attributes.suggested_fee_recipient,
|
||||
withdrawals: beacon_api_payload_attributes.withdrawals,
|
||||
parent_beacon_block_root: beacon_api_payload_attributes.parent_beacon_block_root,
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism_payload_attributes: crate::eth::engine::OptimismPayloadAttributes {
|
||||
transactions: beacon_api_payload_attributes
|
||||
.optimism_payload_attributes
|
||||
.transactions,
|
||||
no_tx_pool: beacon_api_payload_attributes.optimism_payload_attributes.no_tx_pool,
|
||||
gas_limit: beacon_api_payload_attributes.optimism_payload_attributes.gas_limit,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -623,17 +623,15 @@ pub struct PayloadAttributes {
|
||||
/// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#payloadattributesv3>
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub parent_beacon_block_root: Option<B256>,
|
||||
/// Optimism Payload Attributes
|
||||
#[cfg(feature = "optimism")]
|
||||
#[serde(flatten)]
|
||||
pub optimism_payload_attributes: OptimismPayloadAttributes,
|
||||
}
|
||||
|
||||
/// Optimism Payload Attributes
|
||||
#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg(feature = "optimism")]
|
||||
pub struct OptimismPayloadAttributes {
|
||||
/// The payload attributes
|
||||
#[serde(flatten)]
|
||||
pub payload_attributes: PayloadAttributes,
|
||||
/// Transactions is a field for rollups: the transactions list is forced into the block
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub transactions: Option<Vec<Bytes>>,
|
||||
|
||||
Reference in New Issue
Block a user