Kiln mev boost (#3062)

## Issue Addressed

MEV boost compatibility

## Proposed Changes

See #2987

## Additional Info

This is blocked on the stabilization of a couple specs, [here](https://github.com/ethereum/beacon-APIs/pull/194) and [here](https://github.com/flashbots/mev-boost/pull/20).

Additional TODO's and outstanding questions

- [ ] MEV boost JWT Auth
- [ ] Will `builder_proposeBlindedBlock` return the revealed payload for the BN to propogate
- [ ] Should we remove `private-tx-proposals` flag and communicate BN <> VC with blinded blocks by default once these endpoints enter the beacon-API's repo? This simplifies merge transition logic. 

Co-authored-by: realbigsean <seananderson33@gmail.com>
Co-authored-by: realbigsean <sean@sigmaprime.io>
This commit is contained in:
realbigsean 2022-03-31 07:52:23 +00:00
parent 83234ee4ce
commit ea783360d3
48 changed files with 1628 additions and 644 deletions

222
Cargo.lock generated
View File

@ -86,7 +86,7 @@ checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cipher", "cipher",
"cpufeatures 0.2.1", "cpufeatures 0.2.2",
"ctr", "ctr",
"opaque-debug", "opaque-debug",
] ]
@ -293,7 +293,7 @@ dependencies = [
"eth2", "eth2",
"eth2_hashing 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_hashing 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"eth2_ssz_types", "eth2_ssz_types",
"execution_layer", "execution_layer",
"fork_choice", "fork_choice",
@ -610,7 +610,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"eth2_hashing 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_hashing 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"eth2_ssz_types", "eth2_ssz_types",
"ethereum-types 0.12.1", "ethereum-types 0.12.1",
"quickcheck 0.9.2", "quickcheck 0.9.2",
@ -640,7 +640,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [ dependencies = [
"nom 7.1.0", "nom 7.1.1",
] ]
[[package]] [[package]]
@ -657,7 +657,7 @@ checksum = "01b72a433d0cf2aef113ba70f62634c56fddb0f244e6377185c56a7cadbd8f91"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cipher", "cipher",
"cpufeatures 0.2.1", "cpufeatures 0.2.2",
"zeroize", "zeroize",
] ]
@ -769,7 +769,7 @@ dependencies = [
"slot_clock", "slot_clock",
"store", "store",
"task_executor", "task_executor",
"time 0.3.7", "time 0.3.9",
"timer", "timer",
"tokio", "tokio",
"toml", "toml",
@ -858,9 +858,9 @@ dependencies = [
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -912,9 +912,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.2" version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
@ -933,10 +933,11 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.9.7" version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
dependencies = [ dependencies = [
"autocfg 1.1.0",
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
"lazy_static", "lazy_static",
@ -946,9 +947,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.7" version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"lazy_static", "lazy_static",
@ -1228,9 +1229,9 @@ dependencies = [
[[package]] [[package]]
name = "dirs-sys" name = "dirs-sys"
version = "0.3.6" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [ dependencies = [
"libc", "libc",
"redox_users", "redox_users",
@ -1301,9 +1302,9 @@ dependencies = [
[[package]] [[package]]
name = "ed25519" name = "ed25519"
version = "1.4.0" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed12bbf7b5312f8da1c2722bc06d8c6b12c2d86a7fb35a194c7f3e6fc2bbe39" checksum = "3d5c4b5e5959dc2c2b89918d8e2cc40fcdd623cef026ed09d2f0ee05199dc8e4"
dependencies = [ dependencies = [
"signature", "signature",
] ]
@ -1333,7 +1334,7 @@ dependencies = [
"compare_fields_derive", "compare_fields_derive",
"derivative", "derivative",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"ethereum-types 0.12.1", "ethereum-types 0.12.1",
"fork_choice", "fork_choice",
"fs2", "fs2",
@ -1348,7 +1349,7 @@ dependencies = [
"store", "store",
"swap_or_not_shuffle", "swap_or_not_shuffle",
"tree_hash", "tree_hash",
"tree_hash_derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tree_hash_derive",
"types", "types",
] ]
@ -1476,7 +1477,7 @@ dependencies = [
"eth1_test_rig", "eth1_test_rig",
"eth2", "eth2",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"fallback", "fallback",
"futures", "futures",
"hex", "hex",
@ -1520,7 +1521,7 @@ dependencies = [
"eth2_keystore", "eth2_keystore",
"eth2_serde_utils", "eth2_serde_utils",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"futures", "futures",
"futures-util", "futures-util",
"libsecp256k1 0.6.0", "libsecp256k1 0.6.0",
@ -1647,7 +1648,7 @@ dependencies = [
name = "eth2_ssz" name = "eth2_ssz"
version = "0.4.1" version = "0.4.1"
dependencies = [ dependencies = [
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"ethereum-types 0.12.1", "ethereum-types 0.12.1",
"smallvec", "smallvec",
] ]
@ -1662,18 +1663,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "eth2_ssz_derive"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "635b86d2c941bb71e7419a571e1763d65c93e51a1bafc400352e3bef6ff59fc9"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "eth2_ssz_types" name = "eth2_ssz_types"
version = "0.2.2" version = "0.2.2"
@ -1687,7 +1676,7 @@ dependencies = [
"serde_json", "serde_json",
"smallvec", "smallvec",
"tree_hash", "tree_hash",
"tree_hash_derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tree_hash_derive",
"typenum", "typenum",
] ]
@ -1860,7 +1849,7 @@ dependencies = [
"tempfile", "tempfile",
"tokio", "tokio",
"tree_hash", "tree_hash",
"tree_hash_derive 0.4.0", "tree_hash_derive",
"types", "types",
"warp 0.3.0", "warp 0.3.0",
"zeroize", "zeroize",
@ -2008,7 +1997,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"beacon_chain", "beacon_chain",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"proto_array", "proto_array",
"store", "store",
"types", "types",
@ -2102,9 +2091,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-rustls" name = "futures-rustls"
version = "0.22.0" version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d383f0425d991a05e564c2f3ec150bd6dde863179c131dd60d8aa73a05434461" checksum = "e01fe9932a224b72b45336d96040aa86386d674a31d0af27d800ea7bc8ca97fe"
dependencies = [ dependencies = [
"futures-io", "futures-io",
"rustls 0.20.4", "rustls 0.20.4",
@ -2454,6 +2443,7 @@ dependencies = [
"eth1", "eth1",
"eth2", "eth2",
"eth2_ssz", "eth2_ssz",
"execution_layer",
"futures", "futures",
"hex", "hex",
"lazy_static", "lazy_static",
@ -2518,9 +2508,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.17" version = "0.14.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043f0e083e9901b6cc658a77d1eb86f4fc650bbb977a4337dd63192826aa85dd" checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -2884,15 +2874,15 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.119" version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
[[package]] [[package]]
name = "libflate" name = "libflate"
version = "1.1.2" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d57e534717ac3e0b8dc459fe338bdfb4e29d7eea8fd0926ba649ddd3f4765f" checksum = "05605ab2bce11bcfc0e9c635ff29ef8b2ea83f29be257ee7d730cac3ee373093"
dependencies = [ dependencies = [
"adler32", "adler32",
"crc32fast", "crc32fast",
@ -2926,9 +2916,9 @@ checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db"
[[package]] [[package]]
name = "libmdbx" name = "libmdbx"
version = "0.1.1" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9a8a3723c12c5caa3f2a456b645063d1d8ffb1562895fa43746a999d205b0c6" checksum = "3265f0f9e378bfbbd98596a3288b5909f26f3169e4f6d4a05fda8c734ce2cdd8"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"byteorder", "byteorder",
@ -2936,7 +2926,7 @@ dependencies = [
"indexmap", "indexmap",
"libc", "libc",
"mdbx-sys", "mdbx-sys",
"parking_lot 0.11.2", "parking_lot 0.12.0",
"thiserror", "thiserror",
] ]
@ -3193,9 +3183,9 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-swarm-derive" name = "libp2p-swarm-derive"
version = "0.27.0" version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8153a6472e84ec888ef2bf21deafe8d4214e811f0f162abbf07156c27f8fa8" checksum = "daf2fe8c80b43561355f4d51875273b5b6dfbac37952e8f64b1270769305c9d7"
dependencies = [ dependencies = [
"quote", "quote",
"syn", "syn",
@ -3421,7 +3411,7 @@ dependencies = [
"discv5", "discv5",
"error-chain", "error-chain",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"eth2_ssz_types", "eth2_ssz_types",
"exit-future", "exit-future",
"fnv", "fnv",
@ -3493,9 +3483,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.14" version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@ -3584,9 +3574,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]] [[package]]
name = "mdbx-sys" name = "mdbx-sys"
version = "0.11.4-git.20210105" version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b21b3e0def3a5c880f6388ed2e33b695097c6b0eca039dae6010527b059f8be1" checksum = "cb471ee10f93c8c276083d59cae56365cca92d5bb2e27959da89ced5a8adf13e"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"cc", "cc",
@ -3667,14 +3657,15 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.0" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
"miow", "miow",
"ntapi", "ntapi",
"wasi 0.11.0+wasi-snapshot-preview1",
"winapi", "winapi",
] ]
@ -3981,13 +3972,12 @@ checksum = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff"
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.0" version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
dependencies = [ dependencies = [
"memchr", "memchr",
"minimal-lexical", "minimal-lexical",
"version_check",
] ]
[[package]] [[package]]
@ -4071,9 +4061,9 @@ dependencies = [
[[package]] [[package]]
name = "num_threads" name = "num_threads"
version = "0.1.3" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -4155,7 +4145,7 @@ dependencies = [
"beacon_chain", "beacon_chain",
"derivative", "derivative",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"itertools", "itertools",
"lazy_static", "lazy_static",
"lighthouse_metrics", "lighthouse_metrics",
@ -4441,7 +4431,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede"
dependencies = [ dependencies = [
"cpufeatures 0.2.1", "cpufeatures 0.2.2",
"opaque-debug", "opaque-debug",
"universal-hash", "universal-hash",
] ]
@ -4453,7 +4443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.2.1", "cpufeatures 0.2.2",
"opaque-debug", "opaque-debug",
"universal-hash", "universal-hash",
] ]
@ -4660,7 +4650,7 @@ name = "proto_array"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"serde", "serde",
"serde_derive", "serde_derive",
"serde_yaml", "serde_yaml",
@ -4742,9 +4732,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.15" version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -4905,21 +4895,22 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.11" version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.4.0" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55"
dependencies = [ dependencies = [
"getrandom 0.2.5", "getrandom 0.2.5",
"redox_syscall", "redox_syscall",
"thiserror",
] ]
[[package]] [[package]]
@ -4959,9 +4950,9 @@ dependencies = [
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.9" version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
dependencies = [ dependencies = [
"base64 0.13.0", "base64 0.13.0",
"bytes", "bytes",
@ -4991,7 +4982,7 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
"winreg 0.7.0", "winreg 0.10.1",
] ]
[[package]] [[package]]
@ -5435,7 +5426,7 @@ checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
dependencies = [ dependencies = [
"block-buffer 0.9.0", "block-buffer 0.9.0",
"cfg-if", "cfg-if",
"cpufeatures 0.2.1", "cpufeatures 0.2.2",
"digest 0.9.0", "digest 0.9.0",
"opaque-debug", "opaque-debug",
] ]
@ -5447,7 +5438,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.2.1", "cpufeatures 0.2.2",
"digest 0.10.3", "digest 0.10.3",
] ]
@ -5459,7 +5450,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [ dependencies = [
"block-buffer 0.9.0", "block-buffer 0.9.0",
"cfg-if", "cfg-if",
"cpufeatures 0.2.1", "cpufeatures 0.2.2",
"digest 0.9.0", "digest 0.9.0",
"opaque-debug", "opaque-debug",
] ]
@ -5471,7 +5462,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.2.1", "cpufeatures 0.2.2",
"digest 0.10.3", "digest 0.10.3",
] ]
@ -5530,7 +5521,7 @@ dependencies = [
"num-bigint", "num-bigint",
"num-traits", "num-traits",
"thiserror", "thiserror",
"time 0.3.7", "time 0.3.9",
] ]
[[package]] [[package]]
@ -5563,7 +5554,7 @@ dependencies = [
"bincode", "bincode",
"byteorder", "byteorder",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"filesystem", "filesystem",
"flate2", "flate2",
"lazy_static", "lazy_static",
@ -5582,7 +5573,7 @@ dependencies = [
"sloggers", "sloggers",
"tempfile", "tempfile",
"tree_hash", "tree_hash",
"tree_hash_derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tree_hash_derive",
"types", "types",
] ]
@ -5642,14 +5633,14 @@ dependencies = [
[[package]] [[package]]
name = "slog-json" name = "slog-json"
version = "2.6.0" version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70f825ce7346f40aa318111df5d3a94945a7fdca9081584cb9b05692fb3dfcb4" checksum = "3e1e53f61af1e3c8b852eef0a9dee29008f55d6dd63794f3f12cef786cf0f219"
dependencies = [ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"slog", "slog",
"time 0.3.7", "time 0.3.9",
] ]
[[package]] [[package]]
@ -5675,9 +5666,9 @@ dependencies = [
[[package]] [[package]]
name = "slog-stdlog" name = "slog-stdlog"
version = "4.1.0" version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8228ab7302adbf4fcb37e66f3cda78003feb521e7fd9e3847ec117a7784d0f5a" checksum = "6706b2ace5bbae7291d3f8d2473e2bfab073ccd7d03670946197aec98471fa3e"
dependencies = [ dependencies = [
"log", "log",
"slog", "slog",
@ -5694,7 +5685,7 @@ dependencies = [
"slog", "slog",
"term", "term",
"thread_local", "thread_local",
"time 0.3.7", "time 0.3.9",
] ]
[[package]] [[package]]
@ -5882,7 +5873,7 @@ dependencies = [
"db-key", "db-key",
"directory", "directory",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"itertools", "itertools",
"lazy_static", "lazy_static",
"leveldb", "leveldb",
@ -5961,9 +5952,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -6128,9 +6119,9 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.7" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
dependencies = [ dependencies = [
"itoa 1.0.1", "itoa 1.0.1",
"libc", "libc",
@ -6141,9 +6132,9 @@ dependencies = [
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.3" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]] [[package]]
name = "timer" name = "timer"
@ -6448,11 +6439,11 @@ dependencies = [
"beacon_chain", "beacon_chain",
"eth2_hashing 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_hashing 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"ethereum-types 0.12.1", "ethereum-types 0.12.1",
"rand 0.7.3", "rand 0.7.3",
"smallvec", "smallvec",
"tree_hash_derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tree_hash_derive",
"types", "types",
] ]
@ -6465,17 +6456,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tree_hash_derive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd22d128157837a4434bb51119aef11103f17bfe8c402ce688cf25aa1e608ad"
dependencies = [
"darling",
"quote",
"syn",
]
[[package]] [[package]]
name = "trust-dns-proto" name = "trust-dns-proto"
version = "0.20.4" version = "0.20.4"
@ -6596,7 +6576,7 @@ dependencies = [
"eth2_interop_keypairs", "eth2_interop_keypairs",
"eth2_serde_utils", "eth2_serde_utils",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth2_ssz_derive",
"eth2_ssz_types", "eth2_ssz_types",
"ethereum-types 0.12.1", "ethereum-types 0.12.1",
"hex", "hex",
@ -6623,7 +6603,7 @@ dependencies = [
"tempfile", "tempfile",
"test_random_derive", "test_random_derive",
"tree_hash", "tree_hash",
"tree_hash_derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tree_hash_derive",
] ]
[[package]] [[package]]
@ -6983,6 +6963,12 @@ version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.79" version = "0.2.79"
@ -7199,9 +7185,9 @@ dependencies = [
[[package]] [[package]]
name = "which" name = "which"
version = "4.2.4" version = "4.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2" checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
dependencies = [ dependencies = [
"either", "either",
"lazy_static", "lazy_static",
@ -7311,9 +7297,9 @@ dependencies = [
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.7.0" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [ dependencies = [
"winapi", "winapi",
] ]
@ -7361,14 +7347,14 @@ dependencies = [
[[package]] [[package]]
name = "yamux" name = "yamux"
version = "0.10.0" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29d4c1dd079043fe673e79fe3c3a260ae2d2fb413f1062cae9e062748df0df03" checksum = "0c0608f53c1dc0bad505d03a34bbd49fbf2ad7b51eb036123e896365532745a1"
dependencies = [ dependencies = [
"futures", "futures",
"log", "log",
"nohash-hasher", "nohash-hasher",
"parking_lot 0.11.2", "parking_lot 0.12.0",
"rand 0.8.5", "rand 0.8.5",
"static_assertions", "static_assertions",
] ]

View File

@ -91,6 +91,8 @@ members = [
fixed-hash = { git = "https://github.com/paritytech/parity-common", rev="df638ab0885293d21d656dc300d39236b69ce57d" } fixed-hash = { git = "https://github.com/paritytech/parity-common", rev="df638ab0885293d21d656dc300d39236b69ce57d" }
warp = { git = "https://github.com/macladson/warp", rev ="7e75acc" } warp = { git = "https://github.com/macladson/warp", rev ="7e75acc" }
eth2_ssz = { path = "consensus/ssz" } eth2_ssz = { path = "consensus/ssz" }
eth2_ssz_derive = { path = "consensus/ssz_derive" }
eth2_ssz_types = { path = "consensus/ssz_types" } eth2_ssz_types = { path = "consensus/ssz_types" }
tree_hash = { path = "consensus/tree_hash" } tree_hash = { path = "consensus/tree_hash" }
tree_hash_derive = { path = "consensus/tree_hash_derive" }
eth2_serde_utils = { path = "consensus/serde_utils" } eth2_serde_utils = { path = "consensus/serde_utils" }

View File

@ -59,7 +59,7 @@ strum = { version = "0.21.0", features = ["derive"] }
logging = { path = "../../common/logging" } logging = { path = "../../common/logging" }
execution_layer = { path = "../execution_layer" } execution_layer = { path = "../execution_layer" }
sensitive_url = { path = "../../common/sensitive_url" } sensitive_url = { path = "../../common/sensitive_url" }
superstruct = "0.4.0" superstruct = "0.4.1"
[[test]] [[test]]
name = "beacon_chain_tests" name = "beacon_chain_tests"

View File

@ -80,6 +80,7 @@ use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::io::prelude::*; use std::io::prelude::*;
use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use store::iter::{BlockRootsIterator, ParentRootBlockIterator, StateRootsIterator}; use store::iter::{BlockRootsIterator, ParentRootBlockIterator, StateRootsIterator};
@ -370,7 +371,7 @@ pub struct BeaconChain<T: BeaconChainTypes> {
pub validator_monitor: RwLock<ValidatorMonitor<T::EthSpec>>, pub validator_monitor: RwLock<ValidatorMonitor<T::EthSpec>>,
} }
type BeaconBlockAndState<T> = (BeaconBlock<T>, BeaconState<T>); type BeaconBlockAndState<T, Payload> = (BeaconBlock<T, Payload>, BeaconState<T>);
impl<T: BeaconChainTypes> BeaconChain<T> { impl<T: BeaconChainTypes> BeaconChain<T> {
/// Persists the head tracker and fork choice. /// Persists the head tracker and fork choice.
@ -1151,7 +1152,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.body() .body()
.execution_payload() .execution_payload()
.ok() .ok()
.map(|ep| ep.block_hash), .map(|ep| ep.block_hash()),
random, random,
}) })
}) })
@ -2892,12 +2893,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// ///
/// The produced block will not be inherently valid, it must be signed by a block producer. /// The produced block will not be inherently valid, it must be signed by a block producer.
/// Block signing is out of the scope of this function and should be done by a separate program. /// Block signing is out of the scope of this function and should be done by a separate program.
pub fn produce_block( pub fn produce_block<Payload: ExecPayload<T::EthSpec>>(
&self, &self,
randao_reveal: Signature, randao_reveal: Signature,
slot: Slot, slot: Slot,
validator_graffiti: Option<Graffiti>, validator_graffiti: Option<Graffiti>,
) -> Result<BeaconBlockAndState<T::EthSpec>, BlockProductionError> { ) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> {
self.produce_block_with_verification( self.produce_block_with_verification(
randao_reveal, randao_reveal,
slot, slot,
@ -2907,13 +2908,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
} }
/// Same as `produce_block` but allowing for configuration of RANDAO-verification. /// Same as `produce_block` but allowing for configuration of RANDAO-verification.
pub fn produce_block_with_verification( pub fn produce_block_with_verification<Payload: ExecPayload<T::EthSpec>>(
&self, &self,
randao_reveal: Signature, randao_reveal: Signature,
slot: Slot, slot: Slot,
validator_graffiti: Option<Graffiti>, validator_graffiti: Option<Graffiti>,
verification: ProduceBlockVerification, verification: ProduceBlockVerification,
) -> Result<BeaconBlockAndState<T::EthSpec>, BlockProductionError> { ) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> {
metrics::inc_counter(&metrics::BLOCK_PRODUCTION_REQUESTS); metrics::inc_counter(&metrics::BLOCK_PRODUCTION_REQUESTS);
let _complete_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_TIMES); let _complete_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_TIMES);
@ -2964,7 +2965,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}; };
drop(state_load_timer); drop(state_load_timer);
self.produce_block_on_state( self.produce_block_on_state::<Payload>(
state, state,
state_root_opt, state_root_opt,
slot, slot,
@ -2986,7 +2987,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// The provided `state_root_opt` should only ever be set to `Some` if the contained value is /// The provided `state_root_opt` should only ever be set to `Some` if the contained value is
/// equal to the root of `state`. Providing this value will serve as an optimization to avoid /// equal to the root of `state`. Providing this value will serve as an optimization to avoid
/// performing a tree hash in some scenarios. /// performing a tree hash in some scenarios.
pub fn produce_block_on_state( pub fn produce_block_on_state<Payload: ExecPayload<T::EthSpec>>(
&self, &self,
mut state: BeaconState<T::EthSpec>, mut state: BeaconState<T::EthSpec>,
state_root_opt: Option<Hash256>, state_root_opt: Option<Hash256>,
@ -2994,7 +2995,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
randao_reveal: Signature, randao_reveal: Signature,
validator_graffiti: Option<Graffiti>, validator_graffiti: Option<Graffiti>,
verification: ProduceBlockVerification, verification: ProduceBlockVerification,
) -> Result<BeaconBlockAndState<T::EthSpec>, BlockProductionError> { ) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> {
let eth1_chain = self let eth1_chain = self
.eth1_chain .eth1_chain
.as_ref() .as_ref()
@ -3118,6 +3119,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
attestations, attestations,
deposits, deposits,
voluntary_exits: voluntary_exits.into(), voluntary_exits: voluntary_exits.into(),
_phantom: PhantomData,
}, },
}), }),
BeaconState::Altair(_) => { BeaconState::Altair(_) => {
@ -3137,12 +3139,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
deposits, deposits,
voluntary_exits: voluntary_exits.into(), voluntary_exits: voluntary_exits.into(),
sync_aggregate, sync_aggregate,
_phantom: PhantomData,
}, },
}) })
} }
BeaconState::Merge(_) => { BeaconState::Merge(_) => {
let sync_aggregate = get_sync_aggregate()?; let sync_aggregate = get_sync_aggregate()?;
let execution_payload = get_execution_payload(self, &state, proposer_index)?; let execution_payload =
get_execution_payload::<T, Payload>(self, &state, proposer_index)?;
BeaconBlock::Merge(BeaconBlockMerge { BeaconBlock::Merge(BeaconBlockMerge {
slot, slot,
proposer_index, proposer_index,

View File

@ -75,6 +75,7 @@ use std::io::Write;
use std::time::Duration; use std::time::Duration;
use store::{Error as DBError, HotColdDB, HotStateSummary, KeyValueStore, StoreOp}; use store::{Error as DBError, HotColdDB, HotStateSummary, KeyValueStore, StoreOp};
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::ExecPayload;
use types::{ use types::{
BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, CloneConfig, Epoch, EthSpec, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, CloneConfig, Epoch, EthSpec,
ExecutionBlockHash, Hash256, InconsistentFork, PublicKey, PublicKeyBytes, RelativeEpoch, ExecutionBlockHash, Hash256, InconsistentFork, PublicKey, PublicKeyBytes, RelativeEpoch,
@ -1295,9 +1296,9 @@ impl<'a, T: BeaconChainTypes> FullyVerifiedBlock<'a, T> {
if valid_merge_transition_block { if valid_merge_transition_block {
info!(chain.log, "{}", POS_PANDA_BANNER); info!(chain.log, "{}", POS_PANDA_BANNER);
info!(chain.log, "Proof of Stake Activated"; "slot" => block.slot()); info!(chain.log, "Proof of Stake Activated"; "slot" => block.slot());
info!(chain.log, ""; "Terminal POW Block Hash" => ?block.message().execution_payload()?.parent_hash.into_root()); info!(chain.log, ""; "Terminal POW Block Hash" => ?block.message().execution_payload()?.parent_hash().into_root());
info!(chain.log, ""; "Merge Transition Block Root" => ?block.message().tree_hash_root()); info!(chain.log, ""; "Merge Transition Block Root" => ?block.message().tree_hash_root());
info!(chain.log, ""; "Merge Transition Execution Hash" => ?block.message().execution_payload()?.block_hash.into_root()); info!(chain.log, ""; "Merge Transition Execution Hash" => ?block.message().execution_payload()?.block_hash().into_root());
} }
Ok(Self { Ok(Self {

View File

@ -53,8 +53,9 @@ pub fn notify_new_payload<T: BeaconChainTypes>(
.execution_layer .execution_layer
.as_ref() .as_ref()
.ok_or(ExecutionPayloadError::NoExecutionConnection)?; .ok_or(ExecutionPayloadError::NoExecutionConnection)?;
let new_payload_response = execution_layer let new_payload_response = execution_layer.block_on(|execution_layer| {
.block_on(|execution_layer| execution_layer.notify_new_payload(execution_payload)); execution_layer.notify_new_payload(&execution_payload.execution_payload)
});
match new_payload_response { match new_payload_response {
Ok(status) => match status { Ok(status) => match status {
@ -118,10 +119,10 @@ pub fn validate_merge_block<T: BeaconChainTypes>(
.into()); .into());
} }
if execution_payload.parent_hash != spec.terminal_block_hash { if execution_payload.parent_hash() != spec.terminal_block_hash {
return Err(ExecutionPayloadError::InvalidTerminalBlockHash { return Err(ExecutionPayloadError::InvalidTerminalBlockHash {
terminal_block_hash: spec.terminal_block_hash, terminal_block_hash: spec.terminal_block_hash,
payload_parent_hash: execution_payload.parent_hash, payload_parent_hash: execution_payload.parent_hash(),
} }
.into()); .into());
} }
@ -136,14 +137,14 @@ pub fn validate_merge_block<T: BeaconChainTypes>(
let is_valid_terminal_pow_block = execution_layer let is_valid_terminal_pow_block = execution_layer
.block_on(|execution_layer| { .block_on(|execution_layer| {
execution_layer.is_valid_terminal_pow_block_hash(execution_payload.parent_hash, spec) execution_layer.is_valid_terminal_pow_block_hash(execution_payload.parent_hash(), spec)
}) })
.map_err(ExecutionPayloadError::from)?; .map_err(ExecutionPayloadError::from)?;
match is_valid_terminal_pow_block { match is_valid_terminal_pow_block {
Some(true) => Ok(()), Some(true) => Ok(()),
Some(false) => Err(ExecutionPayloadError::InvalidTerminalPoWBlock { Some(false) => Err(ExecutionPayloadError::InvalidTerminalPoWBlock {
parent_hash: execution_payload.parent_hash, parent_hash: execution_payload.parent_hash(),
} }
.into()), .into()),
None => { None => {
@ -167,7 +168,7 @@ pub fn validate_merge_block<T: BeaconChainTypes>(
debug!( debug!(
chain.log, chain.log,
"Optimistically accepting terminal block"; "Optimistically accepting terminal block";
"block_hash" => ?execution_payload.parent_hash, "block_hash" => ?execution_payload.parent_hash(),
"msg" => "the terminal block/parent was unavailable" "msg" => "the terminal block/parent was unavailable"
); );
Ok(()) Ok(())
@ -215,11 +216,11 @@ pub fn validate_execution_payload_for_gossip<T: BeaconChainTypes>(
))?; ))?;
// The block's execution payload timestamp is correct with respect to the slot // The block's execution payload timestamp is correct with respect to the slot
if execution_payload.timestamp != expected_timestamp { if execution_payload.timestamp() != expected_timestamp {
return Err(BlockError::ExecutionPayloadError( return Err(BlockError::ExecutionPayloadError(
ExecutionPayloadError::InvalidPayloadTimestamp { ExecutionPayloadError::InvalidPayloadTimestamp {
expected: expected_timestamp, expected: expected_timestamp,
found: execution_payload.timestamp, found: execution_payload.timestamp(),
}, },
)); ));
} }
@ -241,20 +242,23 @@ pub fn validate_execution_payload_for_gossip<T: BeaconChainTypes>(
/// Equivalent to the `get_execution_payload` function in the Validator Guide: /// Equivalent to the `get_execution_payload` function in the Validator Guide:
/// ///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal /// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal
pub fn get_execution_payload<T: BeaconChainTypes>( pub fn get_execution_payload<T: BeaconChainTypes, Payload: ExecPayload<T::EthSpec>>(
chain: &BeaconChain<T>, chain: &BeaconChain<T>,
state: &BeaconState<T::EthSpec>, state: &BeaconState<T::EthSpec>,
proposer_index: u64, proposer_index: u64,
) -> Result<ExecutionPayload<T::EthSpec>, BlockProductionError> { ) -> Result<Payload, BlockProductionError> {
Ok(prepare_execution_payload_blocking(chain, state, proposer_index)?.unwrap_or_default()) Ok(
prepare_execution_payload_blocking::<T, Payload>(chain, state, proposer_index)?
.unwrap_or_default(),
)
} }
/// Wraps the async `prepare_execution_payload` function as a blocking task. /// Wraps the async `prepare_execution_payload` function as a blocking task.
pub fn prepare_execution_payload_blocking<T: BeaconChainTypes>( pub fn prepare_execution_payload_blocking<T: BeaconChainTypes, Payload: ExecPayload<T::EthSpec>>(
chain: &BeaconChain<T>, chain: &BeaconChain<T>,
state: &BeaconState<T::EthSpec>, state: &BeaconState<T::EthSpec>,
proposer_index: u64, proposer_index: u64,
) -> Result<Option<ExecutionPayload<T::EthSpec>>, BlockProductionError> { ) -> Result<Option<Payload>, BlockProductionError> {
let execution_layer = chain let execution_layer = chain
.execution_layer .execution_layer
.as_ref() .as_ref()
@ -262,7 +266,7 @@ pub fn prepare_execution_payload_blocking<T: BeaconChainTypes>(
execution_layer execution_layer
.block_on_generic(|_| async { .block_on_generic(|_| async {
prepare_execution_payload(chain, state, proposer_index).await prepare_execution_payload::<T, Payload>(chain, state, proposer_index).await
}) })
.map_err(BlockProductionError::BlockingFailed)? .map_err(BlockProductionError::BlockingFailed)?
} }
@ -281,11 +285,11 @@ pub fn prepare_execution_payload_blocking<T: BeaconChainTypes>(
/// Equivalent to the `prepare_execution_payload` function in the Validator Guide: /// Equivalent to the `prepare_execution_payload` function in the Validator Guide:
/// ///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal /// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal
pub async fn prepare_execution_payload<T: BeaconChainTypes>( pub async fn prepare_execution_payload<T: BeaconChainTypes, Payload: ExecPayload<T::EthSpec>>(
chain: &BeaconChain<T>, chain: &BeaconChain<T>,
state: &BeaconState<T::EthSpec>, state: &BeaconState<T::EthSpec>,
proposer_index: u64, proposer_index: u64,
) -> Result<Option<ExecutionPayload<T::EthSpec>>, BlockProductionError> { ) -> Result<Option<Payload>, BlockProductionError> {
let spec = &chain.spec; let spec = &chain.spec;
let execution_layer = chain let execution_layer = chain
.execution_layer .execution_layer
@ -335,12 +339,12 @@ pub async fn prepare_execution_payload<T: BeaconChainTypes>(
.body() .body()
.execution_payload() .execution_payload()
.ok() .ok()
.map(|ep| ep.block_hash) .map(|ep| ep.block_hash())
}; };
// Note: the suggested_fee_recipient is stored in the `execution_layer`, it will add this parameter. // Note: the suggested_fee_recipient is stored in the `execution_layer`, it will add this parameter.
let execution_payload = execution_layer let execution_payload = execution_layer
.get_payload( .get_payload::<T::EthSpec, Payload>(
parent_hash, parent_hash,
timestamp, timestamp,
random, random,

View File

@ -12,6 +12,7 @@ use state_processing::{
per_block_processing::{per_block_processing, BlockSignatureStrategy}, per_block_processing::{per_block_processing, BlockSignatureStrategy},
per_slot_processing, BlockProcessingError, VerifyBlockRoot, per_slot_processing, BlockProcessingError, VerifyBlockRoot,
}; };
use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use tempfile::tempdir; use tempfile::tempdir;
use types::{test_utils::generate_deterministic_keypair, *}; use types::{test_utils::generate_deterministic_keypair, *};
@ -962,6 +963,7 @@ fn add_base_block_to_altair_chain() {
attestations: altair_body.attestations.clone(), attestations: altair_body.attestations.clone(),
deposits: altair_body.deposits.clone(), deposits: altair_body.deposits.clone(),
voluntary_exits: altair_body.voluntary_exits.clone(), voluntary_exits: altair_body.voluntary_exits.clone(),
_phantom: PhantomData,
}, },
}, },
signature: Signature::empty(), signature: Signature::empty(),
@ -1082,6 +1084,7 @@ fn add_altair_block_to_base_chain() {
deposits: base_body.deposits.clone(), deposits: base_body.deposits.clone(),
voluntary_exits: base_body.voluntary_exits.clone(), voluntary_exits: base_body.voluntary_exits.clone(),
sync_aggregate: SyncAggregate::empty(), sync_aggregate: SyncAggregate::empty(),
_phantom: PhantomData,
}, },
}, },
signature: Signature::empty(), signature: Signature::empty(),

View File

@ -8,17 +8,20 @@ const VALIDATOR_COUNT: usize = 32;
type E = MainnetEthSpec; type E = MainnetEthSpec;
fn verify_execution_payload_chain<T: EthSpec>(chain: &[ExecutionPayload<T>]) { fn verify_execution_payload_chain<T: EthSpec>(chain: &[FullPayload<T>]) {
let mut prev_ep: Option<ExecutionPayload<T>> = None; let mut prev_ep: Option<FullPayload<T>> = None;
for ep in chain { for ep in chain {
assert!(*ep != ExecutionPayload::default()); assert!(*ep != FullPayload::default());
assert!(ep.block_hash != ExecutionBlockHash::zero()); assert!(ep.block_hash() != ExecutionBlockHash::zero());
// Check against previous `ExecutionPayload`. // Check against previous `ExecutionPayload`.
if let Some(prev_ep) = prev_ep { if let Some(prev_ep) = prev_ep {
assert_eq!(prev_ep.block_hash, ep.parent_hash); assert_eq!(prev_ep.block_hash(), ep.execution_payload.parent_hash);
assert_eq!(prev_ep.block_number + 1, ep.block_number); assert_eq!(
prev_ep.execution_payload.block_number + 1,
ep.execution_payload.block_number
);
} }
prev_ep = Some(ep.clone()); prev_ep = Some(ep.clone());
} }
@ -83,12 +86,12 @@ fn merge_with_terminal_block_hash_override() {
let execution_payload = block.message().body().execution_payload().unwrap().clone(); let execution_payload = block.message().body().execution_payload().unwrap().clone();
if i == 0 { if i == 0 {
assert_eq!(execution_payload.block_hash, genesis_pow_block_hash); assert_eq!(execution_payload.block_hash(), genesis_pow_block_hash);
} }
execution_payloads.push(execution_payload); execution_payloads.push(execution_payload);
} }
verify_execution_payload_chain(&execution_payloads); verify_execution_payload_chain(execution_payloads.as_slice());
} }
#[test] #[test]
@ -138,7 +141,7 @@ fn base_altair_merge_with_terminal_block_after_fork() {
assert_eq!(merge_head.slot(), merge_fork_slot); assert_eq!(merge_head.slot(), merge_fork_slot);
assert_eq!( assert_eq!(
*merge_head.message().body().execution_payload().unwrap(), *merge_head.message().body().execution_payload().unwrap(),
ExecutionPayload::default() FullPayload::default()
); );
/* /*
@ -154,7 +157,7 @@ fn base_altair_merge_with_terminal_block_after_fork() {
.body() .body()
.execution_payload() .execution_payload()
.unwrap(), .unwrap(),
ExecutionPayload::default() FullPayload::default()
); );
assert_eq!(one_after_merge_head.slot(), merge_fork_slot + 1); assert_eq!(one_after_merge_head.slot(), merge_fork_slot + 1);
@ -178,5 +181,5 @@ fn base_altair_merge_with_terminal_block_after_fork() {
execution_payloads.push(block.message().body().execution_payload().unwrap().clone()); execution_payloads.push(block.message().body().execution_payload().unwrap().clone());
} }
verify_execution_payload_chain(&execution_payloads); verify_execution_payload_chain(execution_payloads.as_slice());
} }

View File

@ -72,7 +72,7 @@ impl InvalidPayloadRig {
.body() .body()
.execution_payload() .execution_payload()
.unwrap() .unwrap()
.block_hash .block_hash()
} }
fn execution_status(&self, block_root: Hash256) -> ExecutionStatus { fn execution_status(&self, block_root: Hash256) -> ExecutionStatus {

View File

@ -1,18 +1,21 @@
use crate::engines::ForkChoiceState;
use async_trait::async_trait; use async_trait::async_trait;
use eth1::http::RpcError; use eth1::http::RpcError;
pub use json_structures::TransitionConfigurationV1;
use reqwest::StatusCode; use reqwest::StatusCode;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use slog::Logger;
pub const LATEST_TAG: &str = "latest"; pub use types::{
Address, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadHeader, Hash256,
use crate::engines::ForkChoiceState; Uint256,
pub use json_structures::TransitionConfigurationV1; };
pub use types::{Address, EthSpec, ExecutionBlockHash, ExecutionPayload, Hash256, Uint256};
pub mod auth; pub mod auth;
pub mod http; pub mod http;
pub mod json_structures; pub mod json_structures;
pub const LATEST_TAG: &str = "latest";
pub type PayloadId = [u8; 8]; pub type PayloadId = [u8; 8];
#[derive(Debug)] #[derive(Debug)]
@ -24,7 +27,10 @@ pub enum Error {
InvalidExecutePayloadResponse(&'static str), InvalidExecutePayloadResponse(&'static str),
JsonRpc(RpcError), JsonRpc(RpcError),
Json(serde_json::Error), Json(serde_json::Error),
ServerMessage { code: i64, message: String }, ServerMessage {
code: i64,
message: String,
},
Eip155Failure, Eip155Failure,
IsSyncing, IsSyncing,
ExecutionBlockNotFound(ExecutionBlockHash), ExecutionBlockNotFound(ExecutionBlockHash),
@ -32,6 +38,14 @@ pub enum Error {
ParentHashEqualsBlockHash(ExecutionBlockHash), ParentHashEqualsBlockHash(ExecutionBlockHash),
PayloadIdUnavailable, PayloadIdUnavailable,
TransitionConfigurationMismatch, TransitionConfigurationMismatch,
PayloadConversionLogicFlaw,
InvalidBuilderQuery,
MissingPayloadId {
parent_hash: ExecutionBlockHash,
timestamp: u64,
prev_randao: Hash256,
suggested_fee_recipient: Address,
},
} }
impl From<reqwest::Error> for Error { impl From<reqwest::Error> for Error {
@ -59,41 +73,17 @@ impl From<auth::Error> for Error {
} }
} }
/// A generic interface for an execution engine API. pub struct EngineApi;
pub struct BuilderApi;
#[async_trait] #[async_trait]
pub trait EngineApi { pub trait Builder {
async fn upcheck(&self) -> Result<(), Error>; async fn notify_forkchoice_updated(
async fn get_block_by_number<'a>(
&self,
block_by_number: BlockByNumberQuery<'a>,
) -> Result<Option<ExecutionBlock>, Error>;
async fn get_block_by_hash<'a>(
&self,
block_hash: ExecutionBlockHash,
) -> Result<Option<ExecutionBlock>, Error>;
async fn new_payload_v1<T: EthSpec>(
&self,
execution_payload: ExecutionPayload<T>,
) -> Result<PayloadStatusV1, Error>;
async fn get_payload_v1<T: EthSpec>(
&self,
payload_id: PayloadId,
) -> Result<ExecutionPayload<T>, Error>;
async fn forkchoice_updated_v1(
&self, &self,
forkchoice_state: ForkChoiceState, forkchoice_state: ForkChoiceState,
payload_attributes: Option<PayloadAttributes>, payload_attributes: Option<PayloadAttributes>,
log: &Logger,
) -> Result<ForkchoiceUpdatedResponse, Error>; ) -> Result<ForkchoiceUpdatedResponse, Error>;
async fn exchange_transition_configuration_v1(
&self,
transition_configuration: TransitionConfigurationV1,
) -> Result<TransitionConfigurationV1, Error>;
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -142,3 +132,17 @@ pub struct ForkchoiceUpdatedResponse {
pub payload_status: PayloadStatusV1, pub payload_status: PayloadStatusV1,
pub payload_id: Option<PayloadId>, pub payload_id: Option<PayloadId>,
} }
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ProposeBlindedBlockResponseStatus {
Valid,
Invalid,
Syncing,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ProposeBlindedBlockResponse {
pub status: ProposeBlindedBlockResponseStatus,
pub latest_valid_hash: Option<Hash256>,
pub validation_error: Option<String>,
}

View File

@ -3,14 +3,14 @@
use super::*; use super::*;
use crate::auth::Auth; use crate::auth::Auth;
use crate::json_structures::*; use crate::json_structures::*;
use async_trait::async_trait;
use eth1::http::EIP155_ERROR_STR; use eth1::http::EIP155_ERROR_STR;
use reqwest::header::CONTENT_TYPE; use reqwest::header::CONTENT_TYPE;
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde_json::json; use serde_json::json;
use std::marker::PhantomData;
use std::time::Duration; use std::time::Duration;
use types::EthSpec; use types::{BlindedPayload, EthSpec, ExecutionPayloadHeader, SignedBeaconBlock};
pub use reqwest::Client; pub use reqwest::Client;
@ -42,18 +42,26 @@ pub const ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1: &str =
pub const ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1_TIMEOUT: Duration = pub const ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1_TIMEOUT: Duration =
Duration::from_millis(500); Duration::from_millis(500);
pub struct HttpJsonRpc { pub const BUILDER_GET_PAYLOAD_HEADER_V1: &str = "builder_getPayloadHeaderV1";
pub const BUILDER_GET_PAYLOAD_HEADER_TIMEOUT: Duration = Duration::from_secs(2);
pub const BUILDER_PROPOSE_BLINDED_BLOCK_V1: &str = "builder_proposeBlindedBlockV1";
pub const BUILDER_PROPOSE_BLINDED_BLOCK_TIMEOUT: Duration = Duration::from_secs(2);
pub struct HttpJsonRpc<T = EngineApi> {
pub client: Client, pub client: Client,
pub url: SensitiveUrl, pub url: SensitiveUrl,
auth: Option<Auth>, auth: Option<Auth>,
_phantom: PhantomData<T>,
} }
impl HttpJsonRpc { impl<T> HttpJsonRpc<T> {
pub fn new(url: SensitiveUrl) -> Result<Self, Error> { pub fn new(url: SensitiveUrl) -> Result<Self, Error> {
Ok(Self { Ok(Self {
client: Client::builder().build()?, client: Client::builder().build()?,
url, url,
auth: None, auth: None,
_phantom: PhantomData,
}) })
} }
@ -62,15 +70,16 @@ impl HttpJsonRpc {
client: Client::builder().build()?, client: Client::builder().build()?,
url, url,
auth: Some(auth), auth: Some(auth),
_phantom: PhantomData,
}) })
} }
pub async fn rpc_request<T: DeserializeOwned>( pub async fn rpc_request<D: DeserializeOwned>(
&self, &self,
method: &str, method: &str,
params: serde_json::Value, params: serde_json::Value,
timeout: Duration, timeout: Duration,
) -> Result<T, Error> { ) -> Result<D, Error> {
let body = JsonRequestBody { let body = JsonRequestBody {
jsonrpc: JSONRPC_VERSION, jsonrpc: JSONRPC_VERSION,
method, method,
@ -108,9 +117,8 @@ impl HttpJsonRpc {
} }
} }
#[async_trait] impl HttpJsonRpc<EngineApi> {
impl EngineApi for HttpJsonRpc { pub async fn upcheck(&self) -> Result<(), Error> {
async fn upcheck(&self) -> Result<(), Error> {
let result: serde_json::Value = self let result: serde_json::Value = self
.rpc_request(ETH_SYNCING, json!([]), ETH_SYNCING_TIMEOUT) .rpc_request(ETH_SYNCING, json!([]), ETH_SYNCING_TIMEOUT)
.await?; .await?;
@ -127,7 +135,7 @@ impl EngineApi for HttpJsonRpc {
} }
} }
async fn get_block_by_number<'a>( pub async fn get_block_by_number<'a>(
&self, &self,
query: BlockByNumberQuery<'a>, query: BlockByNumberQuery<'a>,
) -> Result<Option<ExecutionBlock>, Error> { ) -> Result<Option<ExecutionBlock>, Error> {
@ -141,7 +149,7 @@ impl EngineApi for HttpJsonRpc {
.await .await
} }
async fn get_block_by_hash<'a>( pub async fn get_block_by_hash<'a>(
&self, &self,
block_hash: ExecutionBlockHash, block_hash: ExecutionBlockHash,
) -> Result<Option<ExecutionBlock>, Error> { ) -> Result<Option<ExecutionBlock>, Error> {
@ -151,7 +159,7 @@ impl EngineApi for HttpJsonRpc {
.await .await
} }
async fn new_payload_v1<T: EthSpec>( pub async fn new_payload_v1<T: EthSpec>(
&self, &self,
execution_payload: ExecutionPayload<T>, execution_payload: ExecutionPayload<T>,
) -> Result<PayloadStatusV1, Error> { ) -> Result<PayloadStatusV1, Error> {
@ -164,7 +172,7 @@ impl EngineApi for HttpJsonRpc {
Ok(response.into()) Ok(response.into())
} }
async fn get_payload_v1<T: EthSpec>( pub async fn get_payload_v1<T: EthSpec>(
&self, &self,
payload_id: PayloadId, payload_id: PayloadId,
) -> Result<ExecutionPayload<T>, Error> { ) -> Result<ExecutionPayload<T>, Error> {
@ -177,7 +185,7 @@ impl EngineApi for HttpJsonRpc {
Ok(response.into()) Ok(response.into())
} }
async fn forkchoice_updated_v1( pub async fn forkchoice_updated_v1(
&self, &self,
forkchoice_state: ForkChoiceState, forkchoice_state: ForkChoiceState,
payload_attributes: Option<PayloadAttributes>, payload_attributes: Option<PayloadAttributes>,
@ -198,7 +206,7 @@ impl EngineApi for HttpJsonRpc {
Ok(response.into()) Ok(response.into())
} }
async fn exchange_transition_configuration_v1( pub async fn exchange_transition_configuration_v1(
&self, &self,
transition_configuration: TransitionConfigurationV1, transition_configuration: TransitionConfigurationV1,
) -> Result<TransitionConfigurationV1, Error> { ) -> Result<TransitionConfigurationV1, Error> {
@ -216,6 +224,62 @@ impl EngineApi for HttpJsonRpc {
} }
} }
impl HttpJsonRpc<BuilderApi> {
pub async fn get_payload_header_v1<T: EthSpec>(
&self,
payload_id: PayloadId,
) -> Result<ExecutionPayloadHeader<T>, Error> {
let params = json!([JsonPayloadIdRequest::from(payload_id)]);
let response: JsonExecutionPayloadHeaderV1<T> = self
.rpc_request(
BUILDER_GET_PAYLOAD_HEADER_V1,
params,
BUILDER_GET_PAYLOAD_HEADER_TIMEOUT,
)
.await?;
Ok(response.into())
}
pub async fn forkchoice_updated_v1(
&self,
forkchoice_state: ForkChoiceState,
payload_attributes: Option<PayloadAttributes>,
) -> Result<ForkchoiceUpdatedResponse, Error> {
let params = json!([
JsonForkChoiceStateV1::from(forkchoice_state),
payload_attributes.map(JsonPayloadAttributesV1::from)
]);
let response: JsonForkchoiceUpdatedV1Response = self
.rpc_request(
ENGINE_FORKCHOICE_UPDATED_V1,
params,
ENGINE_FORKCHOICE_UPDATED_TIMEOUT,
)
.await?;
Ok(response.into())
}
pub async fn propose_blinded_block_v1<T: EthSpec>(
&self,
block: SignedBeaconBlock<T, BlindedPayload<T>>,
) -> Result<ExecutionPayload<T>, Error> {
let params = json!([block]);
let response: JsonExecutionPayloadV1<T> = self
.rpc_request(
BUILDER_PROPOSE_BLINDED_BLOCK_V1,
params,
BUILDER_PROPOSE_BLINDED_BLOCK_TIMEOUT,
)
.await?;
Ok(response.into())
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::auth::JwtKey; use super::auth::JwtKey;
@ -224,7 +288,7 @@ mod test {
use std::future::Future; use std::future::Future;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use types::{MainnetEthSpec, Transaction, Unsigned, VariableList}; use types::{MainnetEthSpec, Transactions, Unsigned, VariableList};
struct Tester { struct Tester {
server: MockServer<MainnetEthSpec>, server: MockServer<MainnetEthSpec>,
@ -326,10 +390,7 @@ mod test {
const LOGS_BLOOM_01: &str = "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"; const LOGS_BLOOM_01: &str = "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101";
fn encode_transactions<E: EthSpec>( fn encode_transactions<E: EthSpec>(
transactions: VariableList< transactions: Transactions<E>,
Transaction<E::MaxBytesPerTransaction>,
E::MaxTransactionsPerPayload,
>,
) -> Result<serde_json::Value, serde_json::Error> { ) -> Result<serde_json::Value, serde_json::Error> {
let ep: JsonExecutionPayloadV1<E> = JsonExecutionPayloadV1 { let ep: JsonExecutionPayloadV1<E> = JsonExecutionPayloadV1 {
transactions, transactions,
@ -341,10 +402,7 @@ mod test {
fn decode_transactions<E: EthSpec>( fn decode_transactions<E: EthSpec>(
transactions: serde_json::Value, transactions: serde_json::Value,
) -> Result< ) -> Result<Transactions<E>, serde_json::Error> {
VariableList<Transaction<E::MaxBytesPerTransaction>, E::MaxTransactionsPerPayload>,
serde_json::Error,
> {
let mut json = json!({ let mut json = json!({
"parentHash": HASH_00, "parentHash": HASH_00,
"feeRecipient": ADDRESS_01, "feeRecipient": ADDRESS_01,
@ -370,7 +428,7 @@ mod test {
fn assert_transactions_serde<E: EthSpec>( fn assert_transactions_serde<E: EthSpec>(
name: &str, name: &str,
as_obj: VariableList<Transaction<E::MaxBytesPerTransaction>, E::MaxTransactionsPerPayload>, as_obj: Transactions<E>,
as_json: serde_json::Value, as_json: serde_json::Value,
) { ) {
assert_eq!( assert_eq!(
@ -388,9 +446,7 @@ mod test {
} }
/// Example: if `spec == &[1, 1]`, then two one-byte transactions will be created. /// Example: if `spec == &[1, 1]`, then two one-byte transactions will be created.
fn generate_transactions<E: EthSpec>( fn generate_transactions<E: EthSpec>(spec: &[usize]) -> Transactions<E> {
spec: &[usize],
) -> VariableList<Transaction<E::MaxBytesPerTransaction>, E::MaxTransactionsPerPayload> {
let mut txs = VariableList::default(); let mut txs = VariableList::default();
for &num_bytes in spec { for &num_bytes in spec {
@ -860,7 +916,7 @@ mod test {
extra_data: vec![].into(), extra_data: vec![].into(),
base_fee_per_gas: Uint256::from(7), base_fee_per_gas: Uint256::from(7),
block_hash: ExecutionBlockHash::from_str("0x6359b8381a370e2f54072a5784ddd78b6ed024991558c511d4452eb4f6ac898c").unwrap(), block_hash: ExecutionBlockHash::from_str("0x6359b8381a370e2f54072a5784ddd78b6ed024991558c511d4452eb4f6ac898c").unwrap(),
transactions: vec![].into(), transactions: vec![].into(),
}; };
assert_eq!(payload, expected); assert_eq!(payload, expected);

View File

@ -1,6 +1,9 @@
use super::*; use super::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use types::{EthSpec, ExecutionBlockHash, FixedVector, Transaction, Unsigned, VariableList}; use types::{
EthSpec, ExecutionBlockHash, ExecutionPayloadHeader, FixedVector, Transaction, Unsigned,
VariableList,
};
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -55,6 +58,70 @@ pub struct JsonPayloadIdResponse {
pub payload_id: PayloadId, pub payload_id: PayloadId,
} }
#[derive(Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(bound = "T: EthSpec", rename_all = "camelCase")]
pub struct JsonExecutionPayloadHeaderV1<T: EthSpec> {
pub parent_hash: ExecutionBlockHash,
pub fee_recipient: Address,
pub state_root: Hash256,
pub receipts_root: Hash256,
#[serde(with = "serde_logs_bloom")]
pub logs_bloom: FixedVector<u8, T::BytesPerLogsBloom>,
pub prev_randao: Hash256,
#[serde(with = "eth2_serde_utils::u64_hex_be")]
pub block_number: u64,
#[serde(with = "eth2_serde_utils::u64_hex_be")]
pub gas_limit: u64,
#[serde(with = "eth2_serde_utils::u64_hex_be")]
pub gas_used: u64,
#[serde(with = "eth2_serde_utils::u64_hex_be")]
pub timestamp: u64,
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
pub base_fee_per_gas: Uint256,
pub block_hash: ExecutionBlockHash,
pub transactions_root: Hash256,
}
impl<T: EthSpec> From<JsonExecutionPayloadHeaderV1<T>> for ExecutionPayloadHeader<T> {
fn from(e: JsonExecutionPayloadHeaderV1<T>) -> Self {
// Use this verbose deconstruction pattern to ensure no field is left unused.
let JsonExecutionPayloadHeaderV1 {
parent_hash,
fee_recipient,
state_root,
receipts_root,
logs_bloom,
prev_randao,
block_number,
gas_limit,
gas_used,
timestamp,
extra_data,
base_fee_per_gas,
block_hash,
transactions_root,
} = e;
Self {
parent_hash,
fee_recipient,
state_root,
receipts_root,
logs_bloom,
prev_randao,
block_number,
gas_limit,
gas_used,
timestamp,
extra_data,
base_fee_per_gas,
block_hash,
transactions_root,
}
}
}
#[derive(Debug, PartialEq, Default, Serialize, Deserialize)] #[derive(Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(bound = "T: EthSpec", rename_all = "camelCase")] #[serde(bound = "T: EthSpec", rename_all = "camelCase")]
pub struct JsonExecutionPayloadV1<T: EthSpec> { pub struct JsonExecutionPayloadV1<T: EthSpec> {
@ -77,7 +144,7 @@ pub struct JsonExecutionPayloadV1<T: EthSpec> {
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>, pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
pub base_fee_per_gas: Uint256, pub base_fee_per_gas: Uint256,
pub block_hash: ExecutionBlockHash, pub block_hash: ExecutionBlockHash,
#[serde(with = "serde_transactions")] #[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: pub transactions:
VariableList<Transaction<T::MaxBytesPerTransaction>, T::MaxTransactionsPerPayload>, VariableList<Transaction<T::MaxBytesPerTransaction>, T::MaxTransactionsPerPayload>,
} }
@ -363,6 +430,59 @@ impl From<ForkchoiceUpdatedResponse> for JsonForkchoiceUpdatedV1Response {
} }
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum JsonProposeBlindedBlockResponseStatus {
Valid,
Invalid,
Syncing,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(bound = "E: EthSpec")]
pub struct JsonProposeBlindedBlockResponse<E: EthSpec> {
pub result: ExecutionPayload<E>,
pub error: Option<String>,
}
impl<E: EthSpec> From<JsonProposeBlindedBlockResponse<E>> for ExecutionPayload<E> {
fn from(j: JsonProposeBlindedBlockResponse<E>) -> Self {
let JsonProposeBlindedBlockResponse { result, error: _ } = j;
result
}
}
impl From<JsonProposeBlindedBlockResponseStatus> for ProposeBlindedBlockResponseStatus {
fn from(j: JsonProposeBlindedBlockResponseStatus) -> Self {
match j {
JsonProposeBlindedBlockResponseStatus::Valid => {
ProposeBlindedBlockResponseStatus::Valid
}
JsonProposeBlindedBlockResponseStatus::Invalid => {
ProposeBlindedBlockResponseStatus::Invalid
}
JsonProposeBlindedBlockResponseStatus::Syncing => {
ProposeBlindedBlockResponseStatus::Syncing
}
}
}
}
impl From<ProposeBlindedBlockResponseStatus> for JsonProposeBlindedBlockResponseStatus {
fn from(f: ProposeBlindedBlockResponseStatus) -> Self {
match f {
ProposeBlindedBlockResponseStatus::Valid => {
JsonProposeBlindedBlockResponseStatus::Valid
}
ProposeBlindedBlockResponseStatus::Invalid => {
JsonProposeBlindedBlockResponseStatus::Invalid
}
ProposeBlindedBlockResponseStatus::Syncing => {
JsonProposeBlindedBlockResponseStatus::Syncing
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TransitionConfigurationV1 { pub struct TransitionConfigurationV1 {
@ -400,75 +520,3 @@ pub mod serde_logs_bloom {
.map_err(|e| serde::de::Error::custom(format!("invalid logs bloom: {:?}", e))) .map_err(|e| serde::de::Error::custom(format!("invalid logs bloom: {:?}", e)))
} }
} }
/// Serializes the `transactions` field of an `ExecutionPayload`.
pub mod serde_transactions {
use super::*;
use eth2_serde_utils::hex;
use serde::ser::SerializeSeq;
use serde::{de, Deserializer, Serializer};
use std::marker::PhantomData;
type Value<M, N> = VariableList<Transaction<M>, N>;
#[derive(Default)]
pub struct ListOfBytesListVisitor<M, N> {
_phantom_m: PhantomData<M>,
_phantom_n: PhantomData<N>,
}
impl<'a, M: Unsigned, N: Unsigned> serde::de::Visitor<'a> for ListOfBytesListVisitor<M, N> {
type Value = Value<M, N>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a list of 0x-prefixed byte lists")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'a>,
{
let mut outer = VariableList::default();
while let Some(val) = seq.next_element::<String>()? {
let inner_vec = hex::decode(&val).map_err(de::Error::custom)?;
let transaction = VariableList::new(inner_vec).map_err(|e| {
serde::de::Error::custom(format!("transaction too large: {:?}", e))
})?;
outer.push(transaction).map_err(|e| {
serde::de::Error::custom(format!("too many transactions: {:?}", e))
})?;
}
Ok(outer)
}
}
pub fn serialize<S, M: Unsigned, N: Unsigned>(
value: &Value<M, N>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(value.len()))?;
for transaction in value {
// It's important to match on the inner values of the transaction. Serializing the
// entire `Transaction` will result in appending the SSZ union prefix byte. The
// execution node does not want that.
let hex = hex::encode(&transaction[..]);
seq.serialize_element(&hex)?;
}
seq.end()
}
pub fn deserialize<'de, D, M: Unsigned, N: Unsigned>(
deserializer: D,
) -> Result<Value<M, N>, D::Error>
where
D: Deserializer<'de>,
{
let visitor: ListOfBytesListVisitor<M, N> = <_>::default();
deserializer.deserialize_any(visitor)
}
}

View File

@ -1,8 +1,11 @@
//! Provides generic behaviour for multiple execution engines, specifically fallback behaviour. //! Provides generic behaviour for multiple execution engines, specifically fallback behaviour.
use crate::engine_api::{ use crate::engine_api::{
EngineApi, Error as EngineApiError, ForkchoiceUpdatedResponse, PayloadAttributes, PayloadId, Builder, EngineApi, Error as EngineApiError, ForkchoiceUpdatedResponse, PayloadAttributes,
PayloadId,
}; };
use crate::{BuilderApi, HttpJsonRpc};
use async_trait::async_trait;
use futures::future::join_all; use futures::future::join_all;
use lru::LruCache; use lru::LruCache;
use slog::{crit, debug, info, warn, Logger}; use slog::{crit, debug, info, warn, Logger};
@ -58,14 +61,14 @@ struct PayloadIdCacheKey {
/// An execution engine. /// An execution engine.
pub struct Engine<T> { pub struct Engine<T> {
pub id: String, pub id: String,
pub api: T, pub api: HttpJsonRpc<T>,
payload_id_cache: Mutex<LruCache<PayloadIdCacheKey, PayloadId>>, payload_id_cache: Mutex<LruCache<PayloadIdCacheKey, PayloadId>>,
state: RwLock<EngineState>, state: RwLock<EngineState>,
} }
impl<T> Engine<T> { impl<T> Engine<T> {
/// Creates a new, offline engine. /// Creates a new, offline engine.
pub fn new(id: String, api: T) -> Self { pub fn new(id: String, api: HttpJsonRpc<T>) -> Self {
Self { Self {
id, id,
api, api,
@ -94,8 +97,9 @@ impl<T> Engine<T> {
} }
} }
impl<T: EngineApi> Engine<T> { #[async_trait]
pub async fn notify_forkchoice_updated( impl Builder for Engine<EngineApi> {
async fn notify_forkchoice_updated(
&self, &self,
forkchoice_state: ForkChoiceState, forkchoice_state: ForkChoiceState,
payload_attributes: Option<PayloadAttributes>, payload_attributes: Option<PayloadAttributes>,
@ -124,14 +128,47 @@ impl<T: EngineApi> Engine<T> {
} }
} }
#[async_trait]
impl Builder for Engine<BuilderApi> {
async fn notify_forkchoice_updated(
&self,
forkchoice_state: ForkChoiceState,
pa: Option<PayloadAttributes>,
log: &Logger,
) -> Result<ForkchoiceUpdatedResponse, EngineApiError> {
let payload_attributes = pa.ok_or(EngineApiError::InvalidBuilderQuery)?;
let response = self
.api
.forkchoice_updated_v1(forkchoice_state, Some(payload_attributes))
.await?;
if let Some(payload_id) = response.payload_id {
let key = PayloadIdCacheKey::new(&forkchoice_state, &payload_attributes);
self.payload_id_cache.lock().await.put(key, payload_id);
} else {
warn!(
log,
"Builder should have returned a payload_id for attributes {:?}", payload_attributes
);
}
Ok(response)
}
}
/// Holds multiple execution engines and provides functionality for managing them in a fallback /// Holds multiple execution engines and provides functionality for managing them in a fallback
/// manner. /// manner.
pub struct Engines<T> { pub struct Engines {
pub engines: Vec<Engine<T>>, pub engines: Vec<Engine<EngineApi>>,
pub latest_forkchoice_state: RwLock<Option<ForkChoiceState>>, pub latest_forkchoice_state: RwLock<Option<ForkChoiceState>>,
pub log: Logger, pub log: Logger,
} }
pub struct Builders {
pub builders: Vec<Engine<BuilderApi>>,
pub log: Logger,
}
#[derive(Debug)] #[derive(Debug)]
pub enum EngineError { pub enum EngineError {
Offline { id: String }, Offline { id: String },
@ -139,7 +176,7 @@ pub enum EngineError {
Auth { id: String }, Auth { id: String },
} }
impl<T: EngineApi> Engines<T> { impl Engines {
async fn get_latest_forkchoice_state(&self) -> Option<ForkChoiceState> { async fn get_latest_forkchoice_state(&self) -> Option<ForkChoiceState> {
*self.latest_forkchoice_state.read().await *self.latest_forkchoice_state.read().await
} }
@ -148,7 +185,7 @@ impl<T: EngineApi> Engines<T> {
*self.latest_forkchoice_state.write().await = Some(state); *self.latest_forkchoice_state.write().await = Some(state);
} }
async fn send_latest_forkchoice_state(&self, engine: &Engine<T>) { async fn send_latest_forkchoice_state(&self, engine: &Engine<EngineApi>) {
let latest_forkchoice_state = self.get_latest_forkchoice_state().await; let latest_forkchoice_state = self.get_latest_forkchoice_state().await;
if let Some(forkchoice_state) = latest_forkchoice_state { if let Some(forkchoice_state) = latest_forkchoice_state {
@ -286,7 +323,7 @@ impl<T: EngineApi> Engines<T> {
/// it runs, it will try to upcheck all offline nodes and then run the function again. /// it runs, it will try to upcheck all offline nodes and then run the function again.
pub async fn first_success<'a, F, G, H>(&'a self, func: F) -> Result<H, Vec<EngineError>> pub async fn first_success<'a, F, G, H>(&'a self, func: F) -> Result<H, Vec<EngineError>>
where where
F: Fn(&'a Engine<T>) -> G + Copy, F: Fn(&'a Engine<EngineApi>) -> G + Copy,
G: Future<Output = Result<H, EngineApiError>>, G: Future<Output = Result<H, EngineApiError>>,
{ {
match self.first_success_without_retry(func).await { match self.first_success_without_retry(func).await {
@ -308,12 +345,12 @@ impl<T: EngineApi> Engines<T> {
/// Run `func` on all engines, in the order in which they are defined, returning the first /// Run `func` on all engines, in the order in which they are defined, returning the first
/// successful result that is found. /// successful result that is found.
async fn first_success_without_retry<'a, F, G, H>( pub async fn first_success_without_retry<'a, F, G, H>(
&'a self, &'a self,
func: F, func: F,
) -> Result<H, Vec<EngineError>> ) -> Result<H, Vec<EngineError>>
where where
F: Fn(&'a Engine<T>) -> G, F: Fn(&'a Engine<EngineApi>) -> G,
G: Future<Output = Result<H, EngineApiError>>, G: Future<Output = Result<H, EngineApiError>>,
{ {
let mut errors = vec![]; let mut errors = vec![];
@ -364,7 +401,7 @@ impl<T: EngineApi> Engines<T> {
/// it runs, it will try to upcheck all offline nodes and then run the function again. /// it runs, it will try to upcheck all offline nodes and then run the function again.
pub async fn broadcast<'a, F, G, H>(&'a self, func: F) -> Vec<Result<H, EngineError>> pub async fn broadcast<'a, F, G, H>(&'a self, func: F) -> Vec<Result<H, EngineError>>
where where
F: Fn(&'a Engine<T>) -> G + Copy, F: Fn(&'a Engine<EngineApi>) -> G + Copy,
G: Future<Output = Result<H, EngineApiError>>, G: Future<Output = Result<H, EngineApiError>>,
{ {
let first_results = self.broadcast_without_retry(func).await; let first_results = self.broadcast_without_retry(func).await;
@ -392,7 +429,7 @@ impl<T: EngineApi> Engines<T> {
func: F, func: F,
) -> Vec<Result<H, EngineError>> ) -> Vec<Result<H, EngineError>>
where where
F: Fn(&'a Engine<T>) -> G, F: Fn(&'a Engine<EngineApi>) -> G,
G: Future<Output = Result<H, EngineApiError>>, G: Future<Output = Result<H, EngineApiError>>,
{ {
let func = &func; let func = &func;
@ -426,6 +463,66 @@ impl<T: EngineApi> Engines<T> {
} }
} }
impl Builders {
pub async fn first_success_without_retry<'a, F, G, H>(
&'a self,
func: F,
) -> Result<H, Vec<EngineError>>
where
F: Fn(&'a Engine<BuilderApi>) -> G,
G: Future<Output = Result<H, EngineApiError>>,
{
let mut errors = vec![];
for builder in &self.builders {
match func(builder).await {
Ok(result) => return Ok(result),
Err(error) => {
debug!(
self.log,
"Builder call failed";
"error" => ?error,
"id" => &builder.id
);
errors.push(EngineError::Api {
id: builder.id.clone(),
error,
})
}
}
}
Err(errors)
}
pub async fn broadcast_without_retry<'a, F, G, H>(
&'a self,
func: F,
) -> Vec<Result<H, EngineError>>
where
F: Fn(&'a Engine<BuilderApi>) -> G,
G: Future<Output = Result<H, EngineApiError>>,
{
let func = &func;
let futures = self.builders.iter().map(|engine| async move {
func(engine).await.map_err(|error| {
debug!(
self.log,
"Builder call failed";
"error" => ?error,
"id" => &engine.id
);
EngineError::Api {
id: engine.id.clone(),
error,
}
})
});
join_all(futures).await
}
}
impl PayloadIdCacheKey { impl PayloadIdCacheKey {
fn new(state: &ForkChoiceState, attributes: &PayloadAttributes) -> Self { fn new(state: &ForkChoiceState, attributes: &PayloadAttributes) -> Self {
Self { Self {

View File

@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize};
use slog::{crit, debug, error, info, trace, Logger}; use slog::{crit, debug, error, info, trace, Logger};
use slot_clock::SlotClock; use slot_clock::SlotClock;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryInto;
use std::future::Future; use std::future::Future;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
@ -24,11 +25,15 @@ use tokio::{
sync::{Mutex, MutexGuard, RwLock}, sync::{Mutex, MutexGuard, RwLock},
time::{sleep, sleep_until, Instant}, time::{sleep, sleep_until, Instant},
}; };
use types::{ChainSpec, Epoch, ExecutionBlockHash, ProposerPreparationData, Slot}; use types::{
BlindedPayload, BlockType, ChainSpec, Epoch, ExecPayload, ExecutionBlockHash,
pub use engine_api::{ ProposerPreparationData, SignedBeaconBlock, Slot,
http::HttpJsonRpc, json_structures, PayloadAttributes, PayloadStatusV1Status,
}; };
use crate::engine_api::Builder;
use crate::engines::Builders;
pub use engine_api::*;
pub use engine_api::{http, http::HttpJsonRpc};
pub use payload_status::PayloadStatus; pub use payload_status::PayloadStatus;
mod engine_api; mod engine_api;
@ -59,6 +64,7 @@ const CONFIG_POLL_INTERVAL: Duration = Duration::from_secs(60);
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
NoEngines, NoEngines,
NoPayloadBuilder,
ApiError(ApiError), ApiError(ApiError),
EngineErrors(Vec<EngineError>), EngineErrors(Vec<EngineError>),
NotSynced, NotSynced,
@ -94,7 +100,8 @@ pub struct Proposer {
} }
struct Inner { struct Inner {
engines: Engines<HttpJsonRpc>, engines: Engines,
builders: Builders,
execution_engine_forkchoice_lock: Mutex<()>, execution_engine_forkchoice_lock: Mutex<()>,
suggested_fee_recipient: Option<Address>, suggested_fee_recipient: Option<Address>,
proposer_preparation_data: Mutex<HashMap<u64, ProposerPreparationDataEntry>>, proposer_preparation_data: Mutex<HashMap<u64, ProposerPreparationDataEntry>>,
@ -108,6 +115,8 @@ struct Inner {
pub struct Config { pub struct Config {
/// Endpoint urls for EL nodes that are running the engine api. /// Endpoint urls for EL nodes that are running the engine api.
pub execution_endpoints: Vec<SensitiveUrl>, pub execution_endpoints: Vec<SensitiveUrl>,
/// Endpoint urls for services providing the builder api.
pub builder_endpoints: Vec<SensitiveUrl>,
/// JWT secrets for the above endpoints running the engine api. /// JWT secrets for the above endpoints running the engine api.
pub secret_files: Vec<PathBuf>, pub secret_files: Vec<PathBuf>,
/// The default fee recipient to use on the beacon node if none if provided from /// The default fee recipient to use on the beacon node if none if provided from
@ -148,6 +157,7 @@ impl ExecutionLayer {
pub fn from_config(config: Config, executor: TaskExecutor, log: Logger) -> Result<Self, Error> { pub fn from_config(config: Config, executor: TaskExecutor, log: Logger) -> Result<Self, Error> {
let Config { let Config {
execution_endpoints: urls, execution_endpoints: urls,
builder_endpoints: builder_urls,
mut secret_files, mut secret_files,
suggested_fee_recipient, suggested_fee_recipient,
jwt_id, jwt_id,
@ -203,15 +213,24 @@ impl ExecutionLayer {
.collect::<Result<_, _>>() .collect::<Result<_, _>>()
.map_err(Error::InvalidJWTSecret)?; .map_err(Error::InvalidJWTSecret)?;
let engines: Vec<Engine<_>> = urls let engines: Vec<Engine<EngineApi>> = urls
.into_iter() .into_iter()
.zip(secrets.into_iter()) .zip(secrets.into_iter())
.map(|(url, (secret, path))| { .map(|(url, (secret, path))| {
let id = url.to_string(); let id = url.to_string();
let auth = Auth::new(secret, jwt_id.clone(), jwt_version.clone()); let auth = Auth::new(secret, jwt_id.clone(), jwt_version.clone());
debug!(log, "Loaded execution endpoint"; "endpoint" => %id, "jwt_path" => ?path); debug!(log, "Loaded execution endpoint"; "endpoint" => %id, "jwt_path" => ?path);
let api = HttpJsonRpc::new_with_auth(url, auth)?; let api = HttpJsonRpc::<EngineApi>::new_with_auth(url, auth)?;
Ok(Engine::new(id, api)) Ok(Engine::<EngineApi>::new(id, api))
})
.collect::<Result<_, ApiError>>()?;
let builders: Vec<Engine<BuilderApi>> = builder_urls
.into_iter()
.map(|url| {
let id = url.to_string();
let api = HttpJsonRpc::<BuilderApi>::new(url)?;
Ok(Engine::<BuilderApi>::new(id, api))
}) })
.collect::<Result<_, ApiError>>()?; .collect::<Result<_, ApiError>>()?;
@ -221,6 +240,10 @@ impl ExecutionLayer {
latest_forkchoice_state: <_>::default(), latest_forkchoice_state: <_>::default(),
log: log.clone(), log: log.clone(),
}, },
builders: Builders {
builders,
log: log.clone(),
},
execution_engine_forkchoice_lock: <_>::default(), execution_engine_forkchoice_lock: <_>::default(),
suggested_fee_recipient, suggested_fee_recipient,
proposer_preparation_data: Mutex::new(HashMap::new()), proposer_preparation_data: Mutex::new(HashMap::new()),
@ -237,10 +260,14 @@ impl ExecutionLayer {
} }
impl ExecutionLayer { impl ExecutionLayer {
fn engines(&self) -> &Engines<HttpJsonRpc> { fn engines(&self) -> &Engines {
&self.inner.engines &self.inner.engines
} }
fn builders(&self) -> &Builders {
&self.inner.builders
}
fn executor(&self) -> &TaskExecutor { fn executor(&self) -> &TaskExecutor {
&self.inner.executor &self.inner.executor
} }
@ -542,14 +569,14 @@ impl ExecutionLayer {
/// ///
/// The result will be returned from the first node that returns successfully. No more nodes /// The result will be returned from the first node that returns successfully. No more nodes
/// will be contacted. /// will be contacted.
pub async fn get_payload<T: EthSpec>( pub async fn get_payload<T: EthSpec, Payload: ExecPayload<T>>(
&self, &self,
parent_hash: ExecutionBlockHash, parent_hash: ExecutionBlockHash,
timestamp: u64, timestamp: u64,
prev_randao: Hash256, prev_randao: Hash256,
finalized_block_hash: ExecutionBlockHash, finalized_block_hash: ExecutionBlockHash,
proposer_index: u64, proposer_index: u64,
) -> Result<ExecutionPayload<T>, Error> { ) -> Result<Payload, Error> {
let _timer = metrics::start_timer_vec( let _timer = metrics::start_timer_vec(
&metrics::EXECUTION_LAYER_REQUEST_TIMES, &metrics::EXECUTION_LAYER_REQUEST_TIMES,
&[metrics::GET_PAYLOAD], &[metrics::GET_PAYLOAD],
@ -557,72 +584,121 @@ impl ExecutionLayer {
let suggested_fee_recipient = self.get_suggested_fee_recipient(proposer_index).await; let suggested_fee_recipient = self.get_suggested_fee_recipient(proposer_index).await;
debug!( match Payload::block_type() {
self.log(), BlockType::Blinded => {
"Issuing engine_getPayload"; debug!(
"suggested_fee_recipient" => ?suggested_fee_recipient, self.log(),
"prev_randao" => ?prev_randao, "Issuing builder_getPayloadHeader";
"timestamp" => timestamp, "suggested_fee_recipient" => ?suggested_fee_recipient,
"parent_hash" => ?parent_hash, "prev_randao" => ?prev_randao,
); "timestamp" => timestamp,
self.engines() "parent_hash" => ?parent_hash,
.first_success(|engine| async move { );
let payload_id = if let Some(id) = engine self.builders()
.get_payload_id(parent_hash, timestamp, prev_randao, suggested_fee_recipient) .first_success_without_retry(|engine| async move {
let payload_id = engine
.get_payload_id(
parent_hash,
timestamp,
prev_randao,
suggested_fee_recipient,
)
.await
.ok_or(ApiError::MissingPayloadId {
parent_hash,
timestamp,
prev_randao,
suggested_fee_recipient,
})?;
engine
.api
.get_payload_header_v1::<T>(payload_id)
.await?
.try_into()
.map_err(|_| ApiError::PayloadConversionLogicFlaw)
})
.await .await
{ .map_err(Error::EngineErrors)
// The payload id has been cached for this engine. }
metrics::inc_counter_vec( BlockType::Full => {
&metrics::EXECUTION_LAYER_PRE_PREPARED_PAYLOAD_ID, debug!(
&[metrics::HIT], self.log(),
); "Issuing engine_getPayload";
id "suggested_fee_recipient" => ?suggested_fee_recipient,
} else { "prev_randao" => ?prev_randao,
// The payload id has *not* been cached for this engine. Trigger an artificial "timestamp" => timestamp,
// fork choice update to retrieve a payload ID. "parent_hash" => ?parent_hash,
// );
// TODO(merge): a better algorithm might try to favour a node that already had a self.engines()
// cached payload id, since a payload that has had more time to produce is .first_success(|engine| async move {
// likely to be more profitable. let payload_id = if let Some(id) = engine
metrics::inc_counter_vec( .get_payload_id(
&metrics::EXECUTION_LAYER_PRE_PREPARED_PAYLOAD_ID, parent_hash,
&[metrics::MISS], timestamp,
); prev_randao,
let fork_choice_state = ForkChoiceState { suggested_fee_recipient,
head_block_hash: parent_hash, )
safe_block_hash: parent_hash, .await
finalized_block_hash, {
}; // The payload id has been cached for this engine.
let payload_attributes = PayloadAttributes { metrics::inc_counter_vec(
timestamp, &metrics::EXECUTION_LAYER_PRE_PREPARED_PAYLOAD_ID,
prev_randao, &[metrics::HIT],
suggested_fee_recipient,
};
engine
.notify_forkchoice_updated(
fork_choice_state,
Some(payload_attributes),
self.log(),
)
.await
.map(|response| response.payload_id)?
.ok_or_else(|| {
error!(
self.log(),
"Exec engine unable to produce payload";
"msg" => "No payload ID, the engine is likely syncing. \
This has the potential to cause a missed block proposal.",
); );
id
} else {
// The payload id has *not* been cached for this engine. Trigger an artificial
// fork choice update to retrieve a payload ID.
//
// TODO(merge): a better algorithm might try to favour a node that already had a
// cached payload id, since a payload that has had more time to produce is
// likely to be more profitable.
metrics::inc_counter_vec(
&metrics::EXECUTION_LAYER_PRE_PREPARED_PAYLOAD_ID,
&[metrics::MISS],
);
let fork_choice_state = ForkChoiceState {
head_block_hash: parent_hash,
safe_block_hash: parent_hash,
finalized_block_hash,
};
let payload_attributes = PayloadAttributes {
timestamp,
prev_randao,
suggested_fee_recipient,
};
ApiError::PayloadIdUnavailable engine
})? .notify_forkchoice_updated(
}; fork_choice_state,
Some(payload_attributes),
self.log(),
)
.await
.map(|response| response.payload_id)?
.ok_or_else(|| {
error!(
self.log(),
"Exec engine unable to produce payload";
"msg" => "No payload ID, the engine is likely syncing. \
This has the potential to cause a missed block \
proposal.",
);
engine.api.get_payload_v1(payload_id).await ApiError::PayloadIdUnavailable
}) })?
.await };
.map_err(Error::EngineErrors)
engine
.api
.get_payload_v1::<T>(payload_id)
.await
.map(Into::into)
})
.await
.map_err(Error::EngineErrors)
}
}
} }
/// Maps to the `engine_newPayload` JSON-RPC call. /// Maps to the `engine_newPayload` JSON-RPC call.
@ -801,10 +877,23 @@ impl ExecutionLayer {
}) })
.await; .await;
// Only query builders with payload attributes populated.
let builder_broadcast_results = if payload_attributes.is_some() {
self.builders()
.broadcast_without_retry(|engine| async move {
engine
.notify_forkchoice_updated(forkchoice_state, payload_attributes, self.log())
.await
})
.await
} else {
vec![]
};
process_multiple_payload_statuses( process_multiple_payload_statuses(
head_block_hash, head_block_hash,
broadcast_results broadcast_results
.into_iter() .into_iter()
.chain(builder_broadcast_results.into_iter())
.map(|result| result.map(|response| response.payload_status)), .map(|result| result.map(|response| response.payload_status)),
self.log(), self.log(),
) )
@ -931,7 +1020,7 @@ impl ExecutionLayer {
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md /// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md
async fn get_pow_block_hash_at_total_difficulty( async fn get_pow_block_hash_at_total_difficulty(
&self, &self,
engine: &Engine<HttpJsonRpc>, engine: &Engine<EngineApi>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<Option<ExecutionBlockHash>, ApiError> { ) -> Result<Option<ExecutionBlockHash>, ApiError> {
let mut block = engine let mut block = engine
@ -1013,7 +1102,6 @@ impl ExecutionLayer {
)); ));
} }
} }
Ok(None) Ok(None)
}) })
.await; .await;
@ -1076,7 +1164,7 @@ impl ExecutionLayer {
/// https://github.com/ethereum/consensus-specs/issues/2636 /// https://github.com/ethereum/consensus-specs/issues/2636
async fn get_pow_block( async fn get_pow_block(
&self, &self,
engine: &Engine<HttpJsonRpc>, engine: &Engine<EngineApi>,
hash: ExecutionBlockHash, hash: ExecutionBlockHash,
) -> Result<Option<ExecutionBlock>, ApiError> { ) -> Result<Option<ExecutionBlock>, ApiError> {
if let Some(cached) = self.execution_blocks().await.get(&hash).copied() { if let Some(cached) = self.execution_blocks().await.get(&hash).copied() {
@ -1094,6 +1182,23 @@ impl ExecutionLayer {
Ok(None) Ok(None)
} }
} }
pub async fn propose_blinded_beacon_block<T: EthSpec>(
&self,
block: &SignedBeaconBlock<T, BlindedPayload<T>>,
) -> Result<ExecutionPayload<T>, Error> {
debug!(
self.log(),
"Issuing builder_proposeBlindedBlock";
"root" => ?block.canonical_root(),
);
self.builders()
.first_success_without_retry(|engine| async move {
engine.api.propose_blinded_block_v1(block.clone()).await
})
.await
.map_err(Error::EngineErrors)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -7,7 +7,7 @@ use sensitive_url::SensitiveUrl;
use std::sync::Arc; use std::sync::Arc;
use task_executor::TaskExecutor; use task_executor::TaskExecutor;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
use types::{Address, ChainSpec, Epoch, EthSpec, Hash256, Uint256}; use types::{Address, ChainSpec, Epoch, EthSpec, FullPayload, Hash256, Uint256};
pub struct ExecutionLayerRuntime { pub struct ExecutionLayerRuntime {
pub runtime: Option<Arc<tokio::runtime::Runtime>>, pub runtime: Option<Arc<tokio::runtime::Runtime>>,
@ -154,7 +154,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
let validator_index = 0; let validator_index = 0;
let payload = self let payload = self
.el .el
.get_payload::<T>( .get_payload::<T, FullPayload<T>>(
parent_hash, parent_hash,
timestamp, timestamp,
prev_randao, prev_randao,
@ -162,7 +162,8 @@ impl<T: EthSpec> MockExecutionLayer<T> {
validator_index, validator_index,
) )
.await .await
.unwrap(); .unwrap()
.execution_payload;
let block_hash = payload.block_hash; let block_hash = payload.block_hash;
assert_eq!(payload.parent_hash, parent_hash); assert_eq!(payload.parent_hash, parent_hash);
assert_eq!(payload.block_number, block_number); assert_eq!(payload.block_number, block_number);

View File

@ -27,6 +27,7 @@ slot_clock = { path = "../../common/slot_clock" }
eth2_ssz = "0.4.1" eth2_ssz = "0.4.1"
bs58 = "0.4.0" bs58 = "0.4.0"
futures = "0.3.8" futures = "0.3.8"
execution_layer = {path = "../execution_layer"}
parking_lot = "0.11.0" parking_lot = "0.11.0"
safe_arith = {path = "../../consensus/safe_arith"} safe_arith = {path = "../../consensus/safe_arith"}

View File

@ -45,9 +45,10 @@ use std::sync::Arc;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use tokio_stream::{wrappers::BroadcastStream, StreamExt}; use tokio_stream::{wrappers::BroadcastStream, StreamExt};
use types::{ use types::{
Attestation, AttesterSlashing, BeaconStateError, CommitteeCache, ConfigAndPreset, Epoch, Attestation, AttesterSlashing, BeaconBlockBodyMerge, BeaconBlockMerge, BeaconStateError,
EthSpec, ForkName, ProposerPreparationData, ProposerSlashing, RelativeEpoch, Signature, BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, FullPayload,
SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof, SignedVoluntaryExit, ProposerPreparationData, ProposerSlashing, RelativeEpoch, Signature, SignedAggregateAndProof,
SignedBeaconBlock, SignedBeaconBlockMerge, SignedContributionAndProof, SignedVoluntaryExit,
Slot, SyncCommitteeMessage, SyncContributionData, Slot, SyncCommitteeMessage, SyncContributionData,
}; };
use version::{ use version::{
@ -1022,6 +1023,116 @@ pub fn serve<T: BeaconChainTypes>(
}, },
); );
/*
* beacon/blocks
*/
// POST beacon/blocks
let post_beacon_blinded_blocks = eth1_v1
.and(warp::path("beacon"))
.and(warp::path("blinded_blocks"))
.and(warp::path::end())
.and(warp::body::json())
.and(chain_filter.clone())
.and(network_tx_filter.clone())
.and(log_filter.clone())
.and_then(
|block: SignedBeaconBlock<T::EthSpec, BlindedPayload<_>>,
chain: Arc<BeaconChain<T>>,
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
_log: Logger| {
blocking_json_task(move || {
if let Some(el) = chain.execution_layer.as_ref() {
//FIXME(sean): we may not always receive the payload in this response because it
// should be the relay's job to propogate the block. However, since this block is
// already signed and sent this might be ok (so long as the relay validates
// the block before revealing the payload).
//FIXME(sean) additionally, this endpoint should serve blocks prior to Bellatrix, and should
// be able to support the normal block proposal flow, because at some point full block endpoints
// will be deprecated from the beacon API. This will entail creating full blocks in
// `validator/blinded_blocks`, caching their payloads, and transforming them into blinded
// blocks. We will access the payload of those blocks here. This flow should happen if the
// execution layer has no payload builders or if we have not yet finalized post-merge transition.
let payload = el
.block_on(|el| el.propose_blinded_beacon_block(&block))
.map_err(|e| {
warp_utils::reject::custom_server_error(format!(
"proposal failed: {:?}",
e
))
})?;
let new_block = SignedBeaconBlock::Merge(SignedBeaconBlockMerge {
message: BeaconBlockMerge {
slot: block.message().slot(),
proposer_index: block.message().proposer_index(),
parent_root: block.message().parent_root(),
state_root: block.message().state_root(),
body: BeaconBlockBodyMerge {
randao_reveal: block.message().body().randao_reveal().clone(),
eth1_data: block.message().body().eth1_data().clone(),
graffiti: *block.message().body().graffiti(),
proposer_slashings: block
.message()
.body()
.proposer_slashings()
.clone(),
attester_slashings: block
.message()
.body()
.attester_slashings()
.clone(),
attestations: block.message().body().attestations().clone(),
deposits: block.message().body().deposits().clone(),
voluntary_exits: block
.message()
.body()
.voluntary_exits()
.clone(),
sync_aggregate: block
.message()
.body()
.sync_aggregate()
.unwrap()
.clone(),
execution_payload: payload.into(),
},
},
signature: block.signature().clone(),
});
// Send the block, regardless of whether or not it is valid. The API
// specification is very clear that this is the desired behaviour.
publish_pubsub_message(
&network_tx,
PubsubMessage::BeaconBlock(Box::new(new_block.clone())),
)?;
match chain.process_block(new_block) {
Ok(_) => {
// Update the head since it's likely this block will become the new
// head.
chain
.fork_choice()
.map_err(warp_utils::reject::beacon_chain_error)?;
Ok(())
}
Err(e) => {
let msg = format!("{:?}", e);
Err(warp_utils::reject::broadcast_without_import(msg))
}
}
} else {
Err(warp_utils::reject::custom_server_error(
"no execution layer found".to_string(),
))
}
})
},
);
let block_id_or_err = warp::path::param::<BlockId>().or_else(|_| async { let block_id_or_err = warp::path::param::<BlockId>().or_else(|_| async {
Err(warp_utils::reject::custom_bad_request( Err(warp_utils::reject::custom_bad_request(
"Invalid block ID".to_string(), "Invalid block ID".to_string(),
@ -1899,7 +2010,69 @@ pub fn serve<T: BeaconChainTypes>(
}; };
let (block, _) = chain let (block, _) = chain
.produce_block_with_verification( .produce_block_with_verification::<FullPayload<T::EthSpec>>(
randao_reveal,
slot,
query.graffiti.map(Into::into),
randao_verification,
)
.map_err(warp_utils::reject::block_production_error)?;
let fork_name = block
.to_ref()
.fork_name(&chain.spec)
.map_err(inconsistent_fork_rejection)?;
fork_versioned_response(endpoint_version, fork_name, block)
})
},
);
// GET validator/blinded_blocks/{slot}
let get_validator_blinded_blocks = any_version
.and(warp::path("validator"))
.and(warp::path("blinded_blocks"))
.and(warp::path::param::<Slot>().or_else(|_| async {
Err(warp_utils::reject::custom_bad_request(
"Invalid slot".to_string(),
))
}))
.and(warp::path::end())
.and(not_while_syncing_filter.clone())
.and(warp::query::<api_types::ValidatorBlocksQuery>())
.and(chain_filter.clone())
.and_then(
|endpoint_version: EndpointVersion,
slot: Slot,
query: api_types::ValidatorBlocksQuery,
chain: Arc<BeaconChain<T>>| {
blocking_json_task(move || {
let randao_reveal = query.randao_reveal.as_ref().map_or_else(
|| {
if query.verify_randao {
Err(warp_utils::reject::custom_bad_request(
"randao_reveal is mandatory unless verify_randao=false".into(),
))
} else {
Ok(Signature::empty())
}
},
|sig_bytes| {
sig_bytes.try_into().map_err(|e| {
warp_utils::reject::custom_bad_request(format!(
"randao reveal is not a valid BLS signature: {:?}",
e
))
})
},
)?;
let randao_verification = if query.verify_randao {
ProduceBlockVerification::VerifyRandao
} else {
ProduceBlockVerification::NoVerification
};
let (block, _) = chain
.produce_block_with_verification::<BlindedPayload<T::EthSpec>>(
randao_reveal, randao_reveal,
slot, slot,
query.graffiti.map(Into::into), query.graffiti.map(Into::into),
@ -2766,6 +2939,7 @@ pub fn serve<T: BeaconChainTypes>(
.or(get_node_peer_count.boxed()) .or(get_node_peer_count.boxed())
.or(get_validator_duties_proposer.boxed()) .or(get_validator_duties_proposer.boxed())
.or(get_validator_blocks.boxed()) .or(get_validator_blocks.boxed())
.or(get_validator_blinded_blocks.boxed())
.or(get_validator_attestation_data.boxed()) .or(get_validator_attestation_data.boxed())
.or(get_validator_aggregate_attestation.boxed()) .or(get_validator_aggregate_attestation.boxed())
.or(get_validator_sync_committee_contribution.boxed()) .or(get_validator_sync_committee_contribution.boxed())
@ -2791,6 +2965,7 @@ pub fn serve<T: BeaconChainTypes>(
.or(warp::post().and( .or(warp::post().and(
post_beacon_blocks post_beacon_blocks
.boxed() .boxed()
.or(post_beacon_blinded_blocks.boxed())
.or(post_beacon_pool_attestations.boxed()) .or(post_beacon_pool_attestations.boxed())
.or(post_beacon_pool_attester_slashings.boxed()) .or(post_beacon_pool_attester_slashings.boxed())
.or(post_beacon_pool_proposer_slashings.boxed()) .or(post_beacon_pool_proposer_slashings.boxed())

View File

@ -1902,7 +1902,7 @@ impl ApiTester {
let block = self let block = self
.client .client
.get_validator_blocks::<E>(slot, &randao_reveal, None) .get_validator_blocks::<E, FullPayload<E>>(slot, &randao_reveal, None)
.await .await
.unwrap() .unwrap()
.data; .data;
@ -1925,7 +1925,12 @@ impl ApiTester {
let block = self let block = self
.client .client
.get_validator_blocks_with_verify_randao::<E>(slot, None, None, Some(false)) .get_validator_blocks_with_verify_randao::<E, FullPayload<E>>(
slot,
None,
None,
Some(false),
)
.await .await
.unwrap() .unwrap()
.data; .data;
@ -1976,13 +1981,13 @@ impl ApiTester {
// Check failure with no `verify_randao` passed. // Check failure with no `verify_randao` passed.
self.client self.client
.get_validator_blocks::<E>(slot, &bad_randao_reveal, None) .get_validator_blocks::<E, FullPayload<E>>(slot, &bad_randao_reveal, None)
.await .await
.unwrap_err(); .unwrap_err();
// Check failure with `verify_randao=true`. // Check failure with `verify_randao=true`.
self.client self.client
.get_validator_blocks_with_verify_randao::<E>( .get_validator_blocks_with_verify_randao::<E, FullPayload<E>>(
slot, slot,
Some(&bad_randao_reveal), Some(&bad_randao_reveal),
None, None,
@ -1993,14 +1998,16 @@ impl ApiTester {
// Check failure with no randao reveal provided. // Check failure with no randao reveal provided.
self.client self.client
.get_validator_blocks_with_verify_randao::<E>(slot, None, None, None) .get_validator_blocks_with_verify_randao::<E, FullPayload<E>>(
slot, None, None, None,
)
.await .await
.unwrap_err(); .unwrap_err();
// Check success with `verify_randao=false`. // Check success with `verify_randao=false`.
let block = self let block = self
.client .client
.get_validator_blocks_with_verify_randao::<E>( .get_validator_blocks_with_verify_randao::<E, FullPayload<E>>(
slot, slot,
Some(&bad_randao_reveal), Some(&bad_randao_reveal),
None, None,

View File

@ -37,7 +37,7 @@ rand = "0.7.3"
directory = { path = "../../common/directory" } directory = { path = "../../common/directory" }
regex = "1.5.5" regex = "1.5.5"
strum = { version = "0.21.0", features = ["derive"] } strum = { version = "0.21.0", features = ["derive"] }
superstruct = "0.4.0" superstruct = "0.4.1"
prometheus-client = "0.15.0" prometheus-client = "0.15.0"
unused_port = { path = "../../common/unused_port" } unused_port = { path = "../../common/unused_port" }

View File

@ -21,11 +21,11 @@ type E = MinimalEthSpec;
/// Merge block with length < max_rpc_size. /// Merge block with length < max_rpc_size.
fn merge_block_small(fork_context: &ForkContext) -> BeaconBlock<E> { fn merge_block_small(fork_context: &ForkContext) -> BeaconBlock<E> {
let mut block = BeaconBlockMerge::empty(&E::default_spec()); let mut block = BeaconBlockMerge::<E>::empty(&E::default_spec());
let tx = VariableList::from(vec![0; 1024]); let tx = VariableList::from(vec![0; 1024]);
let txs = VariableList::from(std::iter::repeat(tx).take(100).collect::<Vec<_>>()); let txs = VariableList::from(std::iter::repeat(tx).take(100).collect::<Vec<_>>());
block.body.execution_payload.transactions = txs; block.body.execution_payload.execution_payload.transactions = txs;
let block = BeaconBlock::Merge(block); let block = BeaconBlock::Merge(block);
assert!(block.ssz_bytes_len() <= max_rpc_size(fork_context)); assert!(block.ssz_bytes_len() <= max_rpc_size(fork_context));
@ -36,11 +36,11 @@ fn merge_block_small(fork_context: &ForkContext) -> BeaconBlock<E> {
/// The max limit for a merge block is in the order of ~16GiB which wouldn't fit in memory. /// The max limit for a merge block is in the order of ~16GiB which wouldn't fit in memory.
/// Hence, we generate a merge block just greater than `MAX_RPC_SIZE` to test rejection on the rpc layer. /// Hence, we generate a merge block just greater than `MAX_RPC_SIZE` to test rejection on the rpc layer.
fn merge_block_large(fork_context: &ForkContext) -> BeaconBlock<E> { fn merge_block_large(fork_context: &ForkContext) -> BeaconBlock<E> {
let mut block = BeaconBlockMerge::empty(&E::default_spec()); let mut block = BeaconBlockMerge::<E>::empty(&E::default_spec());
let tx = VariableList::from(vec![0; 1024]); let tx = VariableList::from(vec![0; 1024]);
let txs = VariableList::from(std::iter::repeat(tx).take(100000).collect::<Vec<_>>()); let txs = VariableList::from(std::iter::repeat(tx).take(100000).collect::<Vec<_>>());
block.body.execution_payload.transactions = txs; block.body.execution_payload.execution_payload.transactions = txs;
let block = BeaconBlock::Merge(block); let block = BeaconBlock::Merge(block);
assert!(block.ssz_bytes_len() > max_rpc_size(fork_context)); assert!(block.ssz_bytes_len() > max_rpc_size(fork_context));

View File

@ -454,6 +454,13 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.requires("merge") .requires("merge")
.takes_value(true) .takes_value(true)
) )
.arg(
Arg::with_name("payload-builders")
.long("payload-builders")
.help("The URL of a service compatible with the MEV-boost API.")
.requires("merge")
.takes_value(true)
)
/* /*
* Database purging and compaction. * Database purging and compaction.

View File

@ -250,6 +250,14 @@ pub fn get_config<E: EthSpec>(
el_config.execution_endpoints = client_config.eth1.endpoints.clone(); el_config.execution_endpoints = client_config.eth1.endpoints.clone();
} }
if let Some(endpoints) = cli_args.value_of("payload-builders") {
el_config.builder_endpoints = endpoints
.split(',')
.map(SensitiveUrl::parse)
.collect::<Result<_, _>>()
.map_err(|e| format!("payload-builders contains an invalid URL {:?}", e))?;
}
if let Some(secrets) = cli_args.value_of("jwt-secrets") { if let Some(secrets) = cli_args.value_of("jwt-secrets") {
let secret_files: Vec<_> = secrets.split(',').map(PathBuf::from).collect(); let secret_files: Vec<_> = secrets.split(',').map(PathBuf::from).collect();
if !secret_files.is_empty() && secret_files.len() != el_config.execution_endpoints.len() if !secret_files.is_empty() && secret_files.len() != el_config.execution_endpoints.len()

View File

@ -579,9 +579,9 @@ impl BeaconNodeHttpClient {
/// `POST beacon/blocks` /// `POST beacon/blocks`
/// ///
/// Returns `Ok(None)` on a 404 error. /// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blocks<T: EthSpec>( pub async fn post_beacon_blocks<T: EthSpec, Payload: ExecPayload<T>>(
&self, &self,
block: &SignedBeaconBlock<T>, block: &SignedBeaconBlock<T, Payload>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut path = self.eth_path(V1)?; let mut path = self.eth_path(V1)?;
@ -596,6 +596,26 @@ impl BeaconNodeHttpClient {
Ok(()) Ok(())
} }
/// `POST beacon/blinded_blocks`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blinded_blocks<T: EthSpec, Payload: ExecPayload<T>>(
&self,
block: &SignedBeaconBlock<T, Payload>,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("blinded_blocks");
self.post_with_timeout(path, block, self.timeouts.proposal)
.await?;
Ok(())
}
/// Path for `v2/beacon/blocks` /// Path for `v2/beacon/blocks`
pub fn get_beacon_blocks_path(&self, block_id: BlockId) -> Result<Url, Error> { pub fn get_beacon_blocks_path(&self, block_id: BlockId) -> Result<Url, Error> {
let mut path = self.eth_path(V2)?; let mut path = self.eth_path(V2)?;
@ -1150,24 +1170,24 @@ impl BeaconNodeHttpClient {
} }
/// `GET v2/validator/blocks/{slot}` /// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks<T: EthSpec>( pub async fn get_validator_blocks<T: EthSpec, Payload: ExecPayload<T>>(
&self, &self,
slot: Slot, slot: Slot,
randao_reveal: &SignatureBytes, randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>, graffiti: Option<&Graffiti>,
) -> Result<ForkVersionedResponse<BeaconBlock<T>>, Error> { ) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
self.get_validator_blocks_with_verify_randao(slot, Some(randao_reveal), graffiti, None) self.get_validator_blocks_with_verify_randao(slot, Some(randao_reveal), graffiti, None)
.await .await
} }
/// `GET v2/validator/blocks/{slot}` /// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks_with_verify_randao<T: EthSpec>( pub async fn get_validator_blocks_with_verify_randao<T: EthSpec, Payload: ExecPayload<T>>(
&self, &self,
slot: Slot, slot: Slot,
randao_reveal: Option<&SignatureBytes>, randao_reveal: Option<&SignatureBytes>,
graffiti: Option<&Graffiti>, graffiti: Option<&Graffiti>,
verify_randao: Option<bool>, verify_randao: Option<bool>,
) -> Result<ForkVersionedResponse<BeaconBlock<T>>, Error> { ) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
let mut path = self.eth_path(V2)?; let mut path = self.eth_path(V2)?;
path.path_segments_mut() path.path_segments_mut()
@ -1194,6 +1214,59 @@ impl BeaconNodeHttpClient {
self.get(path).await self.get(path).await
} }
/// `GET v2/validator/blinded_blocks/{slot}`
pub async fn get_validator_blinded_blocks<T: EthSpec, Payload: ExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
self.get_validator_blinded_blocks_with_verify_randao(
slot,
Some(randao_reveal),
graffiti,
None,
)
.await
}
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blinded_blocks_with_verify_randao<
T: EthSpec,
Payload: ExecPayload<T>,
>(
&self,
slot: Slot,
randao_reveal: Option<&SignatureBytes>,
graffiti: Option<&Graffiti>,
verify_randao: Option<bool>,
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
let mut path = self.eth_path(V2)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("blinded_blocks")
.push(&slot.to_string());
if let Some(randao_reveal) = randao_reveal {
path.query_pairs_mut()
.append_pair("randao_reveal", &randao_reveal.to_string());
}
if let Some(graffiti) = graffiti {
path.query_pairs_mut()
.append_pair("graffiti", &graffiti.to_string());
}
if let Some(verify_randao) = verify_randao {
path.query_pairs_mut()
.append_pair("verify_randao", &verify_randao.to_string());
}
self.get(path).await
}
/// `GET validator/attestation_data?slot,committee_index` /// `GET validator/attestation_data?slot,committee_index`
pub async fn get_validator_attestation_data( pub async fn get_validator_attestation_data(
&self, &self,

View File

@ -6,8 +6,8 @@ use std::marker::PhantomData;
use std::time::Duration; use std::time::Duration;
use types::{ use types::{
consts::merge::INTERVALS_PER_SLOT, AttestationShufflingId, BeaconBlock, BeaconState, consts::merge::INTERVALS_PER_SLOT, AttestationShufflingId, BeaconBlock, BeaconState,
BeaconStateError, ChainSpec, Checkpoint, Epoch, EthSpec, ExecutionBlockHash, Hash256, BeaconStateError, ChainSpec, Checkpoint, Epoch, EthSpec, ExecPayload, ExecutionBlockHash,
IndexedAttestation, RelativeEpoch, SignedBeaconBlock, Slot, Hash256, IndexedAttestation, RelativeEpoch, SignedBeaconBlock, Slot,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -323,7 +323,7 @@ where
} else { } else {
// Assume that this payload is valid, since the anchor should be a trusted block and // Assume that this payload is valid, since the anchor should be a trusted block and
// state. // state.
ExecutionStatus::Valid(message.body.execution_payload.block_hash) ExecutionStatus::Valid(message.body.execution_payload.block_hash())
} }
}, },
); );
@ -648,7 +648,7 @@ where
.map_err(Error::AfterBlockFailed)?; .map_err(Error::AfterBlockFailed)?;
let execution_status = if let Ok(execution_payload) = block.body().execution_payload() { let execution_status = if let Ok(execution_payload) = block.body().execution_payload() {
let block_hash = execution_payload.block_hash; let block_hash = execution_payload.block_hash();
if block_hash == ExecutionBlockHash::zero() { if block_hash == ExecutionBlockHash::zero() {
// The block is post-merge-fork, but pre-terminal-PoW block. We don't need to verify // The block is post-merge-fork, but pre-terminal-PoW block. We don't need to verify

View File

@ -87,9 +87,9 @@ pub enum VerifyBlockRoot {
/// re-calculating the root when it is already known. Note `block_root` should be equal to the /// re-calculating the root when it is already known. Note `block_root` should be equal to the
/// tree hash root of the block, NOT the signing root of the block. This function takes /// tree hash root of the block, NOT the signing root of the block. This function takes
/// care of mixing in the domain. /// care of mixing in the domain.
pub fn per_block_processing<T: EthSpec>( pub fn per_block_processing<T: EthSpec, Payload: ExecPayload<T>>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
signed_block: &SignedBeaconBlock<T>, signed_block: &SignedBeaconBlock<T, Payload>,
block_root: Option<Hash256>, block_root: Option<Hash256>,
block_signature_strategy: BlockSignatureStrategy, block_signature_strategy: BlockSignatureStrategy,
verify_block_root: VerifyBlockRoot, verify_block_root: VerifyBlockRoot,
@ -129,7 +129,12 @@ pub fn per_block_processing<T: EthSpec>(
BlockSignatureStrategy::VerifyRandao => VerifySignatures::False, BlockSignatureStrategy::VerifyRandao => VerifySignatures::False,
}; };
let proposer_index = process_block_header(state, block, verify_block_root, spec)?; let proposer_index = process_block_header(
state,
block.temporary_block_header(),
verify_block_root,
spec,
)?;
if verify_signatures.is_true() { if verify_signatures.is_true() {
verify_block_signature(state, signed_block, block_root, spec)?; verify_block_signature(state, signed_block, block_root, spec)?;
@ -172,28 +177,28 @@ pub fn per_block_processing<T: EthSpec>(
/// Processes the block header, returning the proposer index. /// Processes the block header, returning the proposer index.
pub fn process_block_header<T: EthSpec>( pub fn process_block_header<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
block: BeaconBlockRef<'_, T>, block_header: BeaconBlockHeader,
verify_block_root: VerifyBlockRoot, verify_block_root: VerifyBlockRoot,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<u64, BlockOperationError<HeaderInvalid>> { ) -> Result<u64, BlockOperationError<HeaderInvalid>> {
// Verify that the slots match // Verify that the slots match
verify!( verify!(
block.slot() == state.slot(), block_header.slot == state.slot(),
HeaderInvalid::StateSlotMismatch HeaderInvalid::StateSlotMismatch
); );
// Verify that the block is newer than the latest block header // Verify that the block is newer than the latest block header
verify!( verify!(
block.slot() > state.latest_block_header().slot, block_header.slot > state.latest_block_header().slot,
HeaderInvalid::OlderThanLatestBlockHeader { HeaderInvalid::OlderThanLatestBlockHeader {
block_slot: block.slot(), block_slot: block_header.slot,
latest_block_header_slot: state.latest_block_header().slot, latest_block_header_slot: state.latest_block_header().slot,
} }
); );
// Verify that proposer index is the correct index // Verify that proposer index is the correct index
let proposer_index = block.proposer_index() as usize; let proposer_index = block_header.proposer_index as usize;
let state_proposer_index = state.get_beacon_proposer_index(block.slot(), spec)?; let state_proposer_index = state.get_beacon_proposer_index(block_header.slot, spec)?;
verify!( verify!(
proposer_index == state_proposer_index, proposer_index == state_proposer_index,
HeaderInvalid::ProposerIndexMismatch { HeaderInvalid::ProposerIndexMismatch {
@ -205,15 +210,15 @@ pub fn process_block_header<T: EthSpec>(
if verify_block_root == VerifyBlockRoot::True { if verify_block_root == VerifyBlockRoot::True {
let expected_previous_block_root = state.latest_block_header().tree_hash_root(); let expected_previous_block_root = state.latest_block_header().tree_hash_root();
verify!( verify!(
block.parent_root() == expected_previous_block_root, block_header.parent_root == expected_previous_block_root,
HeaderInvalid::ParentBlockRootMismatch { HeaderInvalid::ParentBlockRootMismatch {
state: expected_previous_block_root, state: expected_previous_block_root,
block: block.parent_root(), block: block_header.parent_root,
} }
); );
} }
*state.latest_block_header_mut() = block.temporary_block_header(); *state.latest_block_header_mut() = block_header;
// Verify proposer is not slashed // Verify proposer is not slashed
verify!( verify!(
@ -221,15 +226,15 @@ pub fn process_block_header<T: EthSpec>(
HeaderInvalid::ProposerSlashed(proposer_index) HeaderInvalid::ProposerSlashed(proposer_index)
); );
Ok(block.proposer_index()) Ok(proposer_index as u64)
} }
/// Verifies the signature of a block. /// Verifies the signature of a block.
/// ///
/// Spec v0.12.1 /// Spec v0.12.1
pub fn verify_block_signature<T: EthSpec>( pub fn verify_block_signature<T: EthSpec, Payload: ExecPayload<T>>(
state: &BeaconState<T>, state: &BeaconState<T>,
block: &SignedBeaconBlock<T>, block: &SignedBeaconBlock<T, Payload>,
block_root: Option<Hash256>, block_root: Option<Hash256>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), BlockOperationError<HeaderInvalid>> { ) -> Result<(), BlockOperationError<HeaderInvalid>> {
@ -250,9 +255,9 @@ pub fn verify_block_signature<T: EthSpec>(
/// Verifies the `randao_reveal` against the block's proposer pubkey and updates /// Verifies the `randao_reveal` against the block's proposer pubkey and updates
/// `state.latest_randao_mixes`. /// `state.latest_randao_mixes`.
pub fn process_randao<T: EthSpec>( pub fn process_randao<T: EthSpec, Payload: ExecPayload<T>>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
block: BeaconBlockRef<'_, T>, block: BeaconBlockRef<'_, T, Payload>,
verify_signatures: VerifySignatures, verify_signatures: VerifySignatures,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), BlockProcessingError> { ) -> Result<(), BlockProcessingError> {
@ -314,34 +319,34 @@ pub fn get_new_eth1_data<T: EthSpec>(
/// Contains a partial set of checks from the `process_execution_payload` function: /// Contains a partial set of checks from the `process_execution_payload` function:
/// ///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/beacon-chain.md#process_execution_payload /// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/beacon-chain.md#process_execution_payload
pub fn partially_verify_execution_payload<T: EthSpec>( pub fn partially_verify_execution_payload<T: EthSpec, Payload: ExecPayload<T>>(
state: &BeaconState<T>, state: &BeaconState<T>,
payload: &ExecutionPayload<T>, payload: &Payload,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), BlockProcessingError> { ) -> Result<(), BlockProcessingError> {
if is_merge_transition_complete(state) { if is_merge_transition_complete(state) {
block_verify!( block_verify!(
payload.parent_hash == state.latest_execution_payload_header()?.block_hash, payload.parent_hash() == state.latest_execution_payload_header()?.block_hash,
BlockProcessingError::ExecutionHashChainIncontiguous { BlockProcessingError::ExecutionHashChainIncontiguous {
expected: state.latest_execution_payload_header()?.block_hash, expected: state.latest_execution_payload_header()?.block_hash,
found: payload.parent_hash, found: payload.parent_hash(),
} }
); );
} }
block_verify!( block_verify!(
payload.prev_randao == *state.get_randao_mix(state.current_epoch())?, payload.prev_randao() == *state.get_randao_mix(state.current_epoch())?,
BlockProcessingError::ExecutionRandaoMismatch { BlockProcessingError::ExecutionRandaoMismatch {
expected: *state.get_randao_mix(state.current_epoch())?, expected: *state.get_randao_mix(state.current_epoch())?,
found: payload.prev_randao, found: payload.prev_randao(),
} }
); );
let timestamp = compute_timestamp_at_slot(state, spec)?; let timestamp = compute_timestamp_at_slot(state, spec)?;
block_verify!( block_verify!(
payload.timestamp == timestamp, payload.timestamp() == timestamp,
BlockProcessingError::ExecutionInvalidTimestamp { BlockProcessingError::ExecutionInvalidTimestamp {
expected: timestamp, expected: timestamp,
found: payload.timestamp, found: payload.timestamp(),
} }
); );
@ -355,29 +360,14 @@ pub fn partially_verify_execution_payload<T: EthSpec>(
/// Partially equivalent to the `process_execution_payload` function: /// Partially equivalent to the `process_execution_payload` function:
/// ///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/beacon-chain.md#process_execution_payload /// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/beacon-chain.md#process_execution_payload
pub fn process_execution_payload<T: EthSpec>( pub fn process_execution_payload<T: EthSpec, Payload: ExecPayload<T>>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
payload: &ExecutionPayload<T>, payload: &Payload,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), BlockProcessingError> { ) -> Result<(), BlockProcessingError> {
partially_verify_execution_payload(state, payload, spec)?; partially_verify_execution_payload(state, payload, spec)?;
*state.latest_execution_payload_header_mut()? = ExecutionPayloadHeader { *state.latest_execution_payload_header_mut()? = payload.to_execution_payload_header();
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom.clone(),
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data.clone(),
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: payload.transactions.tree_hash_root(),
};
Ok(()) Ok(())
} }
@ -394,24 +384,21 @@ pub fn is_merge_transition_complete<T: EthSpec>(state: &BeaconState<T>) -> bool
.unwrap_or(false) .unwrap_or(false)
} }
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#is_merge_transition_block /// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#is_merge_transition_block
pub fn is_merge_transition_block<T: EthSpec>( pub fn is_merge_transition_block<T: EthSpec, Payload: ExecPayload<T>>(
state: &BeaconState<T>, state: &BeaconState<T>,
body: BeaconBlockBodyRef<T>, body: BeaconBlockBodyRef<T, Payload>,
) -> bool { ) -> bool {
body.execution_payload() body.execution_payload()
.map(|payload| { .map(|payload| !is_merge_transition_complete(state) && *payload != Payload::default())
!is_merge_transition_complete(state) && *payload != <ExecutionPayload<T>>::default()
})
.unwrap_or(false) .unwrap_or(false)
} }
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#is_execution_enabled /// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#is_execution_enabled
pub fn is_execution_enabled<T: EthSpec>( pub fn is_execution_enabled<T: EthSpec, Payload: ExecPayload<T>>(
state: &BeaconState<T>, state: &BeaconState<T>,
body: BeaconBlockBodyRef<T>, body: BeaconBlockBodyRef<T, Payload>,
) -> bool { ) -> bool {
is_merge_transition_block(state, body) || is_merge_transition_complete(state) is_merge_transition_block(state, body) || is_merge_transition_complete(state)
} }
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#compute_timestamp_at_slot /// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#compute_timestamp_at_slot
pub fn compute_timestamp_at_slot<T: EthSpec>( pub fn compute_timestamp_at_slot<T: EthSpec>(
state: &BeaconState<T>, state: &BeaconState<T>,

View File

@ -7,7 +7,7 @@ use bls::{verify_signature_sets, PublicKey, PublicKeyBytes, SignatureSet};
use rayon::prelude::*; use rayon::prelude::*;
use std::borrow::Cow; use std::borrow::Cow;
use types::{ use types::{
BeaconState, BeaconStateError, ChainSpec, EthSpec, Hash256, IndexedAttestation, BeaconState, BeaconStateError, ChainSpec, EthSpec, ExecPayload, Hash256, IndexedAttestation,
SignedBeaconBlock, SignedBeaconBlock,
}; };
@ -117,11 +117,11 @@ where
/// contains invalid signatures on deposits._ /// contains invalid signatures on deposits._
/// ///
/// See `Self::verify` for more detail. /// See `Self::verify` for more detail.
pub fn verify_entire_block( pub fn verify_entire_block<Payload: ExecPayload<T>>(
state: &'a BeaconState<T>, state: &'a BeaconState<T>,
get_pubkey: F, get_pubkey: F,
decompressor: D, decompressor: D,
block: &'a SignedBeaconBlock<T>, block: &'a SignedBeaconBlock<T, Payload>,
block_root: Option<Hash256>, block_root: Option<Hash256>,
spec: &'a ChainSpec, spec: &'a ChainSpec,
) -> Result<()> { ) -> Result<()> {
@ -131,9 +131,9 @@ where
} }
/// Includes all signatures on the block (except the deposit signatures) for verification. /// Includes all signatures on the block (except the deposit signatures) for verification.
pub fn include_all_signatures( pub fn include_all_signatures<Payload: ExecPayload<T>>(
&mut self, &mut self,
block: &'a SignedBeaconBlock<T>, block: &'a SignedBeaconBlock<T, Payload>,
block_root: Option<Hash256>, block_root: Option<Hash256>,
) -> Result<()> { ) -> Result<()> {
self.include_block_proposal(block, block_root)?; self.include_block_proposal(block, block_root)?;
@ -144,9 +144,9 @@ where
/// Includes all signatures on the block (except the deposit signatures and the proposal /// Includes all signatures on the block (except the deposit signatures and the proposal
/// signature) for verification. /// signature) for verification.
pub fn include_all_signatures_except_proposal( pub fn include_all_signatures_except_proposal<Payload: ExecPayload<T>>(
&mut self, &mut self,
block: &'a SignedBeaconBlock<T>, block: &'a SignedBeaconBlock<T, Payload>,
) -> Result<()> { ) -> Result<()> {
self.include_randao_reveal(block)?; self.include_randao_reveal(block)?;
self.include_proposer_slashings(block)?; self.include_proposer_slashings(block)?;
@ -160,9 +160,9 @@ where
} }
/// Includes the block signature for `self.block` for verification. /// Includes the block signature for `self.block` for verification.
pub fn include_block_proposal( pub fn include_block_proposal<Payload: ExecPayload<T>>(
&mut self, &mut self,
block: &'a SignedBeaconBlock<T>, block: &'a SignedBeaconBlock<T, Payload>,
block_root: Option<Hash256>, block_root: Option<Hash256>,
) -> Result<()> { ) -> Result<()> {
let set = block_proposal_signature_set( let set = block_proposal_signature_set(
@ -177,7 +177,10 @@ where
} }
/// Includes the randao signature for `self.block` for verification. /// Includes the randao signature for `self.block` for verification.
pub fn include_randao_reveal(&mut self, block: &'a SignedBeaconBlock<T>) -> Result<()> { pub fn include_randao_reveal<Payload: ExecPayload<T>>(
&mut self,
block: &'a SignedBeaconBlock<T, Payload>,
) -> Result<()> {
let set = randao_signature_set( let set = randao_signature_set(
self.state, self.state,
self.get_pubkey.clone(), self.get_pubkey.clone(),
@ -189,7 +192,10 @@ where
} }
/// Includes all signatures in `self.block.body.proposer_slashings` for verification. /// Includes all signatures in `self.block.body.proposer_slashings` for verification.
pub fn include_proposer_slashings(&mut self, block: &'a SignedBeaconBlock<T>) -> Result<()> { pub fn include_proposer_slashings<Payload: ExecPayload<T>>(
&mut self,
block: &'a SignedBeaconBlock<T, Payload>,
) -> Result<()> {
self.sets self.sets
.sets .sets
.reserve(block.message().body().proposer_slashings().len() * 2); .reserve(block.message().body().proposer_slashings().len() * 2);
@ -215,7 +221,10 @@ where
} }
/// Includes all signatures in `self.block.body.attester_slashings` for verification. /// Includes all signatures in `self.block.body.attester_slashings` for verification.
pub fn include_attester_slashings(&mut self, block: &'a SignedBeaconBlock<T>) -> Result<()> { pub fn include_attester_slashings<Payload: ExecPayload<T>>(
&mut self,
block: &'a SignedBeaconBlock<T, Payload>,
) -> Result<()> {
self.sets self.sets
.sets .sets
.reserve(block.message().body().attester_slashings().len() * 2); .reserve(block.message().body().attester_slashings().len() * 2);
@ -241,9 +250,9 @@ where
} }
/// Includes all signatures in `self.block.body.attestations` for verification. /// Includes all signatures in `self.block.body.attestations` for verification.
pub fn include_attestations( pub fn include_attestations<Payload: ExecPayload<T>>(
&mut self, &mut self,
block: &'a SignedBeaconBlock<T>, block: &'a SignedBeaconBlock<T, Payload>,
) -> Result<Vec<IndexedAttestation<T>>> { ) -> Result<Vec<IndexedAttestation<T>>> {
self.sets self.sets
.sets .sets
@ -280,7 +289,10 @@ where
} }
/// Includes all signatures in `self.block.body.voluntary_exits` for verification. /// Includes all signatures in `self.block.body.voluntary_exits` for verification.
pub fn include_exits(&mut self, block: &'a SignedBeaconBlock<T>) -> Result<()> { pub fn include_exits<Payload: ExecPayload<T>>(
&mut self,
block: &'a SignedBeaconBlock<T, Payload>,
) -> Result<()> {
self.sets self.sets
.sets .sets
.reserve(block.message().body().voluntary_exits().len()); .reserve(block.message().body().voluntary_exits().len());
@ -301,7 +313,10 @@ where
} }
/// Include the signature of the block's sync aggregate (if it exists) for verification. /// Include the signature of the block's sync aggregate (if it exists) for verification.
pub fn include_sync_aggregate(&mut self, block: &'a SignedBeaconBlock<T>) -> Result<()> { pub fn include_sync_aggregate<Payload: ExecPayload<T>>(
&mut self,
block: &'a SignedBeaconBlock<T, Payload>,
) -> Result<()> {
if let Ok(sync_aggregate) = block.message().body().sync_aggregate() { if let Ok(sync_aggregate) = block.message().body().sync_aggregate() {
if let Some(signature_set) = sync_aggregate_signature_set( if let Some(signature_set) = sync_aggregate_signature_set(
&self.decompressor, &self.decompressor,

View File

@ -8,9 +8,9 @@ use crate::VerifySignatures;
use safe_arith::SafeArith; use safe_arith::SafeArith;
use types::consts::altair::{PARTICIPATION_FLAG_WEIGHTS, PROPOSER_WEIGHT, WEIGHT_DENOMINATOR}; use types::consts::altair::{PARTICIPATION_FLAG_WEIGHTS, PROPOSER_WEIGHT, WEIGHT_DENOMINATOR};
pub fn process_operations<'a, T: EthSpec>( pub fn process_operations<'a, T: EthSpec, Payload: ExecPayload<T>>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
block_body: BeaconBlockBodyRef<'a, T>, block_body: BeaconBlockBodyRef<'a, T, Payload>,
proposer_index: u64, proposer_index: u64,
verify_signatures: VerifySignatures, verify_signatures: VerifySignatures,
spec: &ChainSpec, spec: &ChainSpec,
@ -217,9 +217,9 @@ pub fn process_attester_slashings<T: EthSpec>(
} }
/// Wrapper function to handle calling the correct version of `process_attestations` based on /// Wrapper function to handle calling the correct version of `process_attestations` based on
/// the fork. /// the fork.
pub fn process_attestations<'a, T: EthSpec>( pub fn process_attestations<'a, T: EthSpec, Payload: ExecPayload<T>>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
block_body: BeaconBlockBodyRef<'a, T>, block_body: BeaconBlockBodyRef<'a, T, Payload>,
proposer_index: u64, proposer_index: u64,
verify_signatures: VerifySignatures, verify_signatures: VerifySignatures,
spec: &ChainSpec, spec: &ChainSpec,

View File

@ -8,10 +8,11 @@ use std::borrow::Cow;
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::{ use types::{
AggregateSignature, AttesterSlashing, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, AggregateSignature, AttesterSlashing, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec,
DepositData, Domain, Epoch, EthSpec, Fork, Hash256, InconsistentFork, IndexedAttestation, DepositData, Domain, Epoch, EthSpec, ExecPayload, Fork, Hash256, InconsistentFork,
ProposerSlashing, PublicKey, PublicKeyBytes, Signature, SignedAggregateAndProof, IndexedAttestation, ProposerSlashing, PublicKey, PublicKeyBytes, Signature,
SignedBeaconBlock, SignedBeaconBlockHeader, SignedContributionAndProof, SignedRoot, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader,
SignedVoluntaryExit, SigningData, Slot, SyncAggregate, SyncAggregatorSelectionData, Unsigned, SignedContributionAndProof, SignedRoot, SignedVoluntaryExit, SigningData, Slot, SyncAggregate,
SyncAggregatorSelectionData, Unsigned,
}; };
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
@ -70,10 +71,10 @@ where
} }
/// A signature set that is valid if a block was signed by the expected block producer. /// A signature set that is valid if a block was signed by the expected block producer.
pub fn block_proposal_signature_set<'a, T, F>( pub fn block_proposal_signature_set<'a, T, F, Payload: ExecPayload<T>>(
state: &'a BeaconState<T>, state: &'a BeaconState<T>,
get_pubkey: F, get_pubkey: F,
signed_block: &'a SignedBeaconBlock<T>, signed_block: &'a SignedBeaconBlock<T, Payload>,
block_root: Option<Hash256>, block_root: Option<Hash256>,
spec: &'a ChainSpec, spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> ) -> Result<SignatureSet<'a>>
@ -107,8 +108,8 @@ where
/// Unlike `block_proposal_signature_set` this does **not** check that the proposer index is /// Unlike `block_proposal_signature_set` this does **not** check that the proposer index is
/// correct according to the shuffling. It should only be used if no suitable `BeaconState` is /// correct according to the shuffling. It should only be used if no suitable `BeaconState` is
/// available. /// available.
pub fn block_proposal_signature_set_from_parts<'a, T, F>( pub fn block_proposal_signature_set_from_parts<'a, T, F, Payload: ExecPayload<T>>(
signed_block: &'a SignedBeaconBlock<T>, signed_block: &'a SignedBeaconBlock<T, Payload>,
block_root: Option<Hash256>, block_root: Option<Hash256>,
proposer_index: u64, proposer_index: u64,
fork: &Fork, fork: &Fork,
@ -151,10 +152,10 @@ where
} }
/// A signature set that is valid if the block proposers randao reveal signature is correct. /// A signature set that is valid if the block proposers randao reveal signature is correct.
pub fn randao_signature_set<'a, T, F>( pub fn randao_signature_set<'a, T, F, Payload: ExecPayload<T>>(
state: &'a BeaconState<T>, state: &'a BeaconState<T>,
get_pubkey: F, get_pubkey: F,
block: BeaconBlockRef<'a, T>, block: BeaconBlockRef<'a, T, Payload>,
spec: &'a ChainSpec, spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> ) -> Result<SignatureSet<'a>>
where where

View File

@ -43,7 +43,7 @@ regex = "1.5.5"
lazy_static = "1.4.0" lazy_static = "1.4.0"
parking_lot = "0.11.1" parking_lot = "0.11.1"
itertools = "0.10.0" itertools = "0.10.0"
superstruct = "0.4.0" superstruct = "0.4.1"
serde_json = "1.0.74" serde_json = "1.0.74"
smallvec = "1.8.0" smallvec = "1.8.0"

View File

@ -9,6 +9,7 @@ use derivative::Derivative;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz::{Decode, DecodeError}; use ssz::{Decode, DecodeError};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use std::marker::PhantomData;
use superstruct::superstruct; use superstruct::superstruct;
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash::TreeHash; use tree_hash::TreeHash;
@ -29,8 +30,8 @@ use tree_hash_derive::TreeHash;
TestRandom, TestRandom,
Derivative, Derivative,
), ),
derivative(PartialEq, Hash(bound = "T: EthSpec")), derivative(PartialEq, Hash(bound = "T: EthSpec, Payload: ExecPayload<T>")),
serde(bound = "T: EthSpec", deny_unknown_fields), serde(bound = "T: EthSpec, Payload: ExecPayload<T>", deny_unknown_fields),
cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)), cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)),
), ),
ref_attributes( ref_attributes(
@ -41,11 +42,11 @@ use tree_hash_derive::TreeHash;
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative)] #[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))] #[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(untagged)] #[serde(untagged)]
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec, Payload: ExecPayload<T>")]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[tree_hash(enum_behaviour = "transparent")] #[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")] #[ssz(enum_behaviour = "transparent")]
pub struct BeaconBlock<T: EthSpec> { pub struct BeaconBlock<T: EthSpec, Payload: ExecPayload<T> = FullPayload<T>> {
#[superstruct(getter(copy))] #[superstruct(getter(copy))]
pub slot: Slot, pub slot: Slot,
#[superstruct(getter(copy))] #[superstruct(getter(copy))]
@ -56,17 +57,17 @@ pub struct BeaconBlock<T: EthSpec> {
#[superstruct(getter(copy))] #[superstruct(getter(copy))]
pub state_root: Hash256, pub state_root: Hash256,
#[superstruct(only(Base), partial_getter(rename = "body_base"))] #[superstruct(only(Base), partial_getter(rename = "body_base"))]
pub body: BeaconBlockBodyBase<T>, pub body: BeaconBlockBodyBase<T, Payload>,
#[superstruct(only(Altair), partial_getter(rename = "body_altair"))] #[superstruct(only(Altair), partial_getter(rename = "body_altair"))]
pub body: BeaconBlockBodyAltair<T>, pub body: BeaconBlockBodyAltair<T, Payload>,
#[superstruct(only(Merge), partial_getter(rename = "body_merge"))] #[superstruct(only(Merge), partial_getter(rename = "body_merge"))]
pub body: BeaconBlockBodyMerge<T>, pub body: BeaconBlockBodyMerge<T, Payload>,
} }
impl<T: EthSpec> SignedRoot for BeaconBlock<T> {} impl<T: EthSpec, Payload: ExecPayload<T>> SignedRoot for BeaconBlock<T, Payload> {}
impl<'a, T: EthSpec> SignedRoot for BeaconBlockRef<'a, T> {} impl<'a, T: EthSpec, Payload: ExecPayload<T>> SignedRoot for BeaconBlockRef<'a, T, Payload> {}
impl<T: EthSpec> BeaconBlock<T> { impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlock<T, Payload> {
/// Returns an empty block to be used during genesis. /// Returns an empty block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self { pub fn empty(spec: &ChainSpec) -> Self {
if spec.bellatrix_fork_epoch == Some(T::genesis_epoch()) { if spec.bellatrix_fork_epoch == Some(T::genesis_epoch()) {
@ -114,12 +115,12 @@ impl<T: EthSpec> BeaconBlock<T> {
} }
/// Convenience accessor for the `body` as a `BeaconBlockBodyRef`. /// Convenience accessor for the `body` as a `BeaconBlockBodyRef`.
pub fn body(&self) -> BeaconBlockBodyRef<'_, T> { pub fn body(&self) -> BeaconBlockBodyRef<'_, T, Payload> {
self.to_ref().body() self.to_ref().body()
} }
/// Convenience accessor for the `body` as a `BeaconBlockBodyRefMut`. /// Convenience accessor for the `body` as a `BeaconBlockBodyRefMut`.
pub fn body_mut(&mut self) -> BeaconBlockBodyRefMut<'_, T> { pub fn body_mut(&mut self) -> BeaconBlockBodyRefMut<'_, T, Payload> {
self.to_mut().body_mut() self.to_mut().body_mut()
} }
@ -160,7 +161,7 @@ impl<T: EthSpec> BeaconBlock<T> {
fork: &Fork, fork: &Fork,
genesis_validators_root: Hash256, genesis_validators_root: Hash256,
spec: &ChainSpec, spec: &ChainSpec,
) -> SignedBeaconBlock<T> { ) -> SignedBeaconBlock<T, Payload> {
let domain = spec.get_domain( let domain = spec.get_domain(
self.epoch(), self.epoch(),
Domain::BeaconProposer, Domain::BeaconProposer,
@ -173,7 +174,7 @@ impl<T: EthSpec> BeaconBlock<T> {
} }
} }
impl<'a, T: EthSpec> BeaconBlockRef<'a, T> { impl<'a, T: EthSpec, Payload: ExecPayload<T>> BeaconBlockRef<'a, T, Payload> {
/// Returns the name of the fork pertaining to `self`. /// Returns the name of the fork pertaining to `self`.
/// ///
/// Will return an `Err` if `self` has been instantiated to a variant conflicting with the fork /// Will return an `Err` if `self` has been instantiated to a variant conflicting with the fork
@ -197,7 +198,7 @@ impl<'a, T: EthSpec> BeaconBlockRef<'a, T> {
} }
/// Convenience accessor for the `body` as a `BeaconBlockBodyRef`. /// Convenience accessor for the `body` as a `BeaconBlockBodyRef`.
pub fn body(&self) -> BeaconBlockBodyRef<'a, T> { pub fn body(&self) -> BeaconBlockBodyRef<'a, T, Payload> {
match self { match self {
BeaconBlockRef::Base(block) => BeaconBlockBodyRef::Base(&block.body), BeaconBlockRef::Base(block) => BeaconBlockBodyRef::Base(&block.body),
BeaconBlockRef::Altair(block) => BeaconBlockBodyRef::Altair(&block.body), BeaconBlockRef::Altair(block) => BeaconBlockBodyRef::Altair(&block.body),
@ -240,14 +241,14 @@ impl<'a, T: EthSpec> BeaconBlockRef<'a, T> {
/// Extracts a reference to an execution payload from a block, returning an error if the block /// Extracts a reference to an execution payload from a block, returning an error if the block
/// is pre-merge. /// is pre-merge.
pub fn execution_payload(&self) -> Result<&ExecutionPayload<T>, Error> { pub fn execution_payload(&self) -> Result<&Payload, Error> {
self.body().execution_payload() self.body().execution_payload()
} }
} }
impl<'a, T: EthSpec> BeaconBlockRefMut<'a, T> { impl<'a, T: EthSpec, Payload: ExecPayload<T>> BeaconBlockRefMut<'a, T, Payload> {
/// Convert a mutable reference to a beacon block to a mutable ref to its body. /// Convert a mutable reference to a beacon block to a mutable ref to its body.
pub fn body_mut(self) -> BeaconBlockBodyRefMut<'a, T> { pub fn body_mut(self) -> BeaconBlockBodyRefMut<'a, T, Payload> {
match self { match self {
BeaconBlockRefMut::Base(block) => BeaconBlockBodyRefMut::Base(&mut block.body), BeaconBlockRefMut::Base(block) => BeaconBlockBodyRefMut::Base(&mut block.body),
BeaconBlockRefMut::Altair(block) => BeaconBlockBodyRefMut::Altair(&mut block.body), BeaconBlockRefMut::Altair(block) => BeaconBlockBodyRefMut::Altair(&mut block.body),
@ -256,7 +257,7 @@ impl<'a, T: EthSpec> BeaconBlockRefMut<'a, T> {
} }
} }
impl<T: EthSpec> BeaconBlockBase<T> { impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockBase<T, Payload> {
/// Returns an empty block to be used during genesis. /// Returns an empty block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self { pub fn empty(spec: &ChainSpec) -> Self {
BeaconBlockBase { BeaconBlockBase {
@ -277,6 +278,7 @@ impl<T: EthSpec> BeaconBlockBase<T> {
attestations: VariableList::empty(), attestations: VariableList::empty(),
deposits: VariableList::empty(), deposits: VariableList::empty(),
voluntary_exits: VariableList::empty(), voluntary_exits: VariableList::empty(),
_phantom: PhantomData,
}, },
} }
} }
@ -343,7 +345,7 @@ impl<T: EthSpec> BeaconBlockBase<T> {
signature: Signature::empty(), signature: Signature::empty(),
}; };
let mut block = BeaconBlockBase::<T>::empty(spec); let mut block = BeaconBlockBase::<T, Payload>::empty(spec);
for _ in 0..T::MaxProposerSlashings::to_usize() { for _ in 0..T::MaxProposerSlashings::to_usize() {
block block
.body .body
@ -376,7 +378,7 @@ impl<T: EthSpec> BeaconBlockBase<T> {
} }
} }
impl<T: EthSpec> BeaconBlockAltair<T> { impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockAltair<T, Payload> {
/// Returns an empty Altair block to be used during genesis. /// Returns an empty Altair block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self { pub fn empty(spec: &ChainSpec) -> Self {
BeaconBlockAltair { BeaconBlockAltair {
@ -398,13 +400,14 @@ impl<T: EthSpec> BeaconBlockAltair<T> {
deposits: VariableList::empty(), deposits: VariableList::empty(),
voluntary_exits: VariableList::empty(), voluntary_exits: VariableList::empty(),
sync_aggregate: SyncAggregate::empty(), sync_aggregate: SyncAggregate::empty(),
_phantom: PhantomData,
}, },
} }
} }
/// Return an Altair block where the block has maximum size. /// Return an Altair block where the block has maximum size.
pub fn full(spec: &ChainSpec) -> Self { pub fn full(spec: &ChainSpec) -> Self {
let base_block = BeaconBlockBase::full(spec); let base_block: BeaconBlockBase<_, Payload> = BeaconBlockBase::full(spec);
let sync_aggregate = SyncAggregate { let sync_aggregate = SyncAggregate {
sync_committee_signature: AggregateSignature::empty(), sync_committee_signature: AggregateSignature::empty(),
sync_committee_bits: BitVector::default(), sync_committee_bits: BitVector::default(),
@ -428,12 +431,13 @@ impl<T: EthSpec> BeaconBlockAltair<T> {
deposit_count: 0, deposit_count: 0,
}, },
graffiti: Graffiti::default(), graffiti: Graffiti::default(),
_phantom: PhantomData,
}, },
} }
} }
} }
impl<T: EthSpec> BeaconBlockMerge<T> { impl<T: EthSpec, Payload: ExecPayload<T>> BeaconBlockMerge<T, Payload> {
/// Returns an empty Merge block to be used during genesis. /// Returns an empty Merge block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self { pub fn empty(spec: &ChainSpec) -> Self {
BeaconBlockMerge { BeaconBlockMerge {
@ -455,34 +459,7 @@ impl<T: EthSpec> BeaconBlockMerge<T> {
deposits: VariableList::empty(), deposits: VariableList::empty(),
voluntary_exits: VariableList::empty(), voluntary_exits: VariableList::empty(),
sync_aggregate: SyncAggregate::empty(), sync_aggregate: SyncAggregate::empty(),
execution_payload: ExecutionPayload::empty(), execution_payload: Payload::default(),
},
}
}
/// Return an Merge block where the block has maximum size.
pub fn full(spec: &ChainSpec) -> Self {
let altair_block = BeaconBlockAltair::full(spec);
BeaconBlockMerge {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyMerge {
proposer_slashings: altair_block.body.proposer_slashings,
attester_slashings: altair_block.body.attester_slashings,
attestations: altair_block.body.attestations,
deposits: altair_block.body.deposits,
voluntary_exits: altair_block.body.voluntary_exits,
sync_aggregate: altair_block.body.sync_aggregate,
randao_reveal: Signature::empty(),
eth1_data: Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
deposit_count: 0,
},
graffiti: Graffiti::default(),
execution_payload: ExecutionPayload::default(),
}, },
} }
} }

View File

@ -4,6 +4,7 @@ use derivative::Derivative;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use ssz_types::VariableList; use ssz_types::VariableList;
use std::marker::PhantomData;
use superstruct::superstruct; use superstruct::superstruct;
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash; use tree_hash_derive::TreeHash;
@ -25,8 +26,8 @@ use tree_hash_derive::TreeHash;
TestRandom, TestRandom,
Derivative, Derivative,
), ),
derivative(PartialEq, Hash(bound = "T: EthSpec")), derivative(PartialEq, Hash(bound = "T: EthSpec, Payload: ExecPayload<T>")),
serde(bound = "T: EthSpec", deny_unknown_fields), serde(bound = "T: EthSpec, Payload: ExecPayload<T>", deny_unknown_fields),
cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)) cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))
), ),
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"), cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
@ -35,9 +36,9 @@ use tree_hash_derive::TreeHash;
#[derive(Debug, Clone, Serialize, Deserialize, Derivative)] #[derive(Debug, Clone, Serialize, Deserialize, Derivative)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))] #[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(untagged)] #[serde(untagged)]
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec, Payload: ExecPayload<T>")]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub struct BeaconBlockBody<T: EthSpec> { pub struct BeaconBlockBody<T: EthSpec, Payload: ExecPayload<T> = FullPayload<T>> {
pub randao_reveal: Signature, pub randao_reveal: Signature,
pub eth1_data: Eth1Data, pub eth1_data: Eth1Data,
pub graffiti: Graffiti, pub graffiti: Graffiti,
@ -48,8 +49,17 @@ pub struct BeaconBlockBody<T: EthSpec> {
pub voluntary_exits: VariableList<SignedVoluntaryExit, T::MaxVoluntaryExits>, pub voluntary_exits: VariableList<SignedVoluntaryExit, T::MaxVoluntaryExits>,
#[superstruct(only(Altair, Merge))] #[superstruct(only(Altair, Merge))]
pub sync_aggregate: SyncAggregate<T>, pub sync_aggregate: SyncAggregate<T>,
// We flatten the execution payload so that serde can use the name of the inner type,
// either `execution_payload` for full payloads, or `execution_payload_header` for blinded
// payloads.
#[superstruct(only(Merge))] #[superstruct(only(Merge))]
pub execution_payload: ExecutionPayload<T>, #[serde(flatten)]
pub execution_payload: Payload,
#[superstruct(only(Base, Altair))]
#[ssz(skip_serializing, skip_deserializing)]
#[tree_hash(skip_hashing)]
#[serde(skip)]
pub _phantom: PhantomData<Payload>,
} }
impl<'a, T: EthSpec> BeaconBlockBodyRef<'a, T> { impl<'a, T: EthSpec> BeaconBlockBodyRef<'a, T> {

View File

@ -6,7 +6,11 @@ use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash; use tree_hash_derive::TreeHash;
pub type Transaction<T> = VariableList<u8, T>; pub type Transaction<N> = VariableList<u8, N>;
pub type Transactions<T> = VariableList<
Transaction<<T as EthSpec>::MaxBytesPerTransaction>,
<T as EthSpec>::MaxTransactionsPerPayload,
>;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive( #[derive(
@ -36,8 +40,7 @@ pub struct ExecutionPayload<T: EthSpec> {
pub base_fee_per_gas: Uint256, pub base_fee_per_gas: Uint256,
pub block_hash: ExecutionBlockHash, pub block_hash: ExecutionBlockHash,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")] #[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: pub transactions: Transactions<T>,
VariableList<Transaction<T::MaxBytesPerTransaction>, T::MaxTransactionsPerPayload>,
} }
impl<T: EthSpec> ExecutionPayload<T> { impl<T: EthSpec> ExecutionPayload<T> {
@ -50,9 +53,9 @@ impl<T: EthSpec> ExecutionPayload<T> {
pub fn max_execution_payload_size() -> usize { pub fn max_execution_payload_size() -> usize {
// Fixed part // Fixed part
Self::empty().as_ssz_bytes().len() Self::empty().as_ssz_bytes().len()
// Max size of variable length `extra_data` field // Max size of variable length `extra_data` field
+ (T::max_extra_data_bytes() * <u8 as Encode>::ssz_fixed_len()) + (T::max_extra_data_bytes() * <u8 as Encode>::ssz_fixed_len())
// Max size of variable length `transactions` field // Max size of variable length `transactions` field
+ (T::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + T::max_bytes_per_transaction())) + (T::max_transactions_per_payload() * (ssz::BYTES_PER_LENGTH_OFFSET + T::max_bytes_per_transaction()))
} }
} }

View File

@ -1,13 +1,16 @@
use crate::{test_utils::TestRandom, *}; use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash; use tree_hash_derive::TreeHash;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive( #[derive(
Default, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, Default, Debug, Clone, Serialize, Deserialize, Derivative, Encode, Decode, TreeHash, TestRandom,
)] )]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
pub struct ExecutionPayloadHeader<T: EthSpec> { pub struct ExecutionPayloadHeader<T: EthSpec> {
pub parent_hash: ExecutionBlockHash, pub parent_hash: ExecutionBlockHash,
pub fee_recipient: Address, pub fee_recipient: Address,
@ -37,3 +40,24 @@ impl<T: EthSpec> ExecutionPayloadHeader<T> {
Self::default() Self::default()
} }
} }
impl<'a, T: EthSpec> From<&'a ExecutionPayload<T>> for ExecutionPayloadHeader<T> {
fn from(payload: &'a ExecutionPayload<T>) -> Self {
ExecutionPayloadHeader {
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom.clone(),
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data.clone(),
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: payload.transactions.tree_hash_root(),
}
}
}

View File

@ -70,6 +70,7 @@ pub mod config_and_preset;
pub mod fork_context; pub mod fork_context;
pub mod participation_flags; pub mod participation_flags;
pub mod participation_list; pub mod participation_list;
pub mod payload;
pub mod preset; pub mod preset;
pub mod slot_epoch; pub mod slot_epoch;
pub mod subnet_id; pub mod subnet_id;
@ -115,7 +116,7 @@ pub use crate::enr_fork_id::EnrForkId;
pub use crate::eth1_data::Eth1Data; pub use crate::eth1_data::Eth1Data;
pub use crate::eth_spec::EthSpecId; pub use crate::eth_spec::EthSpecId;
pub use crate::execution_block_hash::ExecutionBlockHash; pub use crate::execution_block_hash::ExecutionBlockHash;
pub use crate::execution_payload::{ExecutionPayload, Transaction}; pub use crate::execution_payload::{ExecutionPayload, Transaction, Transactions};
pub use crate::execution_payload_header::ExecutionPayloadHeader; pub use crate::execution_payload_header::ExecutionPayloadHeader;
pub use crate::fork::Fork; pub use crate::fork::Fork;
pub use crate::fork_context::ForkContext; pub use crate::fork_context::ForkContext;
@ -127,6 +128,7 @@ pub use crate::historical_batch::HistoricalBatch;
pub use crate::indexed_attestation::IndexedAttestation; pub use crate::indexed_attestation::IndexedAttestation;
pub use crate::participation_flags::ParticipationFlags; pub use crate::participation_flags::ParticipationFlags;
pub use crate::participation_list::ParticipationList; pub use crate::participation_list::ParticipationList;
pub use crate::payload::{BlindedPayload, BlockType, ExecPayload, FullPayload};
pub use crate::pending_attestation::PendingAttestation; pub use crate::pending_attestation::PendingAttestation;
pub use crate::preset::{AltairPreset, BasePreset, BellatrixPreset}; pub use crate::preset::{AltairPreset, BasePreset, BellatrixPreset};
pub use crate::proposer_preparation_data::ProposerPreparationData; pub use crate::proposer_preparation_data::ProposerPreparationData;

View File

@ -0,0 +1,236 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use ssz::{Decode, DecodeError, Encode};
use std::convert::TryFrom;
use std::fmt::Debug;
use std::hash::Hash;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
pub enum BlockType {
Blinded,
Full,
}
pub trait ExecPayload<T: EthSpec>:
Encode
+ Decode
+ TestRandom
+ TreeHash
+ Default
+ PartialEq
+ Serialize
+ DeserializeOwned
+ Hash
+ TryFrom<ExecutionPayloadHeader<T>>
+ From<ExecutionPayload<T>>
{
fn block_type() -> BlockType;
/// Convert the payload into a payload header.
fn to_execution_payload_header(&self) -> ExecutionPayloadHeader<T>;
// We provide a subset of field accessors, for the fields used in `consensus`.
//
// More fields can be added here if you wish.
fn parent_hash(&self) -> ExecutionBlockHash;
fn prev_randao(&self) -> Hash256;
fn timestamp(&self) -> u64;
fn block_hash(&self) -> ExecutionBlockHash;
}
impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
fn block_type() -> BlockType {
BlockType::Full
}
fn to_execution_payload_header(&self) -> ExecutionPayloadHeader<T> {
ExecutionPayloadHeader::from(&self.execution_payload)
}
fn parent_hash(&self) -> ExecutionBlockHash {
self.execution_payload.parent_hash
}
fn prev_randao(&self) -> Hash256 {
self.execution_payload.prev_randao
}
fn timestamp(&self) -> u64 {
self.execution_payload.timestamp
}
fn block_hash(&self) -> ExecutionBlockHash {
self.execution_payload.block_hash
}
}
impl<T: EthSpec> ExecPayload<T> for BlindedPayload<T> {
fn block_type() -> BlockType {
BlockType::Blinded
}
fn to_execution_payload_header(&self) -> ExecutionPayloadHeader<T> {
self.execution_payload_header.clone()
}
fn parent_hash(&self) -> ExecutionBlockHash {
self.execution_payload_header.parent_hash
}
fn prev_randao(&self) -> Hash256 {
self.execution_payload_header.prev_randao
}
fn timestamp(&self) -> u64 {
self.execution_payload_header.timestamp
}
fn block_hash(&self) -> ExecutionBlockHash {
self.execution_payload_header.block_hash
}
}
#[derive(Default, Debug, Clone, TestRandom, Serialize, Deserialize, Derivative)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(bound = "T: EthSpec")]
pub struct BlindedPayload<T: EthSpec> {
pub execution_payload_header: ExecutionPayloadHeader<T>,
}
impl<T: EthSpec> From<ExecutionPayloadHeader<T>> for BlindedPayload<T> {
fn from(execution_payload_header: ExecutionPayloadHeader<T>) -> Self {
Self {
execution_payload_header,
}
}
}
impl<T: EthSpec> From<BlindedPayload<T>> for ExecutionPayloadHeader<T> {
fn from(blinded: BlindedPayload<T>) -> Self {
blinded.execution_payload_header
}
}
impl<T: EthSpec> From<ExecutionPayload<T>> for BlindedPayload<T> {
fn from(execution_payload: ExecutionPayload<T>) -> Self {
Self {
execution_payload_header: ExecutionPayloadHeader::from(&execution_payload),
}
}
}
impl<T: EthSpec> TreeHash for BlindedPayload<T> {
fn tree_hash_type() -> tree_hash::TreeHashType {
<ExecutionPayloadHeader<T>>::tree_hash_type()
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
self.execution_payload_header.tree_hash_packed_encoding()
}
fn tree_hash_packing_factor() -> usize {
<ExecutionPayloadHeader<T>>::tree_hash_packing_factor()
}
fn tree_hash_root(&self) -> tree_hash::Hash256 {
self.execution_payload_header.tree_hash_root()
}
}
impl<T: EthSpec> Decode for BlindedPayload<T> {
fn is_ssz_fixed_len() -> bool {
<ExecutionPayloadHeader<T> as Decode>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<ExecutionPayloadHeader<T> as Decode>::ssz_fixed_len()
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
Ok(Self {
execution_payload_header: ExecutionPayloadHeader::from_ssz_bytes(bytes)?,
})
}
}
impl<T: EthSpec> Encode for BlindedPayload<T> {
fn is_ssz_fixed_len() -> bool {
<ExecutionPayloadHeader<T> as Encode>::is_ssz_fixed_len()
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
self.execution_payload_header.ssz_append(buf)
}
fn ssz_bytes_len(&self) -> usize {
self.execution_payload_header.ssz_bytes_len()
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, TestRandom, Derivative)]
#[derivative(PartialEq, Hash(bound = "T: EthSpec"))]
#[serde(bound = "T: EthSpec")]
pub struct FullPayload<T: EthSpec> {
pub execution_payload: ExecutionPayload<T>,
}
impl<T: EthSpec> From<ExecutionPayload<T>> for FullPayload<T> {
fn from(execution_payload: ExecutionPayload<T>) -> Self {
Self { execution_payload }
}
}
impl<T: EthSpec> TryFrom<ExecutionPayloadHeader<T>> for FullPayload<T> {
type Error = ();
fn try_from(_: ExecutionPayloadHeader<T>) -> Result<Self, Self::Error> {
Err(())
}
}
impl<T: EthSpec> TreeHash for FullPayload<T> {
fn tree_hash_type() -> tree_hash::TreeHashType {
<ExecutionPayload<T>>::tree_hash_type()
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
self.execution_payload.tree_hash_packed_encoding()
}
fn tree_hash_packing_factor() -> usize {
<ExecutionPayload<T>>::tree_hash_packing_factor()
}
fn tree_hash_root(&self) -> tree_hash::Hash256 {
self.execution_payload.tree_hash_root()
}
}
impl<T: EthSpec> Decode for FullPayload<T> {
fn is_ssz_fixed_len() -> bool {
<ExecutionPayload<T> as Decode>::is_ssz_fixed_len()
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
Ok(FullPayload {
execution_payload: Decode::from_ssz_bytes(bytes)?,
})
}
}
impl<T: EthSpec> Encode for FullPayload<T> {
fn is_ssz_fixed_len() -> bool {
<ExecutionPayload<T> as Encode>::is_ssz_fixed_len()
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
self.execution_payload.ssz_append(buf)
}
fn ssz_bytes_len(&self) -> usize {
self.execution_payload.ssz_bytes_len()
}
}

View File

@ -52,27 +52,27 @@ impl From<SignedBeaconBlockHash> for Hash256 {
), ),
derivative(PartialEq, Hash(bound = "E: EthSpec")), derivative(PartialEq, Hash(bound = "E: EthSpec")),
cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)), cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary)),
serde(bound = "E: EthSpec") serde(bound = "E: EthSpec, Payload: ExecPayload<E>"),
) )
)] )]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative)] #[derive(Debug, Clone, Serialize, Deserialize, Encode, TreeHash, Derivative)]
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))] #[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
#[serde(untagged)] #[serde(untagged)]
#[serde(bound = "E: EthSpec")] #[serde(bound = "E: EthSpec, Payload: ExecPayload<E>")]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[tree_hash(enum_behaviour = "transparent")] #[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")] #[ssz(enum_behaviour = "transparent")]
pub struct SignedBeaconBlock<E: EthSpec> { pub struct SignedBeaconBlock<E: EthSpec, Payload: ExecPayload<E> = FullPayload<E>> {
#[superstruct(only(Base), partial_getter(rename = "message_base"))] #[superstruct(only(Base), partial_getter(rename = "message_base"))]
pub message: BeaconBlockBase<E>, pub message: BeaconBlockBase<E, Payload>,
#[superstruct(only(Altair), partial_getter(rename = "message_altair"))] #[superstruct(only(Altair), partial_getter(rename = "message_altair"))]
pub message: BeaconBlockAltair<E>, pub message: BeaconBlockAltair<E, Payload>,
#[superstruct(only(Merge), partial_getter(rename = "message_merge"))] #[superstruct(only(Merge), partial_getter(rename = "message_merge"))]
pub message: BeaconBlockMerge<E>, pub message: BeaconBlockMerge<E, Payload>,
pub signature: Signature, pub signature: Signature,
} }
impl<E: EthSpec> SignedBeaconBlock<E> { impl<E: EthSpec, Payload: ExecPayload<E>> SignedBeaconBlock<E, Payload> {
/// Returns the name of the fork pertaining to `self`. /// Returns the name of the fork pertaining to `self`.
/// ///
/// Will return an `Err` if `self` has been instantiated to a variant conflicting with the fork /// Will return an `Err` if `self` has been instantiated to a variant conflicting with the fork
@ -94,7 +94,7 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
/// SSZ decode with custom decode function. /// SSZ decode with custom decode function.
pub fn from_ssz_bytes_with( pub fn from_ssz_bytes_with(
bytes: &[u8], bytes: &[u8],
block_decoder: impl FnOnce(&[u8]) -> Result<BeaconBlock<E>, ssz::DecodeError>, block_decoder: impl FnOnce(&[u8]) -> Result<BeaconBlock<E, Payload>, ssz::DecodeError>,
) -> Result<Self, ssz::DecodeError> { ) -> Result<Self, ssz::DecodeError> {
// We need the customer decoder for `BeaconBlock`, which doesn't compose with the other // We need the customer decoder for `BeaconBlock`, which doesn't compose with the other
// SSZ utils, so we duplicate some parts of `ssz_derive` here. // SSZ utils, so we duplicate some parts of `ssz_derive` here.
@ -113,7 +113,7 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
} }
/// Create a new `SignedBeaconBlock` from a `BeaconBlock` and `Signature`. /// Create a new `SignedBeaconBlock` from a `BeaconBlock` and `Signature`.
pub fn from_block(block: BeaconBlock<E>, signature: Signature) -> Self { pub fn from_block(block: BeaconBlock<E, Payload>, signature: Signature) -> Self {
match block { match block {
BeaconBlock::Base(message) => { BeaconBlock::Base(message) => {
SignedBeaconBlock::Base(SignedBeaconBlockBase { message, signature }) SignedBeaconBlock::Base(SignedBeaconBlockBase { message, signature })
@ -131,7 +131,7 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
/// ///
/// This is necessary to get a `&BeaconBlock` from a `SignedBeaconBlock` because /// This is necessary to get a `&BeaconBlock` from a `SignedBeaconBlock` because
/// `SignedBeaconBlock` only contains a `BeaconBlock` _variant_. /// `SignedBeaconBlock` only contains a `BeaconBlock` _variant_.
pub fn deconstruct(self) -> (BeaconBlock<E>, Signature) { pub fn deconstruct(self) -> (BeaconBlock<E, Payload>, Signature) {
match self { match self {
SignedBeaconBlock::Base(block) => (BeaconBlock::Base(block.message), block.signature), SignedBeaconBlock::Base(block) => (BeaconBlock::Base(block.message), block.signature),
SignedBeaconBlock::Altair(block) => { SignedBeaconBlock::Altair(block) => {
@ -142,7 +142,7 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
} }
/// Accessor for the block's `message` field as a ref. /// Accessor for the block's `message` field as a ref.
pub fn message(&self) -> BeaconBlockRef<'_, E> { pub fn message(&self) -> BeaconBlockRef<'_, E, Payload> {
match self { match self {
SignedBeaconBlock::Base(inner) => BeaconBlockRef::Base(&inner.message), SignedBeaconBlock::Base(inner) => BeaconBlockRef::Base(&inner.message),
SignedBeaconBlock::Altair(inner) => BeaconBlockRef::Altair(&inner.message), SignedBeaconBlock::Altair(inner) => BeaconBlockRef::Altair(&inner.message),
@ -151,7 +151,7 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
} }
/// Accessor for the block's `message` as a mutable reference (for testing only). /// Accessor for the block's `message` as a mutable reference (for testing only).
pub fn message_mut(&mut self) -> BeaconBlockRefMut<'_, E> { pub fn message_mut(&mut self) -> BeaconBlockRefMut<'_, E, Payload> {
match self { match self {
SignedBeaconBlock::Base(inner) => BeaconBlockRefMut::Base(&mut inner.message), SignedBeaconBlock::Base(inner) => BeaconBlockRefMut::Base(&mut inner.message),
SignedBeaconBlock::Altair(inner) => BeaconBlockRefMut::Altair(&mut inner.message), SignedBeaconBlock::Altair(inner) => BeaconBlockRefMut::Altair(&mut inner.message),

View File

@ -3,6 +3,7 @@ use rand::RngCore;
use rand::SeedableRng; use rand::SeedableRng;
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use ssz_types::typenum::Unsigned; use ssz_types::typenum::Unsigned;
use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
mod address; mod address;
@ -25,6 +26,12 @@ pub trait TestRandom {
fn random_for_test(rng: &mut impl RngCore) -> Self; fn random_for_test(rng: &mut impl RngCore) -> Self;
} }
impl<T> TestRandom for PhantomData<T> {
fn random_for_test(_rng: &mut impl RngCore) -> Self {
PhantomData::default()
}
}
impl TestRandom for bool { impl TestRandom for bool {
fn random_for_test(rng: &mut impl RngCore) -> Self { fn random_for_test(rng: &mut impl RngCore) -> Self {
(rng.next_u32() % 2) == 1 (rng.next_u32() % 2) == 1

View File

@ -17,8 +17,8 @@ use state_processing::per_block_processing::{
use std::fmt::Debug; use std::fmt::Debug;
use std::path::Path; use std::path::Path;
use types::{ use types::{
Attestation, AttesterSlashing, BeaconBlock, BeaconState, ChainSpec, Deposit, EthSpec, Attestation, AttesterSlashing, BeaconBlock, BeaconState, ChainSpec, Deposit, EthSpec, ForkName,
ExecutionPayload, ForkName, ProposerSlashing, SignedVoluntaryExit, SyncAggregate, FullPayload, ProposerSlashing, SignedVoluntaryExit, SyncAggregate,
}; };
#[derive(Debug, Clone, Default, Deserialize)] #[derive(Debug, Clone, Default, Deserialize)]
@ -183,7 +183,12 @@ impl<E: EthSpec> Operation<E> for BeaconBlock<E> {
spec: &ChainSpec, spec: &ChainSpec,
_: &Operations<E, Self>, _: &Operations<E, Self>,
) -> Result<(), BlockProcessingError> { ) -> Result<(), BlockProcessingError> {
process_block_header(state, self.to_ref(), VerifyBlockRoot::True, spec)?; process_block_header(
state,
self.to_ref().temporary_block_header(),
VerifyBlockRoot::True,
spec,
)?;
Ok(()) Ok(())
} }
} }
@ -216,7 +221,7 @@ impl<E: EthSpec> Operation<E> for SyncAggregate<E> {
} }
} }
impl<E: EthSpec> Operation<E> for ExecutionPayload<E> { impl<E: EthSpec> Operation<E> for FullPayload<E> {
fn handler_name() -> String { fn handler_name() -> String {
"execution_payload".into() "execution_payload".into()
} }

View File

@ -54,6 +54,7 @@ type_name!(DepositData);
type_name!(DepositMessage); type_name!(DepositMessage);
type_name!(Eth1Data); type_name!(Eth1Data);
type_name_generic!(ExecutionPayload); type_name_generic!(ExecutionPayload);
type_name_generic!(FullPayload, "ExecutionPayload");
type_name_generic!(ExecutionPayloadHeader); type_name_generic!(ExecutionPayloadHeader);
type_name!(Fork); type_name!(Fork);
type_name!(ForkData); type_name!(ForkData);

View File

@ -72,8 +72,8 @@ fn operations_sync_aggregate() {
#[test] #[test]
fn operations_execution_payload() { fn operations_execution_payload() {
OperationsHandler::<MinimalEthSpec, ExecutionPayload<_>>::default().run(); OperationsHandler::<MinimalEthSpec, FullPayload<_>>::default().run();
OperationsHandler::<MainnetEthSpec, ExecutionPayload<_>>::default().run(); OperationsHandler::<MainnetEthSpec, FullPayload<_>>::default().run();
} }
#[test] #[test]

View File

@ -5,7 +5,8 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use task_executor::TaskExecutor; use task_executor::TaskExecutor;
use tokio::time::sleep; use tokio::time::sleep;
use types::{ use types::{
Address, ChainSpec, EthSpec, ExecutionBlockHash, Hash256, MainnetEthSpec, Slot, Uint256, Address, ChainSpec, EthSpec, ExecutionBlockHash, FullPayload, Hash256, MainnetEthSpec, Slot,
Uint256,
}; };
const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(10); const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(10);
@ -171,7 +172,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let valid_payload = self let valid_payload = self
.ee_a .ee_a
.execution_layer .execution_layer
.get_payload::<MainnetEthSpec>( .get_payload::<MainnetEthSpec, FullPayload<MainnetEthSpec>>(
parent_hash, parent_hash,
timestamp, timestamp,
prev_randao, prev_randao,
@ -179,7 +180,8 @@ impl<E: GenericExecutionEngine> TestRig<E> {
proposer_index, proposer_index,
) )
.await .await
.unwrap(); .unwrap()
.execution_payload;
/* /*
* Execution Engine A: * Execution Engine A:
@ -262,7 +264,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let second_payload = self let second_payload = self
.ee_a .ee_a
.execution_layer .execution_layer
.get_payload::<MainnetEthSpec>( .get_payload::<MainnetEthSpec, FullPayload<MainnetEthSpec>>(
parent_hash, parent_hash,
timestamp, timestamp,
prev_randao, prev_randao,
@ -270,7 +272,8 @@ impl<E: GenericExecutionEngine> TestRig<E> {
proposer_index, proposer_index,
) )
.await .await
.unwrap(); .unwrap()
.execution_payload;
/* /*
* Execution Engine A: * Execution Engine A:

View File

@ -1,3 +1,4 @@
use crate::beacon_node_fallback::{AllErrored, Error as FallbackError};
use crate::{ use crate::{
beacon_node_fallback::{BeaconNodeFallback, RequireSynced}, beacon_node_fallback::{BeaconNodeFallback, RequireSynced},
graffiti_file::GraffitiFile, graffiti_file::GraffitiFile,
@ -10,7 +11,30 @@ use slot_clock::SlotClock;
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use types::{EthSpec, PublicKeyBytes, Slot}; use types::{
BlindedPayload, BlockType, Epoch, EthSpec, ExecPayload, FullPayload, PublicKeyBytes, Slot,
};
#[derive(Debug)]
pub enum BlockError {
Recoverable(String),
Irrecoverable(String),
}
impl From<AllErrored<BlockError>> for BlockError {
fn from(e: AllErrored<BlockError>) -> Self {
if e.0.iter().any(|(_, error)| {
matches!(
error,
FallbackError::RequestFailed(BlockError::Irrecoverable(_))
)
}) {
BlockError::Irrecoverable(e.to_string())
} else {
BlockError::Recoverable(e.to_string())
}
}
}
/// Builds a `BlockService`. /// Builds a `BlockService`.
pub struct BlockServiceBuilder<T, E: EthSpec> { pub struct BlockServiceBuilder<T, E: EthSpec> {
@ -20,6 +44,7 @@ pub struct BlockServiceBuilder<T, E: EthSpec> {
context: Option<RuntimeContext<E>>, context: Option<RuntimeContext<E>>,
graffiti: Option<Graffiti>, graffiti: Option<Graffiti>,
graffiti_file: Option<GraffitiFile>, graffiti_file: Option<GraffitiFile>,
private_tx_proposals: bool,
} }
impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> { impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
@ -31,6 +56,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
context: None, context: None,
graffiti: None, graffiti: None,
graffiti_file: None, graffiti_file: None,
private_tx_proposals: false,
} }
} }
@ -64,6 +90,11 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
self self
} }
pub fn private_tx_proposals(mut self, private_tx_proposals: bool) -> Self {
self.private_tx_proposals = private_tx_proposals;
self
}
pub fn build(self) -> Result<BlockService<T, E>, String> { pub fn build(self) -> Result<BlockService<T, E>, String> {
Ok(BlockService { Ok(BlockService {
inner: Arc::new(Inner { inner: Arc::new(Inner {
@ -81,6 +112,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
.ok_or("Cannot build BlockService without runtime_context")?, .ok_or("Cannot build BlockService without runtime_context")?,
graffiti: self.graffiti, graffiti: self.graffiti,
graffiti_file: self.graffiti_file, graffiti_file: self.graffiti_file,
private_tx_proposals: self.private_tx_proposals,
}), }),
}) })
} }
@ -94,6 +126,7 @@ pub struct Inner<T, E: EthSpec> {
context: RuntimeContext<E>, context: RuntimeContext<E>,
graffiti: Option<Graffiti>, graffiti: Option<Graffiti>,
graffiti_file: Option<GraffitiFile>, graffiti_file: Option<GraffitiFile>,
private_tx_proposals: bool,
} }
/// Attempts to produce attestations for any block producer(s) at the start of the epoch. /// Attempts to produce attestations for any block producer(s) at the start of the epoch.
@ -202,16 +235,46 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
) )
} }
let private_tx_proposals = self.private_tx_proposals;
let merge_slot = self
.context
.eth2_config
.spec
.bellatrix_fork_epoch
.unwrap_or_else(Epoch::max_value)
.start_slot(E::slots_per_epoch());
for validator_pubkey in proposers { for validator_pubkey in proposers {
let service = self.clone(); let service = self.clone();
let log = log.clone(); let log = log.clone();
self.inner.context.executor.spawn( self.inner.context.executor.spawn(
async move { async move {
if let Err(e) = service.publish_block(slot, validator_pubkey).await { let publish_result = if private_tx_proposals && slot >= merge_slot {
let mut result = service.clone()
.publish_block::<BlindedPayload<E>>(slot, validator_pubkey)
.await;
match result.as_ref() {
Err(BlockError::Recoverable(e)) => {
error!(log, "Error whilst producing a blinded block, attempting to publish full block"; "error" => ?e);
result = service
.publish_block::<FullPayload<E>>(slot, validator_pubkey)
.await;
},
Err(BlockError::Irrecoverable(e)) => {
error!(log, "Error whilst producing a blinded block, cannot fallback because block was signed"; "error" => ?e);
},
_ => {},
};
result
} else {
service
.publish_block::<FullPayload<E>>(slot, validator_pubkey)
.await
};
if let Err(e) = publish_result {
crit!( crit!(
log, log,
"Error whilst producing block"; "Error whilst producing block";
"message" => e "message" => ?e
); );
} }
}, },
@ -223,25 +286,29 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
} }
/// Produce a block at the given slot for validator_pubkey /// Produce a block at the given slot for validator_pubkey
async fn publish_block( async fn publish_block<Payload: ExecPayload<E>>(
self, self,
slot: Slot, slot: Slot,
validator_pubkey: PublicKeyBytes, validator_pubkey: PublicKeyBytes,
) -> Result<(), String> { ) -> Result<(), BlockError> {
let log = self.context.log(); let log = self.context.log();
let _timer = let _timer =
metrics::start_timer_vec(&metrics::BLOCK_SERVICE_TIMES, &[metrics::BEACON_BLOCK]); metrics::start_timer_vec(&metrics::BLOCK_SERVICE_TIMES, &[metrics::BEACON_BLOCK]);
let current_slot = self let current_slot = self.slot_clock.now().ok_or_else(|| {
.slot_clock BlockError::Recoverable("Unable to determine current slot from clock".to_string())
.now() })?;
.ok_or("Unable to determine current slot from clock")?;
let randao_reveal = self let randao_reveal = self
.validator_store .validator_store
.randao_reveal(validator_pubkey, slot.epoch(E::slots_per_epoch())) .randao_reveal(validator_pubkey, slot.epoch(E::slots_per_epoch()))
.await .await
.map_err(|e| format!("Unable to produce randao reveal signature: {:?}", e))? .map_err(|e| {
BlockError::Recoverable(format!(
"Unable to produce randao reveal signature: {:?}",
e
))
})?
.into(); .into();
let graffiti = self let graffiti = self
@ -268,41 +335,86 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
&metrics::BLOCK_SERVICE_TIMES, &metrics::BLOCK_SERVICE_TIMES,
&[metrics::BEACON_BLOCK_HTTP_GET], &[metrics::BEACON_BLOCK_HTTP_GET],
); );
let block = beacon_node let block = match Payload::block_type() {
.get_validator_blocks(slot, randao_reveal_ref, graffiti.as_ref()) BlockType::Full => {
.await beacon_node
.map_err(|e| format!("Error from beacon node when producing block: {:?}", e))? .get_validator_blocks::<E, Payload>(
.data; slot,
randao_reveal_ref,
graffiti.as_ref(),
)
.await
.map_err(|e| {
BlockError::Recoverable(format!(
"Error from beacon node when producing block: {:?}",
e
))
})?
.data
}
BlockType::Blinded => {
beacon_node
.get_validator_blinded_blocks::<E, Payload>(
slot,
randao_reveal_ref,
graffiti.as_ref(),
)
.await
.map_err(|e| {
BlockError::Recoverable(format!(
"Error from beacon node when producing block: {:?}",
e
))
})?
.data
}
};
drop(get_timer); drop(get_timer);
if proposer_index != Some(block.proposer_index()) { if proposer_index != Some(block.proposer_index()) {
return Err( return Err(BlockError::Recoverable(
"Proposer index does not match block proposer. Beacon chain re-orged" "Proposer index does not match block proposer. Beacon chain re-orged"
.to_string(), .to_string(),
); ));
} }
let signed_block = self_ref let signed_block = self_ref
.validator_store .validator_store
.sign_block(*validator_pubkey_ref, block, current_slot) .sign_block::<Payload>(*validator_pubkey_ref, block, current_slot)
.await .await
.map_err(|e| format!("Unable to sign block: {:?}", e))?; .map_err(|e| {
BlockError::Recoverable(format!("Unable to sign block: {:?}", e))
})?;
let _post_timer = metrics::start_timer_vec( let _post_timer = metrics::start_timer_vec(
&metrics::BLOCK_SERVICE_TIMES, &metrics::BLOCK_SERVICE_TIMES,
&[metrics::BEACON_BLOCK_HTTP_POST], &[metrics::BEACON_BLOCK_HTTP_POST],
); );
beacon_node
.post_beacon_blocks(&signed_block)
.await
.map_err(|e| {
format!("Error from beacon node when publishing block: {:?}", e)
})?;
Ok::<_, String>(signed_block) match Payload::block_type() {
BlockType::Full => beacon_node
.post_beacon_blocks(&signed_block)
.await
.map_err(|e| {
BlockError::Irrecoverable(format!(
"Error from beacon node when publishing block: {:?}",
e
))
})?,
BlockType::Blinded => beacon_node
.post_beacon_blinded_blocks(&signed_block)
.await
.map_err(|e| {
BlockError::Irrecoverable(format!(
"Error from beacon node when publishing block: {:?}",
e
))
})?,
}
Ok::<_, BlockError>(signed_block)
}) })
.await .await?;
.map_err(|e| e.to_string())?;
info!( info!(
log, log,

View File

@ -258,4 +258,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
immediately.") immediately.")
.takes_value(false), .takes_value(false),
) )
.arg(
Arg::with_name("private-tx-proposals")
.long("private-tx-proposals")
.help("If this flag is set, Lighthouse will query the Beacon Node for only block \
headers during proposals and will sign over headers. Useful for outsourcing \
execution payload construction during proposals.")
.takes_value(false),
)
} }

View File

@ -55,6 +55,7 @@ pub struct Config {
/// If true, enable functionality that monitors the network for attestations or proposals from /// If true, enable functionality that monitors the network for attestations or proposals from
/// any of the validators managed by this client before starting up. /// any of the validators managed by this client before starting up.
pub enable_doppelganger_protection: bool, pub enable_doppelganger_protection: bool,
pub private_tx_proposals: bool,
/// A list of custom certificates that the validator client will additionally use when /// A list of custom certificates that the validator client will additionally use when
/// connecting to a beacon node over SSL/TLS. /// connecting to a beacon node over SSL/TLS.
pub beacon_nodes_tls_certs: Option<Vec<PathBuf>>, pub beacon_nodes_tls_certs: Option<Vec<PathBuf>>,
@ -91,6 +92,7 @@ impl Default for Config {
monitoring_api: None, monitoring_api: None,
enable_doppelganger_protection: false, enable_doppelganger_protection: false,
beacon_nodes_tls_certs: None, beacon_nodes_tls_certs: None,
private_tx_proposals: false,
} }
} }
} }
@ -306,6 +308,10 @@ impl Config {
config.enable_doppelganger_protection = true; config.enable_doppelganger_protection = true;
} }
if cli_args.is_present("private-tx-proposals") {
config.private_tx_proposals = true;
}
Ok(config) Ok(config)
} }
} }

View File

@ -400,6 +400,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
.runtime_context(context.service_context("block".into())) .runtime_context(context.service_context("block".into()))
.graffiti(config.graffiti) .graffiti(config.graffiti)
.graffiti_file(config.graffiti_file.clone()) .graffiti_file(config.graffiti_file.clone())
.private_tx_proposals(config.private_tx_proposals)
.build()?; .build()?;
let attestation_service = AttestationServiceBuilder::new() let attestation_service = AttestationServiceBuilder::new()

View File

@ -33,9 +33,9 @@ pub enum Error {
} }
/// Enumerates all messages that can be signed by a validator. /// Enumerates all messages that can be signed by a validator.
pub enum SignableMessage<'a, T: EthSpec> { pub enum SignableMessage<'a, T: EthSpec, Payload: ExecPayload<T> = FullPayload<T>> {
RandaoReveal(Epoch), RandaoReveal(Epoch),
BeaconBlock(&'a BeaconBlock<T>), BeaconBlock(&'a BeaconBlock<T, Payload>),
AttestationData(&'a AttestationData), AttestationData(&'a AttestationData),
SignedAggregateAndProof(&'a AggregateAndProof<T>), SignedAggregateAndProof(&'a AggregateAndProof<T>),
SelectionProof(Slot), SelectionProof(Slot),
@ -47,7 +47,7 @@ pub enum SignableMessage<'a, T: EthSpec> {
SignedContributionAndProof(&'a ContributionAndProof<T>), SignedContributionAndProof(&'a ContributionAndProof<T>),
} }
impl<'a, T: EthSpec> SignableMessage<'a, T> { impl<'a, T: EthSpec, Payload: ExecPayload<T>> SignableMessage<'a, T, Payload> {
/// Returns the `SignedRoot` for the contained message. /// Returns the `SignedRoot` for the contained message.
/// ///
/// The actual `SignedRoot` trait is not used since it also requires a `TreeHash` impl, which is /// The actual `SignedRoot` trait is not used since it also requires a `TreeHash` impl, which is
@ -113,9 +113,9 @@ impl SigningContext {
impl SigningMethod { impl SigningMethod {
/// Return the signature of `signable_message`, with respect to the `signing_context`. /// Return the signature of `signable_message`, with respect to the `signing_context`.
pub async fn get_signature<T: EthSpec>( pub async fn get_signature<T: EthSpec, Payload: ExecPayload<T>>(
&self, &self,
signable_message: SignableMessage<'_, T>, signable_message: SignableMessage<'_, T, Payload>,
signing_context: SigningContext, signing_context: SigningContext,
spec: &ChainSpec, spec: &ChainSpec,
executor: &TaskExecutor, executor: &TaskExecutor,

View File

@ -34,7 +34,7 @@ pub struct ForkInfo {
#[derive(Debug, PartialEq, Serialize)] #[derive(Debug, PartialEq, Serialize)]
#[serde(bound = "T: EthSpec", rename_all = "snake_case")] #[serde(bound = "T: EthSpec", rename_all = "snake_case")]
pub enum Web3SignerObject<'a, T: EthSpec> { pub enum Web3SignerObject<'a, T: EthSpec, Payload: ExecPayload<T>> {
AggregationSlot { AggregationSlot {
slot: Slot, slot: Slot,
}, },
@ -42,7 +42,7 @@ pub enum Web3SignerObject<'a, T: EthSpec> {
Attestation(&'a AttestationData), Attestation(&'a AttestationData),
BeaconBlock { BeaconBlock {
version: ForkName, version: ForkName,
block: &'a BeaconBlock<T>, block: &'a BeaconBlock<T, Payload>,
}, },
#[allow(dead_code)] #[allow(dead_code)]
Deposit { Deposit {
@ -66,8 +66,8 @@ pub enum Web3SignerObject<'a, T: EthSpec> {
ContributionAndProof(&'a ContributionAndProof<T>), ContributionAndProof(&'a ContributionAndProof<T>),
} }
impl<'a, T: EthSpec> Web3SignerObject<'a, T> { impl<'a, T: EthSpec, Payload: ExecPayload<T>> Web3SignerObject<'a, T, Payload> {
pub fn beacon_block(block: &'a BeaconBlock<T>) -> Result<Self, Error> { pub fn beacon_block(block: &'a BeaconBlock<T, Payload>) -> Result<Self, Error> {
let version = match block { let version = match block {
BeaconBlock::Base(_) => ForkName::Phase0, BeaconBlock::Base(_) => ForkName::Phase0,
BeaconBlock::Altair(_) => ForkName::Altair, BeaconBlock::Altair(_) => ForkName::Altair,
@ -99,7 +99,7 @@ impl<'a, T: EthSpec> Web3SignerObject<'a, T> {
#[derive(Debug, PartialEq, Serialize)] #[derive(Debug, PartialEq, Serialize)]
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec")]
pub struct SigningRequest<'a, T: EthSpec> { pub struct SigningRequest<'a, T: EthSpec, Payload: ExecPayload<T>> {
#[serde(rename = "type")] #[serde(rename = "type")]
pub message_type: MessageType, pub message_type: MessageType,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@ -107,7 +107,7 @@ pub struct SigningRequest<'a, T: EthSpec> {
#[serde(rename = "signingRoot")] #[serde(rename = "signingRoot")]
pub signing_root: Hash256, pub signing_root: Hash256,
#[serde(flatten)] #[serde(flatten)]
pub object: Web3SignerObject<'a, T>, pub object: Web3SignerObject<'a, T, Payload>,
} }
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize)]

View File

@ -18,10 +18,11 @@ use std::sync::Arc;
use task_executor::TaskExecutor; use task_executor::TaskExecutor;
use types::{ use types::{
attestation::Error as AttestationError, graffiti::GraffitiString, Address, AggregateAndProof, attestation::Error as AttestationError, graffiti::GraffitiString, Address, AggregateAndProof,
Attestation, BeaconBlock, ChainSpec, ContributionAndProof, Domain, Epoch, EthSpec, Fork, Attestation, BeaconBlock, BlindedPayload, ChainSpec, ContributionAndProof, Domain, Epoch,
Graffiti, Hash256, Keypair, PublicKeyBytes, SelectionProof, Signature, SignedAggregateAndProof, EthSpec, ExecPayload, Fork, Graffiti, Hash256, Keypair, PublicKeyBytes, SelectionProof,
SignedBeaconBlock, SignedContributionAndProof, Slot, SyncAggregatorSelectionData, Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof, Slot,
SyncCommitteeContribution, SyncCommitteeMessage, SyncSelectionProof, SyncSubnetId, SyncAggregatorSelectionData, SyncCommitteeContribution, SyncCommitteeMessage,
SyncSelectionProof, SyncSubnetId,
}; };
use validator_dir::ValidatorDir; use validator_dir::ValidatorDir;
@ -338,7 +339,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
let signing_context = self.signing_context(Domain::Randao, signing_epoch); let signing_context = self.signing_context(Domain::Randao, signing_epoch);
let signature = signing_method let signature = signing_method
.get_signature::<E>( .get_signature::<E, BlindedPayload<E>>(
SignableMessage::RandaoReveal(signing_epoch), SignableMessage::RandaoReveal(signing_epoch),
signing_context, signing_context,
&self.spec, &self.spec,
@ -359,12 +360,12 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
.suggested_fee_recipient(validator_pubkey) .suggested_fee_recipient(validator_pubkey)
} }
pub async fn sign_block( pub async fn sign_block<Payload: ExecPayload<E>>(
&self, &self,
validator_pubkey: PublicKeyBytes, validator_pubkey: PublicKeyBytes,
block: BeaconBlock<E>, block: BeaconBlock<E, Payload>,
current_slot: Slot, current_slot: Slot,
) -> Result<SignedBeaconBlock<E>, Error> { ) -> Result<SignedBeaconBlock<E, Payload>, Error> {
// Make sure the block slot is not higher than the current slot to avoid potential attacks. // Make sure the block slot is not higher than the current slot to avoid potential attacks.
if block.slot() > current_slot { if block.slot() > current_slot {
warn!( warn!(
@ -397,7 +398,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?; let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
let signature = signing_method let signature = signing_method
.get_signature( .get_signature::<E, Payload>(
SignableMessage::BeaconBlock(&block), SignableMessage::BeaconBlock(&block),
signing_context, signing_context,
&self.spec, &self.spec,
@ -466,7 +467,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
Ok(Safe::Valid) => { Ok(Safe::Valid) => {
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?; let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
let signature = signing_method let signature = signing_method
.get_signature::<E>( .get_signature::<E, BlindedPayload<E>>(
SignableMessage::AttestationData(&attestation.data), SignableMessage::AttestationData(&attestation.data),
signing_context, signing_context,
&self.spec, &self.spec,
@ -543,7 +544,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?; let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
let signature = signing_method let signature = signing_method
.get_signature( .get_signature::<E, BlindedPayload<E>>(
SignableMessage::SignedAggregateAndProof(&message), SignableMessage::SignedAggregateAndProof(&message),
signing_context, signing_context,
&self.spec, &self.spec,
@ -576,7 +577,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
let signing_method = self.doppelganger_bypassed_signing_method(validator_pubkey)?; let signing_method = self.doppelganger_bypassed_signing_method(validator_pubkey)?;
let signature = signing_method let signature = signing_method
.get_signature::<E>( .get_signature::<E, BlindedPayload<E>>(
SignableMessage::SelectionProof(slot), SignableMessage::SelectionProof(slot),
signing_context, signing_context,
&self.spec, &self.spec,
@ -615,7 +616,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
}; };
let signature = signing_method let signature = signing_method
.get_signature::<E>( .get_signature::<E, BlindedPayload<E>>(
SignableMessage::SyncSelectionProof(&message), SignableMessage::SyncSelectionProof(&message),
signing_context, signing_context,
&self.spec, &self.spec,
@ -641,7 +642,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
let signing_method = self.doppelganger_bypassed_signing_method(*validator_pubkey)?; let signing_method = self.doppelganger_bypassed_signing_method(*validator_pubkey)?;
let signature = signing_method let signature = signing_method
.get_signature::<E>( .get_signature::<E, BlindedPayload<E>>(
SignableMessage::SyncCommitteeSignature { SignableMessage::SyncCommitteeSignature {
beacon_block_root, beacon_block_root,
slot, slot,
@ -686,7 +687,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
}; };
let signature = signing_method let signature = signing_method
.get_signature( .get_signature::<E, BlindedPayload<E>>(
SignableMessage::SignedContributionAndProof(&message), SignableMessage::SignedContributionAndProof(&message),
signing_context, signing_context,
&self.spec, &self.spec,