diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3d23b4110e7..476b3cf1798 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,38 +31,22 @@ jobs: strategy: matrix: arch: [aarch64-unknown-linux-gnu, - aarch64-unknown-linux-gnu-portable, x86_64-unknown-linux-gnu, - x86_64-unknown-linux-gnu-portable, x86_64-apple-darwin, - x86_64-apple-darwin-portable, - x86_64-windows, - x86_64-windows-portable] + x86_64-windows] include: - arch: aarch64-unknown-linux-gnu runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "release", "large"]') || 'ubuntu-latest' }} profile: maxperf - - arch: aarch64-unknown-linux-gnu-portable - runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "release", "large"]') || 'ubuntu-latest' }} - profile: maxperf - arch: x86_64-unknown-linux-gnu runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "release", "large"]') || 'ubuntu-latest' }} profile: maxperf - - arch: x86_64-unknown-linux-gnu-portable - runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "release", "large"]') || 'ubuntu-latest' }} - profile: maxperf - arch: x86_64-apple-darwin - runner: macos-latest - profile: maxperf - - arch: x86_64-apple-darwin-portable - runner: macos-latest + runner: macos-13 profile: maxperf - arch: x86_64-windows runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "windows", "release"]') || 'windows-2019' }} profile: maxperf - - arch: x86_64-windows-portable - runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "windows", "release"]') || 'windows-2019' }} - profile: maxperf runs-on: ${{ matrix.runner }} needs: extract-version @@ -90,53 +74,29 @@ jobs: # Builds # ============================== - - name: Build Lighthouse for aarch64-unknown-linux-gnu-portable - if: matrix.arch == 'aarch64-unknown-linux-gnu-portable' - run: | - cargo install cross - env CROSS_PROFILE=${{ matrix.profile }} make build-aarch64-portable - - name: Build Lighthouse for aarch64-unknown-linux-gnu if: matrix.arch == 'aarch64-unknown-linux-gnu' run: | cargo install cross - env CROSS_PROFILE=${{ matrix.profile }} make build-aarch64 - - - name: Build Lighthouse for x86_64-unknown-linux-gnu-portable - if: matrix.arch == 'x86_64-unknown-linux-gnu-portable' - run: | - cargo install cross - env CROSS_PROFILE=${{ matrix.profile }} make build-x86_64-portable + env CROSS_PROFILE=${{ matrix.profile }} make build-aarch64-portable - name: Build Lighthouse for x86_64-unknown-linux-gnu if: matrix.arch == 'x86_64-unknown-linux-gnu' run: | cargo install cross - env CROSS_PROFILE=${{ matrix.profile }} make build-x86_64 - - - name: Move cross-compiled binary - if: startsWith(matrix.arch, 'aarch64') - run: mv target/aarch64-unknown-linux-gnu/${{ matrix.profile }}/lighthouse ~/.cargo/bin/lighthouse + env CROSS_PROFILE=${{ matrix.profile }} make build-x86_64-portable - name: Move cross-compiled binary - if: startsWith(matrix.arch, 'x86_64-unknown-linux-gnu') - run: mv target/x86_64-unknown-linux-gnu/${{ matrix.profile }}/lighthouse ~/.cargo/bin/lighthouse - - - name: Build Lighthouse for x86_64-apple-darwin portable - if: matrix.arch == 'x86_64-apple-darwin-portable' - run: cargo install --path lighthouse --force --locked --features portable,gnosis --profile ${{ matrix.profile }} + if: contains(matrix.arch, 'unknown-linux-gnu') + run: mv target/${{ matrix.arch }}/${{ matrix.profile }}/lighthouse ~/.cargo/bin/lighthouse - - name: Build Lighthouse for x86_64-apple-darwin modern + - name: Build Lighthouse for x86_64-apple-darwin if: matrix.arch == 'x86_64-apple-darwin' - run: cargo install --path lighthouse --force --locked --features modern,gnosis --profile ${{ matrix.profile }} - - - name: Build Lighthouse for Windows portable - if: matrix.arch == 'x86_64-windows-portable' run: cargo install --path lighthouse --force --locked --features portable,gnosis --profile ${{ matrix.profile }} - - name: Build Lighthouse for Windows modern + - name: Build Lighthouse for Windows if: matrix.arch == 'x86_64-windows' - run: cargo install --path lighthouse --force --locked --features modern,gnosis --profile ${{ matrix.profile }} + run: cargo install --path lighthouse --force --locked --features portable,gnosis --profile ${{ matrix.profile }} - name: Configure GPG and create artifacts if: startsWith(matrix.arch, 'x86_64-windows') != true @@ -151,6 +111,11 @@ jobs: cd artifacts tar -czf lighthouse-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.arch }}.tar.gz lighthouse echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab lighthouse-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.arch }}.tar.gz + for ext in "tar.gz" "tar.gz.asc";\ + do for f in *.$ext;\ + do cp $f "../${f%.$ext}-portable.$ext";\ + done;\ + done mv *tar.gz* .. - name: Configure GPG and create artifacts Windows @@ -179,6 +144,14 @@ jobs: path: lighthouse-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.arch }}.tar.gz compression-level: 0 + - name: Upload artifact (copy) + if: startsWith(matrix.arch, 'x86_64-windows') != true + uses: actions/upload-artifact@v4 + with: + name: lighthouse-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.arch }}-portable.tar.gz + path: lighthouse-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.arch }}-portable.tar.gz + compression-level: 0 + - name: Upload signature uses: actions/upload-artifact@v4 with: @@ -186,6 +159,14 @@ jobs: path: lighthouse-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.arch }}.tar.gz.asc compression-level: 0 + - name: Upload signature (copy) + if: startsWith(matrix.arch, 'x86_64-windows') != true + uses: actions/upload-artifact@v4 + with: + name: lighthouse-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.arch }}-portable.tar.gz.asc + path: lighthouse-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.arch }}-portable.tar.gz.asc + compression-level: 0 + draft-release: name: Draft Release needs: [build, extract-version] diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 413dd2b95dd..74aab44ac08 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -273,9 +273,7 @@ jobs: run: | make - name: Install lcli - # TODO: uncomment after the version of lcli in https://github.com/sigp/lighthouse/pull/5137 - # is installed on the runners - # if: env.SELF_HOSTED_RUNNERS == 'false' + if: env.SELF_HOSTED_RUNNERS == 'false' run: make install-lcli - name: Run the doppelganger protection failure test script run: | diff --git a/Cargo.lock b/Cargo.lock index cc38877fe76..3a52b854ced 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -533,7 +533,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 0.38.33", + "rustix 0.38.34", "slab", "tracing", "windows-sys 0.52.0", @@ -739,9 +739,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -781,7 +781,7 @@ dependencies = [ "merkle_proof", "oneshot_broadcast", "operation_pool", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "proto_array", "rand", "rayon", @@ -860,7 +860,7 @@ dependencies = [ "lighthouse_network", "logging", "num_cpus", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "serde", "slog", "slot_clock", @@ -1191,9 +1191,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" dependencies = [ "jobserver", "libc", @@ -1346,7 +1346,7 @@ dependencies = [ "network", "num_cpus", "operation_pool", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "sensitive_url", "serde", "serde_yaml", @@ -1391,9 +1391,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -1752,15 +1752,15 @@ checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "data-encoding-macro" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1768,9 +1768,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" dependencies = [ "data-encoding", "syn 1.0.109", @@ -2240,7 +2240,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.60", @@ -2340,7 +2340,7 @@ dependencies = [ "lazy_static", "lighthouse_metrics", "merkle_proof", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "reqwest", "sensitive_url", "serde", @@ -2848,7 +2848,7 @@ dependencies = [ "lighthouse_metrics", "lighthouse_version", "lru", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "pretty_reqwest_error", "rand", "reqwest", @@ -2897,9 +2897,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fastrlp" @@ -2912,6 +2912,16 @@ dependencies = [ "bytes", ] +[[package]] +name = "fdlimit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182f7dbc2ef73d9ef67351c5fbbea084729c48362d3ce9dd44c28e32e277fe5" +dependencies = [ + "libc", + "thiserror", +] + [[package]] name = "ff" version = "0.12.1" @@ -2940,9 +2950,9 @@ checksum = "ec54ac60a7f2ee9a97cad9946f9bf629a3bc6a7ae59e68983dc9318f5a54b81a" [[package]] name = "fiat-crypto" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" +checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e" [[package]] name = "field-offset" @@ -2995,9 +3005,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "libz-sys", @@ -3158,7 +3168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35bd3cf68c183738046838e300353e4716c674dc5e56890de4826801a6622a28" dependencies = [ "futures-io", - "rustls 0.21.11", + "rustls 0.21.12", ] [[package]] @@ -3415,18 +3425,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -3447,7 +3448,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -3480,6 +3481,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -3533,7 +3540,7 @@ dependencies = [ "ipnet", "once_cell", "rand", - "socket2 0.5.6", + "socket2 0.5.7", "thiserror", "tinyvec", "tokio", @@ -3553,7 +3560,7 @@ dependencies = [ "ipconfig", "lru-cache", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "rand", "resolv-conf", "smallvec", @@ -3704,7 +3711,7 @@ dependencies = [ "lru", "network", "operation_pool", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "proto_array", "safe_arith", "sensitive_url", @@ -3781,7 +3788,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.6", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -3816,7 +3823,7 @@ dependencies = [ "futures-util", "http 0.2.12", "hyper 0.14.28", - "rustls 0.21.11", + "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", ] @@ -3846,7 +3853,7 @@ dependencies = [ "http-body 1.0.0", "hyper 1.3.1", "pin-project-lite", - "socket2 0.5.6", + "socket2 0.5.7", "tokio", ] @@ -4026,7 +4033,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -4085,7 +4092,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.6", + "socket2 0.5.7", "widestring 1.1.0", "windows-sys 0.48.0", "winreg", @@ -4338,15 +4345,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libflate" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7d5654ae1795afc7ff76f4365c2c8791b0feb18e8996a96adad8ffd7c3b2bf" +checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" dependencies = [ "adler32", "core2", @@ -4357,12 +4364,12 @@ dependencies = [ [[package]] name = "libflate_lz77" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524" +checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" dependencies = [ "core2", - "hashbrown 0.13.2", + "hashbrown 0.14.5", "rle-decode-fast", ] @@ -4393,7 +4400,7 @@ dependencies = [ "indexmap 1.9.3", "libc", "mdbx-sys", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "thiserror", ] @@ -4470,7 +4477,7 @@ dependencies = [ "multihash", "multistream-select", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "pin-project", "quick-protobuf", "rand", @@ -4493,16 +4500,16 @@ dependencies = [ "hickory-resolver", "libp2p-core", "libp2p-identity", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "smallvec", "tracing", ] [[package]] name = "libp2p-identify" -version = "0.44.1" +version = "0.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20499a945d2f0221fdc6269b3848892c0f370d2ee3e19c7f65a29d8f860f6126" +checksum = "b5d635ebea5ca0c3c3e77d414ae9b67eccf2a822be06091b9c1a0d13029a1e2f" dependencies = [ "asynchronous-codec 0.7.0", "either", @@ -4559,7 +4566,7 @@ dependencies = [ "libp2p-swarm", "rand", "smallvec", - "socket2 0.5.6", + "socket2 0.5.7", "tokio", "tracing", "void", @@ -4593,7 +4600,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "nohash-hasher", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "rand", "smallvec", "tracing", @@ -4655,12 +4662,12 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-tls", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "quinn", "rand", "ring 0.16.20", - "rustls 0.21.11", - "socket2 0.5.6", + "rustls 0.21.12", + "socket2 0.5.7", "thiserror", "tokio", "tracing", @@ -4668,9 +4675,9 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.44.1" +version = "0.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e92532fc3c4fb292ae30c371815c9b10103718777726ea5497abc268a4761866" +checksum = "80cae6cb75f89dbca53862f9ebe0b9f463aa7b302762fcfaafb9e51dcc9b0f7e" dependencies = [ "either", "fnv", @@ -4680,6 +4687,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm-derive", + "lru", "multistream-select", "once_cell", "rand", @@ -4691,11 +4699,11 @@ dependencies = [ [[package]] name = "libp2p-swarm-derive" -version = "0.34.1" +version = "0.34.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b644268b4acfdaa6a6100b31226ee7a36d96ab4c43287d113bfd2308607d8b6f" +checksum = "5daceb9dd908417b6dfcfe8e94098bc4aac54500c282e78120b885dadc09b999" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn 2.0.60", @@ -4713,7 +4721,7 @@ dependencies = [ "libc", "libp2p-core", "libp2p-identity", - "socket2 0.5.6", + "socket2 0.5.7", "tokio", "tracing", ] @@ -4730,7 +4738,7 @@ dependencies = [ "libp2p-identity", "rcgen", "ring 0.16.20", - "rustls 0.21.11", + "rustls 0.21.12", "rustls-webpki 0.101.7", "thiserror", "x509-parser", @@ -4739,9 +4747,9 @@ dependencies = [ [[package]] name = "libp2p-upnp" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49cc89949bf0e06869297cd4fe2c132358c23fe93e76ad43950453df4da3d35" +checksum = "cccf04b0e3ff3de52d07d5fd6c3b061d0e7f908ffc683c32d9638caedce86fc8" dependencies = [ "futures", "futures-timer", @@ -4765,7 +4773,7 @@ dependencies = [ "thiserror", "tracing", "yamux 0.12.1", - "yamux 0.13.1", + "yamux 0.13.2", ] [[package]] @@ -4931,7 +4939,7 @@ dependencies = [ "lighthouse_version", "lru", "lru_cache", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "prometheus-client", "quickcheck", "quickcheck_macros", @@ -5012,9 +5020,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -5041,7 +5049,7 @@ dependencies = [ "chrono", "lazy_static", "lighthouse_metrics", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "serde", "serde_json", "slog", @@ -5063,7 +5071,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -5101,7 +5109,7 @@ dependencies = [ "lazy_static", "libc", "lighthouse_metrics", - "parking_lot 0.12.1", + "parking_lot 0.12.2", ] [[package]] @@ -5248,7 +5256,7 @@ dependencies = [ "ethereum_ssz", "ethereum_ssz_derive", "itertools", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "rayon", "serde", "smallvec", @@ -5501,7 +5509,7 @@ dependencies = [ "matches", "num_cpus", "operation_pool", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "rand", "rlp", "slog", @@ -5697,7 +5705,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" name = "oneshot_broadcast" version = "0.1.0" dependencies = [ - "parking_lot 0.12.1", + "parking_lot 0.12.2", ] [[package]] @@ -5804,7 +5812,7 @@ dependencies = [ "lazy_static", "lighthouse_metrics", "maplit", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "rand", "rayon", "serde", @@ -5903,12 +5911,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", - "parking_lot_core 0.9.9", + "parking_lot_core 0.9.10", ] [[package]] @@ -5927,15 +5935,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -5997,7 +6005,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "serde", ] @@ -6018,9 +6026,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.9" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", @@ -6155,15 +6163,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi 0.3.9", "pin-project-lite", - "rustix 0.38.33", + "rustix 0.38.34", "tracing", "windows-sys 0.52.0", ] @@ -6350,7 +6358,7 @@ dependencies = [ "fnv", "lazy_static", "memchr", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "protobuf", "thiserror", ] @@ -6363,7 +6371,7 @@ checksum = "c1ca959da22a332509f2a73ae9e5f23f9dcfc31fd3a54d71f159495bd5909baa" dependencies = [ "dtoa", "itoa", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "prometheus-client-derive-encode", ] @@ -6511,7 +6519,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.21.11", + "rustls 0.21.12", "thiserror", "tokio", "tracing", @@ -6527,7 +6535,7 @@ dependencies = [ "rand", "ring 0.16.20", "rustc-hash", - "rustls 0.21.11", + "rustls 0.21.12", "slab", "thiserror", "tinyvec", @@ -6542,7 +6550,7 @@ checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ "bytes", "libc", - "socket2 0.5.6", + "socket2 0.5.7", "tracing", "windows-sys 0.48.0", ] @@ -6563,7 +6571,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" dependencies = [ "log", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "scheduled-thread-pool", ] @@ -6678,6 +6686,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "redox_users" version = "0.4.5" @@ -6758,7 +6775,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.11", + "rustls 0.21.12", "rustls-pemfile 1.0.4", "serde", "serde_json", @@ -7006,9 +7023,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.33" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", @@ -7019,9 +7036,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.11" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", @@ -7038,7 +7055,7 @@ dependencies = [ "log", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.2", + "rustls-webpki 0.102.3", "subtle", "zeroize", ] @@ -7058,15 +7075,15 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" [[package]] name = "rustls-webpki" @@ -7080,9 +7097,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -7185,7 +7202,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot 0.12.1", + "parking_lot 0.12.2", ] [[package]] @@ -7316,9 +7333,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] @@ -7345,9 +7362,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", @@ -7577,7 +7594,7 @@ dependencies = [ "execution_layer", "futures", "node_test_rig", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "rayon", "sensitive_url", "serde_json", @@ -7619,7 +7636,7 @@ dependencies = [ "logging", "lru", "maplit", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "rand", "rayon", "safe_arith", @@ -7776,7 +7793,7 @@ version = "0.2.0" dependencies = [ "lazy_static", "lighthouse_metrics", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "types", ] @@ -7821,9 +7838,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -7946,7 +7963,7 @@ dependencies = [ "lighthouse_metrics", "logging", "lru", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "safe_arith", "serde", "slog", @@ -7996,7 +8013,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -8119,7 +8136,7 @@ name = "system_health" version = "0.1.0" dependencies = [ "lighthouse_network", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "serde", "serde_json", "sysinfo", @@ -8173,7 +8190,7 @@ checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "rustix 0.38.33", + "rustix 0.38.34", "windows-sys 0.52.0", ] @@ -8386,7 +8403,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.6", + "socket2 0.5.7", "tokio-macros", "windows-sys 0.48.0", ] @@ -8435,14 +8452,14 @@ dependencies = [ "futures-channel", "futures-util", "log", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "percent-encoding", "phf", "pin-project-lite", "postgres-protocol", "postgres-types", "rand", - "socket2 0.5.6", + "socket2 0.5.7", "tokio", "tokio-util 0.7.10", "whoami", @@ -8454,7 +8471,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.11", + "rustls 0.21.12", "tokio", ] @@ -8731,9 +8748,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee8098afad3fb0c54a9007aab6804558410503ad676d4633f9c2559a00ac0f" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" dependencies = [ "serde", "stable_deref_trait", @@ -8779,7 +8796,7 @@ dependencies = [ "merkle_proof", "metastruct", "milhouse", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "paste", "rand", "rand_xorshift", @@ -8868,9 +8885,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "unicode-xid" @@ -8948,7 +8965,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "lru_cache", - "parking_lot 0.12.1", + "parking_lot 0.12.2", ] [[package]] @@ -8988,6 +9005,7 @@ dependencies = [ "eth2", "eth2_keystore", "ethereum_serde_utils", + "fdlimit", "filesystem", "futures", "hex", @@ -9001,7 +9019,7 @@ dependencies = [ "logging", "malloc_utils", "monitoring_api", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "rand", "reqwest", "ring 0.16.20", @@ -9339,7 +9357,7 @@ dependencies = [ "eth2_network_config", "futures", "lazy_static", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "reqwest", "serde", "serde_json", @@ -9401,11 +9419,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -9776,7 +9794,7 @@ dependencies = [ "futures", "log", "nohash-hasher", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "pin-project", "rand", "static_assertions", @@ -9784,14 +9802,15 @@ dependencies = [ [[package]] name = "yamux" -version = "0.13.1" -source = "git+https://github.com/sigp/rust-yamux.git#12a23aa0e34b7807c0c5f87f06b3438f7d6c2ed0" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f97202f6b125031b95d83e01dc57292b529384f80bfae4677e4bbc10178cf72" dependencies = [ "futures", "instant", "log", "nohash-hasher", - "parking_lot 0.12.1", + "parking_lot 0.12.2", "pin-project", "rand", "static_assertions", diff --git a/Cargo.toml b/Cargo.toml index be2011ba286..c70d9459124 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,7 +104,7 @@ byteorder = "1" bytes = "1" # Turn off c-kzg's default features which include `blst/portable`. We can turn on blst's portable # feature ourselves when desired. -c-kzg = { version = "1", default-features = false } +c-kzg = { version = "1", default-features = false } clap = "2" compare_fields_derive = { path = "common/compare_fields_derive" } criterion = "0.3" @@ -238,9 +238,6 @@ validator_client = { path = "validator_client" } validator_dir = { path = "common/validator_dir" } warp_utils = { path = "common/warp_utils" } -[patch.crates-io] -yamux = { git = "https://github.com/sigp/rust-yamux.git" } - [profile.maxperf] inherits = "release" lto = "fat" diff --git a/Makefile b/Makefile index 4072ab1e6d8..12d33cc3a8b 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ PROFILE ?= release # List of all hard forks. This list is used to set env variables for several tests so that # they run for different forks. -FORKS=phase0 altair merge capella deneb electra +FORKS=phase0 altair bellatrix capella deneb electra # Extra flags for Cargo CARGO_INSTALL_EXTRA_FLAGS?= diff --git a/beacon_node/beacon_chain/src/attestation_rewards.rs b/beacon_node/beacon_chain/src/attestation_rewards.rs index 491b7ef7da9..d48a83130e6 100644 --- a/beacon_node/beacon_chain/src/attestation_rewards.rs +++ b/beacon_node/beacon_chain/src/attestation_rewards.rs @@ -54,7 +54,7 @@ impl BeaconChain { match state { BeaconState::Base(_) => self.compute_attestation_rewards_base(state, validators), BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => self.compute_attestation_rewards_altair(state, validators), diff --git a/beacon_node/beacon_chain/src/attestation_verification.rs b/beacon_node/beacon_chain/src/attestation_verification.rs index a95160d504d..1dd43b3c024 100644 --- a/beacon_node/beacon_chain/src/attestation_verification.rs +++ b/beacon_node/beacon_chain/src/attestation_verification.rs @@ -1062,7 +1062,9 @@ pub fn verify_propagation_slot_range( let current_fork = spec.fork_name_at_slot::(slot_clock.now().ok_or(BeaconChainError::UnableToReadSlot)?); let earliest_permissible_slot = match current_fork { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => one_epoch_prior, + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { + one_epoch_prior + } // EIP-7045 ForkName::Deneb | ForkName::Electra => one_epoch_prior .epoch(E::slots_per_epoch()) diff --git a/beacon_node/beacon_chain/src/beacon_block_streamer.rs b/beacon_node/beacon_chain/src/beacon_block_streamer.rs index 4f413ce2a86..0c92b7c1f62 100644 --- a/beacon_node/beacon_chain/src/beacon_block_streamer.rs +++ b/beacon_node/beacon_chain/src/beacon_block_streamer.rs @@ -14,8 +14,8 @@ use types::{ SignedBlindedBeaconBlock, Slot, }; use types::{ - ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadElectra, ExecutionPayloadHeader, - ExecutionPayloadMerge, + ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadElectra, + ExecutionPayloadHeader, }; #[derive(PartialEq)] @@ -95,7 +95,7 @@ fn reconstruct_default_header_block( .map_err(BeaconChainError::InconsistentFork)?; let payload: ExecutionPayload = match fork { - ForkName::Merge => ExecutionPayloadMerge::default().into(), + ForkName::Bellatrix => ExecutionPayloadBellatrix::default().into(), ForkName::Capella => ExecutionPayloadCapella::default().into(), ForkName::Deneb => ExecutionPayloadDeneb::default().into(), ForkName::Electra => ExecutionPayloadElectra::default().into(), diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index ffd19dca0af..a99b5db6051 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -2424,6 +2424,7 @@ impl BeaconChain { proposer_slashing: ProposerSlashing, ) -> Result, Error> { let wall_clock_state = self.wall_clock_state()?; + Ok(self.observed_proposer_slashings.lock().verify_and_observe( proposer_slashing, &wall_clock_state, @@ -2436,6 +2437,14 @@ impl BeaconChain { &self, proposer_slashing: SigVerifiedOp, ) { + if let Some(event_handler) = self.event_handler.as_ref() { + if event_handler.has_proposer_slashing_subscribers() { + event_handler.register(EventKind::ProposerSlashing(Box::new( + proposer_slashing.clone().into_inner(), + ))); + } + } + if self.eth1_chain.is_some() { self.op_pool.insert_proposer_slashing(proposer_slashing) } @@ -2447,6 +2456,7 @@ impl BeaconChain { attester_slashing: AttesterSlashing, ) -> Result, T::EthSpec>, Error> { let wall_clock_state = self.wall_clock_state()?; + Ok(self.observed_attester_slashings.lock().verify_and_observe( attester_slashing, &wall_clock_state, @@ -2467,6 +2477,14 @@ impl BeaconChain { .fork_choice_write_lock() .on_attester_slashing(attester_slashing.as_inner().to_ref()); + if let Some(event_handler) = self.event_handler.as_ref() { + if event_handler.has_attester_slashing_subscribers() { + event_handler.register(EventKind::AttesterSlashing(Box::new( + attester_slashing.clone().into_inner(), + ))); + } + } + // Add to the op pool (if we have the ability to propose blocks). if self.eth1_chain.is_some() { self.op_pool.insert_attester_slashing(attester_slashing) @@ -2523,7 +2541,7 @@ impl BeaconChain { /// Check if the current slot is greater than or equal to the Capella fork epoch. pub fn current_slot_is_post_capella(&self) -> Result { let current_fork = self.spec.fork_name_at_slot::(self.slot()?); - if let ForkName::Base | ForkName::Altair | ForkName::Merge = current_fork { + if let ForkName::Base | ForkName::Altair | ForkName::Bellatrix = current_fork { Ok(false) } else { Ok(true) @@ -3839,7 +3857,7 @@ impl BeaconChain { if block.slot() + 2 * T::EthSpec::slots_per_epoch() >= current_slot { metrics::observe( &metrics::OPERATIONS_PER_BLOCK_ATTESTATION, - block.body().attestations().count() as f64, + block.body().attestations_len() as f64, ); if let Ok(sync_aggregate) = block.body().sync_aggregate() { @@ -4399,12 +4417,6 @@ impl BeaconChain { if cached_head.head_block_root() == parent_block_root { (Cow::Borrowed(head_state), cached_head.head_state_root()) } else { - info!( - self.log, - "Missed snapshot cache during withdrawals calculation"; - "slot" => proposal_slot, - "parent_block_root" => ?parent_block_root - ); let block = self .get_blinded_block(&parent_block_root)? .ok_or(Error::MissingBeaconBlock(parent_block_root))?; @@ -4825,7 +4837,7 @@ impl BeaconChain { // allows it to run concurrently with things like attestation packing. let prepare_payload_handle = match &state { BeaconState::Base(_) | BeaconState::Altair(_) => None, - BeaconState::Merge(_) + BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => { @@ -5067,7 +5079,7 @@ impl BeaconChain { match &state { BeaconState::Base(_) | BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) => { if !attestations_electra.is_empty() { @@ -5152,17 +5164,17 @@ impl BeaconChain { None, Uint256::zero(), ), - BeaconState::Merge(_) => { + BeaconState::Bellatrix(_) => { let block_proposal_contents = block_contents.ok_or(BlockProductionError::MissingExecutionPayload)?; let execution_payload_value = block_proposal_contents.block_value().to_owned(); ( - BeaconBlock::Merge(BeaconBlockMerge { + BeaconBlock::Bellatrix(BeaconBlockBellatrix { slot, proposer_index, parent_root, state_root: Hash256::zero(), - body: BeaconBlockBodyMerge { + body: BeaconBlockBodyBellatrix { randao_reveal, eth1_data, graffiti, @@ -5612,7 +5624,7 @@ impl BeaconChain { } else { let prepare_slot_fork = self.spec.fork_name_at_slot::(prepare_slot); let withdrawals = match prepare_slot_fork { - ForkName::Base | ForkName::Altair | ForkName::Merge => None, + ForkName::Base | ForkName::Altair | ForkName::Bellatrix => None, ForkName::Capella | ForkName::Deneb | ForkName::Electra => { let chain = self.clone(); self.spawn_blocking_handle( @@ -5627,7 +5639,7 @@ impl BeaconChain { }; let parent_beacon_block_root = match prepare_slot_fork { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => None, + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => None, ForkName::Deneb | ForkName::Electra => { Some(pre_payload_attributes.parent_beacon_block_root) } @@ -6670,7 +6682,7 @@ impl BeaconChain { match fork_name { ForkName::Altair - | ForkName::Merge + | ForkName::Bellatrix | ForkName::Capella | ForkName::Deneb | ForkName::Electra => { diff --git a/beacon_node/beacon_chain/src/merge_readiness.rs b/beacon_node/beacon_chain/src/bellatrix_readiness.rs similarity index 91% rename from beacon_node/beacon_chain/src/merge_readiness.rs rename to beacon_node/beacon_chain/src/bellatrix_readiness.rs index 52a5ea912e0..bf9e8481261 100644 --- a/beacon_node/beacon_chain/src/merge_readiness.rs +++ b/beacon_node/beacon_chain/src/bellatrix_readiness.rs @@ -11,7 +11,7 @@ use types::*; /// The time before the Bellatrix fork when we will start issuing warnings about preparation. pub const SECONDS_IN_A_WEEK: u64 = 604800; -pub const MERGE_READINESS_PREPARATION_SECONDS: u64 = SECONDS_IN_A_WEEK * 2; +pub const BELLATRIX_READINESS_PREPARATION_SECONDS: u64 = SECONDS_IN_A_WEEK * 2; #[derive(Default, Debug, Serialize, Deserialize)] pub struct MergeConfig { @@ -81,7 +81,7 @@ impl MergeConfig { #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "type")] -pub enum MergeReadiness { +pub enum BellatrixReadiness { /// The node is ready, as far as we can tell. Ready { config: MergeConfig, @@ -94,29 +94,29 @@ pub enum MergeReadiness { NoExecutionEndpoint, } -impl fmt::Display for MergeReadiness { +impl fmt::Display for BellatrixReadiness { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - MergeReadiness::Ready { + BellatrixReadiness::Ready { config: params, current_difficulty, } => { write!( f, - "This node appears ready for the merge. \ + "This node appears ready for Bellatrix \ Params: {}, current_difficulty: {:?}", params, current_difficulty ) } - MergeReadiness::NotSynced => write!( + BellatrixReadiness::NotSynced => write!( f, "The execution endpoint is connected and configured, \ however it is not yet synced" ), - MergeReadiness::NoExecutionEndpoint => write!( + BellatrixReadiness::NoExecutionEndpoint => write!( f, "The --execution-endpoint flag is not specified, this is a \ - requirement for the merge" + requirement for Bellatrix" ), } } @@ -143,12 +143,12 @@ pub enum GenesisExecutionPayloadStatus { impl BeaconChain { /// Returns `true` if user has an EL configured, or if the Bellatrix fork has occurred or will - /// occur within `MERGE_READINESS_PREPARATION_SECONDS`. + /// occur within `BELLATRIX_READINESS_PREPARATION_SECONDS`. pub fn is_time_to_prepare_for_bellatrix(&self, current_slot: Slot) -> bool { if let Some(bellatrix_epoch) = self.spec.bellatrix_fork_epoch { let bellatrix_slot = bellatrix_epoch.start_slot(T::EthSpec::slots_per_epoch()); - let merge_readiness_preparation_slots = - MERGE_READINESS_PREPARATION_SECONDS / self.spec.seconds_per_slot; + let bellatrix_readiness_preparation_slots = + BELLATRIX_READINESS_PREPARATION_SECONDS / self.spec.seconds_per_slot; if self.execution_layer.is_some() { // The user has already configured an execution layer, start checking for readiness @@ -156,7 +156,7 @@ impl BeaconChain { true } else { // Return `true` if Bellatrix has happened or is within the preparation time. - current_slot + merge_readiness_preparation_slots > bellatrix_slot + current_slot + bellatrix_readiness_preparation_slots > bellatrix_slot } } else { // The Bellatrix fork epoch has not been defined yet, no need to prepare. @@ -164,22 +164,22 @@ impl BeaconChain { } } - /// Attempts to connect to the EL and confirm that it is ready for the merge. - pub async fn check_merge_readiness(&self, current_slot: Slot) -> MergeReadiness { + /// Attempts to connect to the EL and confirm that it is ready for Bellatrix. + pub async fn check_bellatrix_readiness(&self, current_slot: Slot) -> BellatrixReadiness { if let Some(el) = self.execution_layer.as_ref() { if !el.is_synced_for_notifier(current_slot).await { // The EL is not synced. - return MergeReadiness::NotSynced; + return BellatrixReadiness::NotSynced; } let params = MergeConfig::from_chainspec(&self.spec); let current_difficulty = el.get_current_difficulty().await.ok(); - MergeReadiness::Ready { + BellatrixReadiness::Ready { config: params, current_difficulty, } } else { // There is no EL configured. - MergeReadiness::NoExecutionEndpoint + BellatrixReadiness::NoExecutionEndpoint } } diff --git a/beacon_node/beacon_chain/src/blob_verification.rs b/beacon_node/beacon_chain/src/blob_verification.rs index 263b9f9e013..fdf8ee2b971 100644 --- a/beacon_node/beacon_chain/src/blob_verification.rs +++ b/beacon_node/beacon_chain/src/blob_verification.rs @@ -571,6 +571,14 @@ pub fn validate_blob_sidecar_for_gossip( }); } + // Kzg verification for gossip blob sidecar + let kzg = chain + .kzg + .as_ref() + .ok_or(GossipBlobError::KzgNotInitialized)?; + let kzg_verified_blob = KzgVerifiedBlob::new(blob_sidecar.clone(), kzg, seen_timestamp) + .map_err(GossipBlobError::KzgError)?; + chain .observed_slashable .write() @@ -605,14 +613,6 @@ pub fn validate_blob_sidecar_for_gossip( }); } - // Kzg verification for gossip blob sidecar - let kzg = chain - .kzg - .as_ref() - .ok_or(GossipBlobError::KzgNotInitialized)?; - let kzg_verified_blob = KzgVerifiedBlob::new(blob_sidecar, kzg, seen_timestamp) - .map_err(GossipBlobError::KzgError)?; - Ok(GossipVerifiedBlob { block_root, blob: kzg_verified_blob, diff --git a/beacon_node/beacon_chain/src/block_verification_types.rs b/beacon_node/beacon_chain/src/block_verification_types.rs index fb0e0c965f1..d0360bf18e5 100644 --- a/beacon_node/beacon_chain/src/block_verification_types.rs +++ b/beacon_node/beacon_chain/src/block_verification_types.rs @@ -96,13 +96,18 @@ impl RpcBlock { } /// Constructs a new `BlockAndBlobs` variant after making consistency - /// checks between the provided blocks and blobs. + /// checks between the provided blocks and blobs. This struct makes no + /// guarantees about whether blobs should be present, only that they are + /// consistent with the block. An empty list passed in for `blobs` is + /// viewed the same as `None` passed in. pub fn new( block_root: Option, block: Arc>, blobs: Option>, ) -> Result { let block_root = block_root.unwrap_or_else(|| get_block_root(&block)); + // Treat empty blob lists as if they are missing. + let blobs = blobs.filter(|b| !b.is_empty()); if let (Some(blobs), Ok(block_commitments)) = ( blobs.as_ref(), diff --git a/beacon_node/beacon_chain/src/builder.rs b/beacon_node/beacon_chain/src/builder.rs index 376bc16c035..90461b8f03e 100644 --- a/beacon_node/beacon_chain/src/builder.rs +++ b/beacon_node/beacon_chain/src/builder.rs @@ -708,8 +708,8 @@ where .ok_or("Cannot build without a genesis state root")?; let validator_monitor_config = self.validator_monitor_config.unwrap_or_default(); let head_tracker = Arc::new(self.head_tracker.unwrap_or_default()); - let beacon_proposer_cache: Arc> = <_>::default(); + let mut validator_monitor = ValidatorMonitor::new( validator_monitor_config, beacon_proposer_cache.clone(), diff --git a/beacon_node/beacon_chain/src/capella_readiness.rs b/beacon_node/beacon_chain/src/capella_readiness.rs index cde71d462d7..88af7db0aaa 100644 --- a/beacon_node/beacon_chain/src/capella_readiness.rs +++ b/beacon_node/beacon_chain/src/capella_readiness.rs @@ -10,7 +10,7 @@ use std::time::Duration; use types::*; /// The time before the Capella fork when we will start issuing warnings about preparation. -use super::merge_readiness::SECONDS_IN_A_WEEK; +use super::bellatrix_readiness::SECONDS_IN_A_WEEK; pub const CAPELLA_READINESS_PREPARATION_SECONDS: u64 = SECONDS_IN_A_WEEK * 2; pub const ENGINE_CAPABILITIES_REFRESH_INTERVAL: u64 = 300; diff --git a/beacon_node/beacon_chain/src/data_availability_checker.rs b/beacon_node/beacon_chain/src/data_availability_checker.rs index dd0d97b1dae..27ed0ae6d56 100644 --- a/beacon_node/beacon_chain/src/data_availability_checker.rs +++ b/beacon_node/beacon_chain/src/data_availability_checker.rs @@ -2,14 +2,11 @@ use crate::blob_verification::{verify_kzg_for_blob_list, GossipVerifiedBlob, Kzg use crate::block_verification_types::{ AvailabilityPendingExecutedBlock, AvailableExecutedBlock, RpcBlock, }; -pub use crate::data_availability_checker::child_components::ChildComponents; use crate::data_availability_checker::overflow_lru_cache::OverflowLRUCache; use crate::{BeaconChain, BeaconChainTypes, BeaconStore}; use kzg::Kzg; -use slasher::test_utils::E; use slog::{debug, error, Logger}; use slot_clock::SlotClock; -use ssz_types::FixedVector; use std::fmt; use std::fmt::Debug; use std::num::NonZeroUsize; @@ -19,7 +16,6 @@ use task_executor::TaskExecutor; use types::blob_sidecar::{BlobIdentifier, BlobSidecar, FixedBlobSidecarList}; use types::{BlobSidecarList, ChainSpec, Epoch, EthSpec, Hash256, SignedBeaconBlock}; -mod child_components; mod error; mod overflow_lru_cache; mod state_lru_cache; @@ -94,68 +90,27 @@ impl DataAvailabilityChecker { self.availability_cache.has_block(block_root) } - pub fn get_missing_blob_ids_with(&self, block_root: Hash256) -> MissingBlobs { + /// Return the required blobs `block_root` expects if the block is currenlty in the cache. + pub fn num_expected_blobs(&self, block_root: &Hash256) -> Option { self.availability_cache - .with_pending_components(&block_root, |pending_components| match pending_components { - Some(pending_components) => self.get_missing_blob_ids( - block_root, - pending_components - .get_cached_block() - .as_ref() - .map(|b| b.as_block()), - &pending_components.verified_blobs, - ), - None => MissingBlobs::new_without_block(block_root, self.is_deneb()), + .peek_pending_components(block_root, |components| { + components.and_then(|components| components.num_expected_blobs()) }) } - /// If there's no block, all possible ids will be returned that don't exist in the given blobs. - /// If there no blobs, all possible ids will be returned. - pub fn get_missing_blob_ids( - &self, - block_root: Hash256, - block: Option<&SignedBeaconBlock>, - blobs: &FixedVector, ::MaxBlobsPerBlock>, - ) -> MissingBlobs { - let Some(current_slot) = self.slot_clock.now_or_genesis() else { - error!( - self.log, - "Failed to read slot clock when checking for missing blob ids" - ); - return MissingBlobs::BlobsNotRequired; - }; - - let current_epoch = current_slot.epoch(T::EthSpec::slots_per_epoch()); - - if self.da_check_required_for_epoch(current_epoch) { - match block { - Some(cached_block) => { - let block_commitments_len = cached_block - .message() - .body() - .blob_kzg_commitments() - .map(|v| v.len()) - .unwrap_or(0); - let blob_ids = blobs + /// Return the set of imported blob indexes for `block_root`. Returns None if there is no block + /// component for `block_root`. + pub fn imported_blob_indexes(&self, block_root: &Hash256) -> Option> { + self.availability_cache + .peek_pending_components(block_root, |components| { + components.map(|components| { + components + .get_cached_blobs() .iter() - .take(block_commitments_len) - .enumerate() - .filter_map(|(index, blob_commitment_opt)| { - blob_commitment_opt.is_none().then_some(BlobIdentifier { - block_root, - index: index as u64, - }) - }) - .collect(); - MissingBlobs::KnownMissing(blob_ids) - } - None => { - MissingBlobs::PossibleMissing(BlobIdentifier::get_all_blob_ids::(block_root)) - } - } - } else { - MissingBlobs::BlobsNotRequired - } + .filter_map(|blob| blob.as_ref().map(|blob| blob.blob_index())) + .collect::>() + }) + }) } /// Get a blob from the availability cache. @@ -351,6 +306,18 @@ impl DataAvailabilityChecker { .map_or(false, |da_epoch| block_epoch >= da_epoch) } + pub fn da_check_required_for_current_epoch(&self) -> bool { + let Some(current_slot) = self.slot_clock.now_or_genesis() else { + error!( + self.log, + "Failed to read slot clock when checking for missing blob ids" + ); + return false; + }; + + self.da_check_required_for_epoch(current_slot.epoch(T::EthSpec::slots_per_epoch())) + } + /// Returns `true` if the current epoch is greater than or equal to the `Deneb` epoch. pub fn is_deneb(&self) -> bool { self.slot_clock.now().map_or(false, |slot| { @@ -544,61 +511,3 @@ impl MaybeAvailableBlock { } } } - -#[derive(Debug, Clone)] -pub enum MissingBlobs { - /// We know for certain these blobs are missing. - KnownMissing(Vec), - /// We think these blobs might be missing. - PossibleMissing(Vec), - /// Blobs are not required. - BlobsNotRequired, -} - -impl MissingBlobs { - pub fn new_without_block(block_root: Hash256, is_deneb: bool) -> Self { - if is_deneb { - MissingBlobs::PossibleMissing(BlobIdentifier::get_all_blob_ids::(block_root)) - } else { - MissingBlobs::BlobsNotRequired - } - } - pub fn is_empty(&self) -> bool { - match self { - MissingBlobs::KnownMissing(v) => v.is_empty(), - MissingBlobs::PossibleMissing(v) => v.is_empty(), - MissingBlobs::BlobsNotRequired => true, - } - } - pub fn contains(&self, blob_id: &BlobIdentifier) -> bool { - match self { - MissingBlobs::KnownMissing(v) => v.contains(blob_id), - MissingBlobs::PossibleMissing(v) => v.contains(blob_id), - MissingBlobs::BlobsNotRequired => false, - } - } - pub fn remove(&mut self, blob_id: &BlobIdentifier) { - match self { - MissingBlobs::KnownMissing(v) => v.retain(|id| id != blob_id), - MissingBlobs::PossibleMissing(v) => v.retain(|id| id != blob_id), - MissingBlobs::BlobsNotRequired => {} - } - } - pub fn indices(&self) -> Vec { - match self { - MissingBlobs::KnownMissing(v) => v.iter().map(|id| id.index).collect(), - MissingBlobs::PossibleMissing(v) => v.iter().map(|id| id.index).collect(), - MissingBlobs::BlobsNotRequired => vec![], - } - } -} - -impl Into> for MissingBlobs { - fn into(self) -> Vec { - match self { - MissingBlobs::KnownMissing(v) => v, - MissingBlobs::PossibleMissing(v) => v, - MissingBlobs::BlobsNotRequired => vec![], - } - } -} diff --git a/beacon_node/beacon_chain/src/data_availability_checker/child_components.rs b/beacon_node/beacon_chain/src/data_availability_checker/child_components.rs deleted file mode 100644 index 184dfc45001..00000000000 --- a/beacon_node/beacon_chain/src/data_availability_checker/child_components.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::block_verification_types::RpcBlock; -use bls::Hash256; -use std::sync::Arc; -use types::blob_sidecar::FixedBlobSidecarList; -use types::{BlobSidecar, EthSpec, SignedBeaconBlock}; - -/// For requests triggered by an `UnknownBlockParent` or `UnknownBlobParent`, this struct -/// is used to cache components as they are sent to the network service. We can't use the -/// data availability cache currently because any blocks or blobs without parents -/// won't pass validation and therefore won't make it into the cache. -pub struct ChildComponents { - pub block_root: Hash256, - pub downloaded_block: Option>>, - pub downloaded_blobs: FixedBlobSidecarList, -} - -impl From> for ChildComponents { - fn from(value: RpcBlock) -> Self { - let (block_root, block, blobs) = value.deconstruct(); - let fixed_blobs = blobs.map(|blobs| { - FixedBlobSidecarList::from(blobs.into_iter().map(Some).collect::>()) - }); - Self::new(block_root, Some(block), fixed_blobs) - } -} - -impl ChildComponents { - pub fn empty(block_root: Hash256) -> Self { - Self { - block_root, - downloaded_block: None, - downloaded_blobs: <_>::default(), - } - } - pub fn new( - block_root: Hash256, - block: Option>>, - blobs: Option>, - ) -> Self { - let mut cache = Self::empty(block_root); - if let Some(block) = block { - cache.merge_block(block); - } - if let Some(blobs) = blobs { - cache.merge_blobs(blobs); - } - cache - } - - pub fn merge_block(&mut self, block: Arc>) { - self.downloaded_block = Some(block); - } - - pub fn merge_blob(&mut self, blob: Arc>) { - if let Some(blob_ref) = self.downloaded_blobs.get_mut(blob.index as usize) { - *blob_ref = Some(blob); - } - } - - pub fn merge_blobs(&mut self, blobs: FixedBlobSidecarList) { - for blob in blobs.iter().flatten() { - self.merge_blob(blob.clone()); - } - } - - pub fn clear_blobs(&mut self) { - self.downloaded_blobs = FixedBlobSidecarList::default(); - } -} diff --git a/beacon_node/beacon_chain/src/data_availability_checker/error.rs b/beacon_node/beacon_chain/src/data_availability_checker/error.rs index 6c524786bfa..d22f6b2cc9f 100644 --- a/beacon_node/beacon_chain/src/data_availability_checker/error.rs +++ b/beacon_node/beacon_chain/src/data_availability_checker/error.rs @@ -22,6 +22,7 @@ pub enum Error { SlotClockError, } +#[derive(PartialEq, Eq)] pub enum ErrorCategory { /// Internal Errors (not caused by peers) Internal, diff --git a/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs b/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs index 3a8bccbad5c..f29cec92444 100644 --- a/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs +++ b/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs @@ -569,7 +569,7 @@ impl OverflowLRUCache { } } - pub fn with_pending_components>) -> R>( + pub fn peek_pending_components>) -> R>( &self, block_root: &Hash256, f: F, @@ -989,17 +989,17 @@ mod test { // go to bellatrix slot harness.extend_to_slot(bellatrix_fork_slot).await; - let merge_head = &harness.chain.head_snapshot().beacon_block; - assert!(merge_head.as_merge().is_ok()); - assert_eq!(merge_head.slot(), bellatrix_fork_slot); + let bellatrix_head = &harness.chain.head_snapshot().beacon_block; + assert!(bellatrix_head.as_bellatrix().is_ok()); + assert_eq!(bellatrix_head.slot(), bellatrix_fork_slot); assert!( - merge_head + bellatrix_head .message() .body() .execution_payload() .unwrap() .is_default_with_empty_roots(), - "Merge head is default payload" + "Bellatrix head is default payload" ); // Trigger the terminal PoW block. harness diff --git a/beacon_node/beacon_chain/src/deneb_readiness.rs b/beacon_node/beacon_chain/src/deneb_readiness.rs index 1ba6fe3ea6c..e11070a1f4f 100644 --- a/beacon_node/beacon_chain/src/deneb_readiness.rs +++ b/beacon_node/beacon_chain/src/deneb_readiness.rs @@ -10,7 +10,7 @@ use std::time::Duration; use types::*; /// The time before the Deneb fork when we will start issuing warnings about preparation. -use super::merge_readiness::SECONDS_IN_A_WEEK; +use super::bellatrix_readiness::SECONDS_IN_A_WEEK; pub const DENEB_READINESS_PREPARATION_SECONDS: u64 = SECONDS_IN_A_WEEK * 2; pub const ENGINE_CAPABILITIES_REFRESH_INTERVAL: u64 = 300; diff --git a/beacon_node/beacon_chain/src/electra_readiness.rs b/beacon_node/beacon_chain/src/electra_readiness.rs index 0e911bf37ec..42ee743fe6b 100644 --- a/beacon_node/beacon_chain/src/electra_readiness.rs +++ b/beacon_node/beacon_chain/src/electra_readiness.rs @@ -11,7 +11,7 @@ use std::time::Duration; use types::*; /// The time before the Electra fork when we will start issuing warnings about preparation. -use super::merge_readiness::SECONDS_IN_A_WEEK; +use super::bellatrix_readiness::SECONDS_IN_A_WEEK; pub const ELECTRA_READINESS_PREPARATION_SECONDS: u64 = SECONDS_IN_A_WEEK * 2; pub const ENGINE_CAPABILITIES_REFRESH_INTERVAL: u64 = 300; diff --git a/beacon_node/beacon_chain/src/events.rs b/beacon_node/beacon_chain/src/events.rs index 1fdcfdf8d07..8700675a66e 100644 --- a/beacon_node/beacon_chain/src/events.rs +++ b/beacon_node/beacon_chain/src/events.rs @@ -20,6 +20,8 @@ pub struct ServerSentEventHandler { light_client_finality_update_tx: Sender>, light_client_optimistic_update_tx: Sender>, block_reward_tx: Sender>, + proposer_slashing_tx: Sender>, + attester_slashing_tx: Sender>, log: Logger, } @@ -45,6 +47,8 @@ impl ServerSentEventHandler { let (light_client_finality_update_tx, _) = broadcast::channel(capacity); let (light_client_optimistic_update_tx, _) = broadcast::channel(capacity); let (block_reward_tx, _) = broadcast::channel(capacity); + let (proposer_slashing_tx, _) = broadcast::channel(capacity); + let (attester_slashing_tx, _) = broadcast::channel(capacity); Self { attestation_tx, @@ -60,6 +64,8 @@ impl ServerSentEventHandler { light_client_finality_update_tx, light_client_optimistic_update_tx, block_reward_tx, + proposer_slashing_tx, + attester_slashing_tx, log, } } @@ -126,6 +132,14 @@ impl ServerSentEventHandler { .block_reward_tx .send(kind) .map(|count| log_count("block reward", count)), + EventKind::ProposerSlashing(_) => self + .proposer_slashing_tx + .send(kind) + .map(|count| log_count("proposer slashing", count)), + EventKind::AttesterSlashing(_) => self + .attester_slashing_tx + .send(kind) + .map(|count| log_count("attester slashing", count)), }; if let Err(SendError(event)) = result { trace!(self.log, "No receivers registered to listen for event"; "event" => ?event); @@ -184,6 +198,14 @@ impl ServerSentEventHandler { self.block_reward_tx.subscribe() } + pub fn subscribe_attester_slashing(&self) -> Receiver> { + self.attester_slashing_tx.subscribe() + } + + pub fn subscribe_proposer_slashing(&self) -> Receiver> { + self.proposer_slashing_tx.subscribe() + } + pub fn has_attestation_subscribers(&self) -> bool { self.attestation_tx.receiver_count() > 0 } @@ -227,4 +249,12 @@ impl ServerSentEventHandler { pub fn has_block_reward_subscribers(&self) -> bool { self.block_reward_tx.receiver_count() > 0 } + + pub fn has_proposer_slashing_subscribers(&self) -> bool { + self.proposer_slashing_tx.receiver_count() > 0 + } + + pub fn has_attester_slashing_subscribers(&self) -> bool { + self.attester_slashing_tx.receiver_count() > 0 + } } diff --git a/beacon_node/beacon_chain/src/execution_payload.rs b/beacon_node/beacon_chain/src/execution_payload.rs index b3804f0d23a..cbffe363422 100644 --- a/beacon_node/beacon_chain/src/execution_payload.rs +++ b/beacon_node/beacon_chain/src/execution_payload.rs @@ -336,7 +336,7 @@ pub fn validate_execution_payload_for_gossip( block: BeaconBlockRef<'_, T::EthSpec>, chain: &BeaconChain, ) -> Result<(), BlockError> { - // Only apply this validation if this is a merge beacon block. + // Only apply this validation if this is a Bellatrix beacon block. if let Ok(execution_payload) = block.body().execution_payload() { // This logic should match `is_execution_enabled`. We use only the execution block hash of // the parent here in order to avoid loading the parent state during gossip verification. @@ -385,7 +385,7 @@ pub fn validate_execution_payload_for_gossip( /// ## Errors /// /// Will return an error when using a pre-merge fork `state`. Ensure to only run this function -/// after the merge fork. +/// after the Bellatrix fork. /// /// ## Specification /// @@ -415,13 +415,13 @@ pub fn get_execution_payload( &BeaconState::Capella(_) | &BeaconState::Deneb(_) | &BeaconState::Electra(_) => { Some(get_expected_withdrawals(state, spec)?.into()) } - &BeaconState::Merge(_) => None, + &BeaconState::Bellatrix(_) => None, // These shouldn't happen but they're here to make the pattern irrefutable &BeaconState::Base(_) | &BeaconState::Altair(_) => None, }; let parent_beacon_block_root = match state { BeaconState::Deneb(_) | BeaconState::Electra(_) => Some(parent_block_root), - BeaconState::Merge(_) | BeaconState::Capella(_) => None, + BeaconState::Bellatrix(_) | BeaconState::Capella(_) => None, // These shouldn't happen but they're here to make the pattern irrefutable BeaconState::Base(_) | BeaconState::Altair(_) => None, }; @@ -457,12 +457,12 @@ pub fn get_execution_payload( /// Prepares an execution payload for inclusion in a block. /// -/// Will return `Ok(None)` if the merge fork has occurred, but a terminal block has not been found. +/// Will return `Ok(None)` if the Bellatrix fork has occurred, but a terminal block has not been found. /// /// ## Errors /// -/// Will return an error when using a pre-merge fork `state`. Ensure to only run this function -/// after the merge fork. +/// Will return an error when using a pre-Bellatrix fork `state`. Ensure to only run this function +/// after the Bellatrix fork. /// /// ## Specification /// diff --git a/beacon_node/beacon_chain/src/lib.rs b/beacon_node/beacon_chain/src/lib.rs index 5ee7803436c..221bb8b2922 100644 --- a/beacon_node/beacon_chain/src/lib.rs +++ b/beacon_node/beacon_chain/src/lib.rs @@ -8,6 +8,7 @@ mod beacon_chain; mod beacon_fork_choice_store; pub mod beacon_proposer_cache; mod beacon_snapshot; +pub mod bellatrix_readiness; pub mod blob_verification; pub mod block_reward; mod block_times_cache; @@ -35,7 +36,6 @@ pub mod kzg_utils; pub mod light_client_finality_update_verification; pub mod light_client_optimistic_update_verification; mod light_client_server_cache; -pub mod merge_readiness; pub mod metrics; pub mod migrate; mod naive_aggregation_pool; diff --git a/beacon_node/beacon_chain/src/metrics.rs b/beacon_node/beacon_chain/src/metrics.rs index fc3f032cdc3..3b2453c3112 100644 --- a/beacon_node/beacon_chain/src/metrics.rs +++ b/beacon_node/beacon_chain/src/metrics.rs @@ -32,18 +32,11 @@ lazy_static! { "beacon_block_processing_successes_total", "Count of blocks processed without error" ); + // Keeping the existing "snapshot_cache" metric name as it would break existing dashboards pub static ref BLOCK_PROCESSING_SNAPSHOT_CACHE_SIZE: Result = try_create_int_gauge( "beacon_block_processing_snapshot_cache_size", "Count snapshots in the snapshot cache" ); - pub static ref BLOCK_PROCESSING_SNAPSHOT_CACHE_MISSES: Result = try_create_int_counter( - "beacon_block_processing_snapshot_cache_misses", - "Count of snapshot cache misses" - ); - pub static ref BLOCK_PROCESSING_SNAPSHOT_CACHE_CLONES: Result = try_create_int_counter( - "beacon_block_processing_snapshot_cache_clones", - "Count of snapshot cache clones" - ); pub static ref BLOCK_PROCESSING_TIMES: Result = try_create_histogram("beacon_block_processing_seconds", "Full runtime of block processing"); pub static ref BLOCK_PROCESSING_BLOCK_ROOT: Result = try_create_histogram( diff --git a/beacon_node/beacon_chain/src/snapshot_cache.rs b/beacon_node/beacon_chain/src/snapshot_cache.rs deleted file mode 100644 index ac4e71d3d5a..00000000000 --- a/beacon_node/beacon_chain/src/snapshot_cache.rs +++ /dev/null @@ -1,521 +0,0 @@ -use crate::BeaconSnapshot; -use itertools::process_results; -use std::cmp; -use std::sync::Arc; -use std::time::Duration; -use types::{ - beacon_state::CloneConfig, BeaconState, BlindedPayload, ChainSpec, Epoch, EthSpec, Hash256, - SignedBeaconBlock, Slot, -}; - -/// The default size of the cache. -pub const DEFAULT_SNAPSHOT_CACHE_SIZE: usize = 3; - -/// The minimum block delay to clone the state in the cache instead of removing it. -/// This helps keep block processing fast during re-orgs from late blocks. -fn minimum_block_delay_for_clone(seconds_per_slot: u64) -> Duration { - // If the block arrived at the attestation deadline or later, it might get re-orged. - Duration::from_secs(seconds_per_slot) / 3 -} - -/// This snapshot is to be used for verifying a child of `self.beacon_block`. -#[derive(Debug)] -pub struct PreProcessingSnapshot { - /// This state is equivalent to the `self.beacon_block.state_root()` state that has been - /// advanced forward one slot using `per_slot_processing`. This state is "primed and ready" for - /// the application of another block. - pub pre_state: BeaconState, - /// This value is only set to `Some` if the `pre_state` was *not* advanced forward. - pub beacon_state_root: Option, - pub beacon_block: SignedBeaconBlock>, - pub beacon_block_root: Hash256, -} - -impl From> for PreProcessingSnapshot { - fn from(snapshot: BeaconSnapshot) -> Self { - let beacon_state_root = Some(snapshot.beacon_state_root()); - Self { - pre_state: snapshot.beacon_state, - beacon_state_root, - beacon_block: snapshot.beacon_block.clone_as_blinded(), - beacon_block_root: snapshot.beacon_block_root, - } - } -} - -impl CacheItem { - pub fn new_without_pre_state(snapshot: BeaconSnapshot) -> Self { - Self { - beacon_block: snapshot.beacon_block, - beacon_block_root: snapshot.beacon_block_root, - beacon_state: snapshot.beacon_state, - pre_state: None, - } - } - - fn clone_to_snapshot_with(&self, clone_config: CloneConfig) -> BeaconSnapshot { - BeaconSnapshot { - beacon_state: self.beacon_state.clone_with(clone_config), - beacon_block: self.beacon_block.clone(), - beacon_block_root: self.beacon_block_root, - } - } - - pub fn into_pre_state(self) -> PreProcessingSnapshot { - // Do not include the beacon state root if the state has been advanced. - let beacon_state_root = - Some(self.beacon_block.state_root()).filter(|_| self.pre_state.is_none()); - - PreProcessingSnapshot { - beacon_block: self.beacon_block.clone_as_blinded(), - beacon_block_root: self.beacon_block_root, - pre_state: self.pre_state.unwrap_or(self.beacon_state), - beacon_state_root, - } - } - - pub fn clone_as_pre_state(&self) -> PreProcessingSnapshot { - // Do not include the beacon state root if the state has been advanced. - let beacon_state_root = - Some(self.beacon_block.state_root()).filter(|_| self.pre_state.is_none()); - - PreProcessingSnapshot { - beacon_block: self.beacon_block.clone_as_blinded(), - beacon_block_root: self.beacon_block_root, - pre_state: self - .pre_state - .as_ref() - .map_or_else(|| self.beacon_state.clone(), |pre_state| pre_state.clone()), - beacon_state_root, - } - } -} - -/// The information required for block production. -pub struct BlockProductionPreState { - /// This state may or may not have been advanced forward a single slot. - /// - /// See the documentation in the `crate::state_advance_timer` module for more information. - pub pre_state: BeaconState, - /// This value will only be `Some` if `self.pre_state` was **not** advanced forward a single - /// slot. - /// - /// This value can be used to avoid tree-hashing the state during the first call to - /// `per_slot_processing`. - pub state_root: Option, -} - -pub enum StateAdvance { - /// The cache does not contain the supplied block root. - BlockNotFound, - /// The cache contains the supplied block root but the state has already been advanced. - AlreadyAdvanced, - /// The cache contains the supplied block root and the state has not yet been advanced. - State { - state: Box>, - state_root: Hash256, - block_slot: Slot, - }, -} - -/// The item stored in the `SnapshotCache`. -pub struct CacheItem { - beacon_block: Arc>, - beacon_block_root: Hash256, - /// This state is equivalent to `self.beacon_block.state_root()`. - beacon_state: BeaconState, - /// This state is equivalent to `self.beacon_state` that has had `per_slot_processing` applied - /// to it. This state assists in optimizing block processing. - pre_state: Option>, -} - -impl Into> for CacheItem { - fn into(self) -> BeaconSnapshot { - BeaconSnapshot { - beacon_state: self.beacon_state, - beacon_block: self.beacon_block, - beacon_block_root: self.beacon_block_root, - } - } -} - -/// Provides a cache of `BeaconSnapshot` that is intended primarily for block processing. -/// -/// ## Cache Queuing -/// -/// The cache has a non-standard queue mechanism (specifically, it is not LRU). -/// -/// The cache has a max number of elements (`max_len`). Until `max_len` is achieved, all snapshots -/// are simply added to the queue. Once `max_len` is achieved, adding a new snapshot will cause an -/// existing snapshot to be ejected. The ejected snapshot will: -/// -/// - Never be the `head_block_root`. -/// - Be the snapshot with the lowest `state.slot` (ties broken arbitrarily). -pub struct SnapshotCache { - max_len: usize, - head_block_root: Hash256, - snapshots: Vec>, -} - -impl SnapshotCache { - /// Instantiate a new cache which contains the `head` snapshot. - /// - /// Setting `max_len = 0` is equivalent to setting `max_len = 1`. - pub fn new(max_len: usize, head: BeaconSnapshot) -> Self { - Self { - max_len: cmp::max(max_len, 1), - head_block_root: head.beacon_block_root, - snapshots: vec![CacheItem::new_without_pre_state(head)], - } - } - - /// The block roots of all snapshots contained in `self`. - pub fn beacon_block_roots(&self) -> Vec { - self.snapshots.iter().map(|s| s.beacon_block_root).collect() - } - - #[allow(clippy::len_without_is_empty)] - /// The number of snapshots contained in `self`. - pub fn len(&self) -> usize { - self.snapshots.len() - } - - /// Insert a snapshot, potentially removing an existing snapshot if `self` is at capacity (see - /// struct-level documentation for more info). - pub fn insert( - &mut self, - snapshot: BeaconSnapshot, - pre_state: Option>, - spec: &ChainSpec, - ) { - let parent_root = snapshot.beacon_block.message().parent_root(); - let item = CacheItem { - beacon_block: snapshot.beacon_block.clone(), - beacon_block_root: snapshot.beacon_block_root, - beacon_state: snapshot.beacon_state, - pre_state, - }; - - // Remove the grandparent of the block that was just inserted. - // - // Assuming it's unlikely to see re-orgs deeper than one block, this method helps keep the - // cache small by removing any states that already have more than one descendant. - // - // Remove the grandparent first to free up room in the cache. - let grandparent_result = - process_results(item.beacon_state.rev_iter_block_roots(spec), |iter| { - iter.map(|(_slot, root)| root) - .find(|root| *root != item.beacon_block_root && *root != parent_root) - }); - if let Ok(Some(grandparent_root)) = grandparent_result { - let head_block_root = self.head_block_root; - self.snapshots.retain(|snapshot| { - let root = snapshot.beacon_block_root; - root == head_block_root || root != grandparent_root - }); - } - - if self.snapshots.len() < self.max_len { - self.snapshots.push(item); - } else { - let insert_at = self - .snapshots - .iter() - .enumerate() - .filter_map(|(i, snapshot)| { - if snapshot.beacon_block_root != self.head_block_root { - Some((i, snapshot.beacon_state.slot())) - } else { - None - } - }) - .min_by_key(|(_i, slot)| *slot) - .map(|(i, _slot)| i); - - if let Some(i) = insert_at { - self.snapshots[i] = item; - } - } - } - - /// If available, returns a `CacheItem` that should be used for importing/processing a block. - /// The method will remove the block from `self`, carrying across any caches that may or may not - /// be built. - /// - /// In the event the block being processed was observed late, clone the cache instead of - /// moving it. This allows us to process the next block quickly in the case of a re-org. - /// Additionally, if the slot was skipped, clone the cache. This ensures blocks that are - /// later than 1 slot still have access to the cache and can be processed quickly. - pub fn get_state_for_block_processing( - &mut self, - block_root: Hash256, - block_slot: Slot, - block_delay: Option, - spec: &ChainSpec, - ) -> Option<(PreProcessingSnapshot, bool)> { - self.snapshots - .iter() - .position(|snapshot| snapshot.beacon_block_root == block_root) - .map(|i| { - if let Some(cache) = self.snapshots.get(i) { - // Avoid cloning the block during sync (when the `block_delay` is `None`). - if let Some(delay) = block_delay { - if delay >= minimum_block_delay_for_clone(spec.seconds_per_slot) - && delay <= Duration::from_secs(spec.seconds_per_slot) * 4 - || block_slot > cache.beacon_block.slot() + 1 - { - return (cache.clone_as_pre_state(), true); - } - } - } - (self.snapshots.remove(i).into_pre_state(), false) - }) - } - - /// If available, obtains a clone of a `BeaconState` that should be used for block production. - /// The clone will use `CloneConfig:all()`, ensuring any tree-hash cache is cloned too. - /// - /// ## Note - /// - /// This method clones the `BeaconState` (instead of removing it) since we assume that any block - /// we produce will soon be pushed to the `BeaconChain` for importing/processing. Keeping a copy - /// of that `BeaconState` in `self` will greatly help with import times. - pub fn get_state_for_block_production( - &self, - block_root: Hash256, - ) -> Option> { - self.snapshots - .iter() - .find(|snapshot| snapshot.beacon_block_root == block_root) - .map(|snapshot| { - if let Some(pre_state) = &snapshot.pre_state { - BlockProductionPreState { - pre_state: pre_state.clone_with(CloneConfig::all()), - state_root: None, - } - } else { - BlockProductionPreState { - pre_state: snapshot.beacon_state.clone_with(CloneConfig::all()), - state_root: Some(snapshot.beacon_block.state_root()), - } - } - }) - } - - /// If there is a snapshot with `block_root`, clone it and return the clone. - pub fn get_cloned( - &self, - block_root: Hash256, - clone_config: CloneConfig, - ) -> Option> { - self.snapshots - .iter() - .find(|snapshot| snapshot.beacon_block_root == block_root) - .map(|snapshot| snapshot.clone_to_snapshot_with(clone_config)) - } - - pub fn get_for_state_advance(&mut self, block_root: Hash256) -> StateAdvance { - if let Some(snapshot) = self - .snapshots - .iter_mut() - .find(|snapshot| snapshot.beacon_block_root == block_root) - { - if snapshot.pre_state.is_some() { - StateAdvance::AlreadyAdvanced - } else { - let cloned = snapshot - .beacon_state - .clone_with(CloneConfig::committee_caches_only()); - - StateAdvance::State { - state: Box::new(std::mem::replace(&mut snapshot.beacon_state, cloned)), - state_root: snapshot.beacon_block.state_root(), - block_slot: snapshot.beacon_block.slot(), - } - } - } else { - StateAdvance::BlockNotFound - } - } - - pub fn update_pre_state(&mut self, block_root: Hash256, state: BeaconState) -> Option<()> { - self.snapshots - .iter_mut() - .find(|snapshot| snapshot.beacon_block_root == block_root) - .map(|snapshot| { - snapshot.pre_state = Some(state); - }) - } - - /// Removes all snapshots from the queue that are less than or equal to the finalized epoch. - pub fn prune(&mut self, finalized_epoch: Epoch) { - self.snapshots.retain(|snapshot| { - snapshot.beacon_state.slot() > finalized_epoch.start_slot(E::slots_per_epoch()) - }) - } - - /// Inform the cache that the head of the beacon chain has changed. - /// - /// The snapshot that matches this `head_block_root` will never be ejected from the cache - /// during `Self::insert`. - pub fn update_head(&mut self, head_block_root: Hash256) { - self.head_block_root = head_block_root - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::test_utils::{BeaconChainHarness, EphemeralHarnessType}; - use types::{test_utils::generate_deterministic_keypair, BeaconBlock, MainnetEthSpec}; - - fn get_harness() -> BeaconChainHarness> { - let harness = BeaconChainHarness::builder(MainnetEthSpec) - .default_spec() - .deterministic_keypairs(1) - .fresh_ephemeral_store() - .build(); - - harness.advance_slot(); - - harness - } - - const CACHE_SIZE: usize = 4; - - fn get_snapshot(i: u64) -> BeaconSnapshot { - let spec = MainnetEthSpec::default_spec(); - - let beacon_state = get_harness().chain.head_beacon_state_cloned(); - - let signed_beacon_block = SignedBeaconBlock::from_block( - BeaconBlock::empty(&spec), - generate_deterministic_keypair(0) - .sk - .sign(Hash256::from_low_u64_be(42)), - ); - - BeaconSnapshot { - beacon_state, - beacon_block: Arc::new(signed_beacon_block), - beacon_block_root: Hash256::from_low_u64_be(i), - } - } - - #[test] - fn insert_get_prune_update() { - let spec = MainnetEthSpec::default_spec(); - let mut cache = SnapshotCache::new(CACHE_SIZE, get_snapshot(0)); - - // Insert a bunch of entries in the cache. It should look like this: - // - // Index Root - // 0 0 <--head - // 1 1 - // 2 2 - // 3 3 - for i in 1..CACHE_SIZE as u64 { - let mut snapshot = get_snapshot(i); - - // Each snapshot should be one slot into an epoch, with each snapshot one epoch apart. - *snapshot.beacon_state.slot_mut() = - Slot::from(i * MainnetEthSpec::slots_per_epoch() + 1); - - cache.insert(snapshot, None, &spec); - - assert_eq!( - cache.snapshots.len(), - i as usize + 1, - "cache length should be as expected" - ); - assert_eq!(cache.head_block_root, Hash256::from_low_u64_be(0)); - } - - // Insert a new value in the cache. Afterwards it should look like: - // - // Index Root - // 0 0 <--head - // 1 42 - // 2 2 - // 3 3 - assert_eq!(cache.snapshots.len(), CACHE_SIZE); - cache.insert(get_snapshot(42), None, &spec); - assert_eq!(cache.snapshots.len(), CACHE_SIZE); - - assert!( - cache - .get_state_for_block_processing( - Hash256::from_low_u64_be(1), - Slot::new(0), - None, - &spec - ) - .is_none(), - "the snapshot with the lowest slot should have been removed during the insert function" - ); - assert!(cache - .get_cloned(Hash256::from_low_u64_be(1), CloneConfig::none()) - .is_none()); - - assert_eq!( - cache - .get_cloned(Hash256::from_low_u64_be(0), CloneConfig::none()) - .expect("the head should still be in the cache") - .beacon_block_root, - Hash256::from_low_u64_be(0), - "get_cloned should get the correct snapshot" - ); - assert_eq!( - cache - .get_state_for_block_processing( - Hash256::from_low_u64_be(0), - Slot::new(0), - None, - &spec - ) - .expect("the head should still be in the cache") - .0 - .beacon_block_root, - Hash256::from_low_u64_be(0), - "get_state_for_block_processing should get the correct snapshot" - ); - - assert_eq!( - cache.snapshots.len(), - CACHE_SIZE - 1, - "get_state_for_block_processing should shorten the cache" - ); - - // Prune the cache. Afterwards it should look like: - // - // Index Root - // 0 2 - // 1 3 - cache.prune(Epoch::new(2)); - - assert_eq!(cache.snapshots.len(), 2); - - cache.update_head(Hash256::from_low_u64_be(2)); - - // Over-fill the cache so it needs to eject some old values on insert. - for i in 0..CACHE_SIZE as u64 { - cache.insert(get_snapshot(u64::max_value() - i), None, &spec); - } - - // Ensure that the new head value was not removed from the cache. - assert_eq!( - cache - .get_state_for_block_processing( - Hash256::from_low_u64_be(2), - Slot::new(0), - None, - &spec - ) - .expect("the new head should still be in the cache") - .0 - .beacon_block_root, - Hash256::from_low_u64_be(2), - "get_state_for_block_processing should get the correct snapshot" - ); - } -} diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index deefb34ed56..613411d8a2d 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -882,7 +882,7 @@ where let block_contents: SignedBlockContentsTuple = match *signed_block { SignedBeaconBlock::Base(_) | SignedBeaconBlock::Altair(_) - | SignedBeaconBlock::Merge(_) + | SignedBeaconBlock::Bellatrix(_) | SignedBeaconBlock::Capella(_) => (signed_block, None), SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => { (signed_block, block_response.blob_items) @@ -946,7 +946,7 @@ where let block_contents: SignedBlockContentsTuple = match *signed_block { SignedBeaconBlock::Base(_) | SignedBeaconBlock::Altair(_) - | SignedBeaconBlock::Merge(_) + | SignedBeaconBlock::Bellatrix(_) | SignedBeaconBlock::Capella(_) => (signed_block, None), SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => { (signed_block, block_response.blob_items) diff --git a/beacon_node/beacon_chain/tests/attestation_verification.rs b/beacon_node/beacon_chain/tests/attestation_verification.rs index 02caf831fe4..1327348713c 100644 --- a/beacon_node/beacon_chain/tests/attestation_verification.rs +++ b/beacon_node/beacon_chain/tests/attestation_verification.rs @@ -336,7 +336,7 @@ impl GossipTester { pub fn earliest_valid_attestation_slot(&self) -> Slot { let offset = match self.harness.spec.fork_name_at_epoch(self.epoch()) { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { // Subtract an additional slot since the harness will be exactly on the start of the // slot and the propagation tolerance will allow an extra slot. E::slots_per_epoch() + 1 @@ -1426,7 +1426,10 @@ async fn attestation_verification_use_head_state_fork() { .block_at_slot(pre_capella_slot, WhenSlotSkipped::Prev) .expect("should not error getting block at slot") .expect("should find block at slot"); - assert_eq!(pre_capella_block.fork_name(&spec).unwrap(), ForkName::Merge); + assert_eq!( + pre_capella_block.fork_name(&spec).unwrap(), + ForkName::Bellatrix + ); // Advance slot clock to Capella fork. harness.advance_slot(); @@ -1471,7 +1474,7 @@ async fn attestation_verification_use_head_state_fork() { // Scenario 2: other node forgot to update their node and signed attestations using bellatrix fork { let attesters = (VALIDATOR_COUNT / 2..VALIDATOR_COUNT).collect::>(); - let merge_fork = spec.fork_for_name(ForkName::Merge).unwrap(); + let bellatrix_fork = spec.fork_for_name(ForkName::Bellatrix).unwrap(); let committee_attestations = harness .make_unaggregated_attestations_with_opts( attesters.as_slice(), @@ -1480,7 +1483,7 @@ async fn attestation_verification_use_head_state_fork() { pre_capella_block.canonical_root().into(), first_capella_slot, MakeAttestationOptions { - fork: merge_fork, + fork: bellatrix_fork, limit: None, }, ) @@ -1527,7 +1530,10 @@ async fn aggregated_attestation_verification_use_head_state_fork() { .block_at_slot(pre_capella_slot, WhenSlotSkipped::Prev) .expect("should not error getting block at slot") .expect("should find block at slot"); - assert_eq!(pre_capella_block.fork_name(&spec).unwrap(), ForkName::Merge); + assert_eq!( + pre_capella_block.fork_name(&spec).unwrap(), + ForkName::Bellatrix + ); // Advance slot clock to Capella fork. harness.advance_slot(); @@ -1569,7 +1575,7 @@ async fn aggregated_attestation_verification_use_head_state_fork() { // Scenario 2: other node forgot to update their node and signed attestations using bellatrix fork { let attesters = (VALIDATOR_COUNT / 2..VALIDATOR_COUNT).collect::>(); - let merge_fork = spec.fork_for_name(ForkName::Merge).unwrap(); + let bellatrix_fork = spec.fork_for_name(ForkName::Bellatrix).unwrap(); let aggregates = harness .make_attestations_with_opts( attesters.as_slice(), @@ -1578,7 +1584,7 @@ async fn aggregated_attestation_verification_use_head_state_fork() { pre_capella_block.canonical_root().into(), first_capella_slot, MakeAttestationOptions { - fork: merge_fork, + fork: bellatrix_fork, limit: None, }, ) diff --git a/beacon_node/beacon_chain/tests/merge.rs b/beacon_node/beacon_chain/tests/bellatrix.rs similarity index 81% rename from beacon_node/beacon_chain/tests/merge.rs rename to beacon_node/beacon_chain/tests/bellatrix.rs index bff5c4523d1..027082c11c9 100644 --- a/beacon_node/beacon_chain/tests/merge.rs +++ b/beacon_node/beacon_chain/tests/bellatrix.rs @@ -71,9 +71,9 @@ async fn merge_with_terminal_block_hash_override() { .chain .head_snapshot() .beacon_block - .as_merge() + .as_bellatrix() .is_ok(), - "genesis block should be a merge block" + "genesis block should be a bellatrix block" ); let mut execution_payloads = vec![]; @@ -93,11 +93,11 @@ async fn merge_with_terminal_block_hash_override() { } #[tokio::test] -async fn base_altair_merge_with_terminal_block_after_fork() { +async fn base_altair_bellatrix_with_terminal_block_after_fork() { let altair_fork_epoch = Epoch::new(4); let altair_fork_slot = altair_fork_epoch.start_slot(E::slots_per_epoch()); let bellatrix_fork_epoch = Epoch::new(8); - let merge_fork_slot = bellatrix_fork_epoch.start_slot(E::slots_per_epoch()); + let bellatrix_fork_slot = bellatrix_fork_epoch.start_slot(E::slots_per_epoch()); let mut spec = E::default_spec(); spec.altair_fork_epoch = Some(altair_fork_epoch); @@ -130,41 +130,41 @@ async fn base_altair_merge_with_terminal_block_after_fork() { assert_eq!(altair_head.slot(), altair_fork_slot); /* - * Do the merge fork, without a terminal PoW block. + * Do the Bellatrix fork, without a terminal PoW block. */ - harness.extend_to_slot(merge_fork_slot).await; + harness.extend_to_slot(bellatrix_fork_slot).await; - let merge_head = &harness.chain.head_snapshot().beacon_block; - assert!(merge_head.as_merge().is_ok()); - assert_eq!(merge_head.slot(), merge_fork_slot); + let bellatrix_head = &harness.chain.head_snapshot().beacon_block; + assert!(bellatrix_head.as_bellatrix().is_ok()); + assert_eq!(bellatrix_head.slot(), bellatrix_fork_slot); assert!( - merge_head + bellatrix_head .message() .body() .execution_payload() .unwrap() .is_default_with_empty_roots(), - "Merge head is default payload" + "Bellatrix head is default payload" ); /* - * Next merge block shouldn't include an exec payload. + * Next Bellatrix block shouldn't include an exec payload. */ harness.extend_slots(1).await; - let one_after_merge_head = &harness.chain.head_snapshot().beacon_block; + let one_after_bellatrix_head = &harness.chain.head_snapshot().beacon_block; assert!( - one_after_merge_head + one_after_bellatrix_head .message() .body() .execution_payload() .unwrap() .is_default_with_empty_roots(), - "One after merge head is default payload" + "One after bellatrix head is default payload" ); - assert_eq!(one_after_merge_head.slot(), merge_fork_slot + 1); + assert_eq!(one_after_bellatrix_head.slot(), bellatrix_fork_slot + 1); /* * Trigger the terminal PoW block. @@ -188,20 +188,20 @@ async fn base_altair_merge_with_terminal_block_after_fork() { harness.extend_slots(1).await; - let two_after_merge_head = &harness.chain.head_snapshot().beacon_block; + let two_after_bellatrix_head = &harness.chain.head_snapshot().beacon_block; assert!( - two_after_merge_head + two_after_bellatrix_head .message() .body() .execution_payload() .unwrap() .is_default_with_empty_roots(), - "Two after merge head is default payload" + "Two after bellatrix head is default payload" ); - assert_eq!(two_after_merge_head.slot(), merge_fork_slot + 2); + assert_eq!(two_after_bellatrix_head.slot(), bellatrix_fork_slot + 2); /* - * Next merge block should include an exec payload. + * Next Bellatrix block should include an exec payload. */ for _ in 0..4 { harness.extend_slots(1).await; diff --git a/beacon_node/beacon_chain/tests/block_verification.rs b/beacon_node/beacon_chain/tests/block_verification.rs index 7b2c1addd28..e24894b242c 100644 --- a/beacon_node/beacon_chain/tests/block_verification.rs +++ b/beacon_node/beacon_chain/tests/block_verification.rs @@ -701,7 +701,7 @@ async fn invalid_signature_attester_slashing() { .push(attester_slashing) .expect("should update attester slashing"); } - BeaconBlockBodyRefMut::Merge(ref mut blk) => { + BeaconBlockBodyRefMut::Bellatrix(ref mut blk) => { blk.attester_slashings .push(attester_slashing) .expect("should update attester slashing"); @@ -758,7 +758,7 @@ async fn invalid_signature_attestation() { .attestations .get_mut(0) .map(|att| att.signature = junk_aggregate_signature()), - BeaconBlockBodyRefMut::Merge(ref mut blk) => blk + BeaconBlockBodyRefMut::Bellatrix(ref mut blk) => blk .attestations .get_mut(0) .map(|att| att.signature = junk_aggregate_signature()), diff --git a/beacon_node/beacon_chain/tests/capella.rs b/beacon_node/beacon_chain/tests/capella.rs index dc40280f530..c8fd2637f0c 100644 --- a/beacon_node/beacon_chain/tests/capella.rs +++ b/beacon_node/beacon_chain/tests/capella.rs @@ -25,11 +25,11 @@ fn verify_execution_payload_chain(chain: &[FullPayload]) { } #[tokio::test] -async fn base_altair_merge_capella() { +async fn base_altair_bellatrix_capella() { let altair_fork_epoch = Epoch::new(4); let altair_fork_slot = altair_fork_epoch.start_slot(E::slots_per_epoch()); let bellatrix_fork_epoch = Epoch::new(8); - let merge_fork_slot = bellatrix_fork_epoch.start_slot(E::slots_per_epoch()); + let bellatrix_fork_slot = bellatrix_fork_epoch.start_slot(E::slots_per_epoch()); let capella_fork_epoch = Epoch::new(12); let capella_fork_slot = capella_fork_epoch.start_slot(E::slots_per_epoch()); @@ -61,39 +61,39 @@ async fn base_altair_merge_capella() { assert_eq!(altair_head.slot(), altair_fork_slot); /* - * Do the merge fork, without a terminal PoW block. + * Do the Bellatrix fork, without a terminal PoW block. */ - harness.extend_to_slot(merge_fork_slot).await; + harness.extend_to_slot(bellatrix_fork_slot).await; - let merge_head = &harness.chain.head_snapshot().beacon_block; - assert!(merge_head.as_merge().is_ok()); - assert_eq!(merge_head.slot(), merge_fork_slot); + let bellatrix_head = &harness.chain.head_snapshot().beacon_block; + assert!(bellatrix_head.as_bellatrix().is_ok()); + assert_eq!(bellatrix_head.slot(), bellatrix_fork_slot); assert!( - merge_head + bellatrix_head .message() .body() .execution_payload() .unwrap() .is_default_with_empty_roots(), - "Merge head is default payload" + "Bellatrix head is default payload" ); /* - * Next merge block shouldn't include an exec payload. + * Next Bellatrix block shouldn't include an exec payload. */ harness.extend_slots(1).await; - let one_after_merge_head = &harness.chain.head_snapshot().beacon_block; + let one_after_bellatrix_head = &harness.chain.head_snapshot().beacon_block; assert!( - one_after_merge_head + one_after_bellatrix_head .message() .body() .execution_payload() .unwrap() .is_default_with_empty_roots(), - "One after merge head is default payload" + "One after bellatrix head is default payload" ); - assert_eq!(one_after_merge_head.slot(), merge_fork_slot + 1); + assert_eq!(one_after_bellatrix_head.slot(), bellatrix_fork_slot + 1); /* * Trigger the terminal PoW block. @@ -114,23 +114,23 @@ async fn base_altair_merge_capella() { }); harness.extend_slots(1).await; - let two_after_merge_head = &harness.chain.head_snapshot().beacon_block; + let two_after_bellatrix_head = &harness.chain.head_snapshot().beacon_block; assert!( - two_after_merge_head + two_after_bellatrix_head .message() .body() .execution_payload() .unwrap() .is_default_with_empty_roots(), - "Two after merge head is default payload" + "Two after bellatrix head is default payload" ); - assert_eq!(two_after_merge_head.slot(), merge_fork_slot + 2); + assert_eq!(two_after_bellatrix_head.slot(), bellatrix_fork_slot + 2); /* - * Next merge block should include an exec payload. + * Next Bellatrix block should include an exec payload. */ let mut execution_payloads = vec![]; - for _ in (merge_fork_slot.as_u64() + 3)..capella_fork_slot.as_u64() { + for _ in (bellatrix_fork_slot.as_u64() + 3)..capella_fork_slot.as_u64() { harness.extend_slots(1).await; let block = &harness.chain.head_snapshot().beacon_block; let full_payload: FullPayload = diff --git a/beacon_node/beacon_chain/tests/main.rs b/beacon_node/beacon_chain/tests/main.rs index e0564e1510b..942ce816846 100644 --- a/beacon_node/beacon_chain/tests/main.rs +++ b/beacon_node/beacon_chain/tests/main.rs @@ -1,9 +1,9 @@ mod attestation_production; mod attestation_verification; +mod bellatrix; mod block_verification; mod capella; mod events; -mod merge; mod op_verification; mod payload_invalidation; mod rewards; diff --git a/beacon_node/beacon_chain/tests/validator_monitor.rs b/beacon_node/beacon_chain/tests/validator_monitor.rs index f595e5037e2..ea9ef73575f 100644 --- a/beacon_node/beacon_chain/tests/validator_monitor.rs +++ b/beacon_node/beacon_chain/tests/validator_monitor.rs @@ -211,7 +211,7 @@ async fn produces_missed_blocks() { // `validator_indexes[slot_in_epoch.as_usize()]` and add it below. let validator_index_to_monitor_altair = 2; // Same as above but for the merge upgrade - let validator_index_to_monitor_merge = 4; + let validator_index_to_monitor_bellatrix = 4; // Same as above but for the capella upgrade let validator_index_to_monitor_capella = 11; // Same as above but for the deneb upgrade @@ -224,7 +224,7 @@ async fn produces_missed_blocks() { vec![ validator_index_to_monitor, validator_index_to_monitor_altair, - validator_index_to_monitor_merge, + validator_index_to_monitor_bellatrix, validator_index_to_monitor_capella, validator_index_to_monitor_deneb, validator_index_to_monitor_electra, diff --git a/beacon_node/client/src/notifier.rs b/beacon_node/client/src/notifier.rs index 912babdae31..a6fd07789d8 100644 --- a/beacon_node/client/src/notifier.rs +++ b/beacon_node/client/src/notifier.rs @@ -1,9 +1,9 @@ use crate::metrics; use beacon_chain::{ + bellatrix_readiness::{BellatrixReadiness, GenesisExecutionPayloadStatus, MergeConfig}, capella_readiness::CapellaReadiness, deneb_readiness::DenebReadiness, electra_readiness::ElectraReadiness, - merge_readiness::{GenesisExecutionPayloadStatus, MergeConfig, MergeReadiness}, BeaconChain, BeaconChainTypes, ExecutionStatus, }; use lighthouse_network::{types::SyncState, NetworkGlobals}; @@ -64,7 +64,7 @@ pub fn spawn_notifier( "wait_time" => estimated_time_pretty(Some(next_slot.as_secs() as f64)), ); eth1_logging(&beacon_chain, &log); - merge_readiness_logging(Slot::new(0), &beacon_chain, &log).await; + bellatrix_readiness_logging(Slot::new(0), &beacon_chain, &log).await; capella_readiness_logging(Slot::new(0), &beacon_chain, &log).await; genesis_execution_payload_logging(&beacon_chain, &log).await; sleep(slot_duration).await; @@ -319,7 +319,7 @@ pub fn spawn_notifier( } eth1_logging(&beacon_chain, &log); - merge_readiness_logging(current_slot, &beacon_chain, &log).await; + bellatrix_readiness_logging(current_slot, &beacon_chain, &log).await; capella_readiness_logging(current_slot, &beacon_chain, &log).await; deneb_readiness_logging(current_slot, &beacon_chain, &log).await; electra_readiness_logging(current_slot, &beacon_chain, &log).await; @@ -334,7 +334,7 @@ pub fn spawn_notifier( /// Provides some helpful logging to users to indicate if their node is ready for the Bellatrix /// fork and subsequent merge transition. -async fn merge_readiness_logging( +async fn bellatrix_readiness_logging( current_slot: Slot, beacon_chain: &BeaconChain, log: &Logger, @@ -372,8 +372,8 @@ async fn merge_readiness_logging( return; } - match beacon_chain.check_merge_readiness(current_slot).await { - MergeReadiness::Ready { + match beacon_chain.check_bellatrix_readiness(current_slot).await { + BellatrixReadiness::Ready { config, current_difficulty, } => match config { @@ -384,7 +384,7 @@ async fn merge_readiness_logging( } => { info!( log, - "Ready for the merge"; + "Ready for Bellatrix"; "terminal_total_difficulty" => %ttd, "current_difficulty" => current_difficulty .map(|d| d.to_string()) @@ -398,7 +398,7 @@ async fn merge_readiness_logging( } => { info!( log, - "Ready for the merge"; + "Ready for Bellatrix"; "info" => "you are using override parameters, please ensure that you \ understand these parameters and their implications.", "terminal_block_hash" => ?terminal_block_hash, @@ -411,14 +411,14 @@ async fn merge_readiness_logging( "config" => ?other ), }, - readiness @ MergeReadiness::NotSynced => warn!( + readiness @ BellatrixReadiness::NotSynced => warn!( log, - "Not ready for merge"; + "Not ready Bellatrix"; "info" => %readiness, ), - readiness @ MergeReadiness::NoExecutionEndpoint => warn!( + readiness @ BellatrixReadiness::NoExecutionEndpoint => warn!( log, - "Not ready for merge"; + "Not ready for Bellatrix"; "info" => %readiness, ), } diff --git a/beacon_node/execution_layer/src/block_hash.rs b/beacon_node/execution_layer/src/block_hash.rs index 1f8c29f6b25..0d0cfaf352c 100644 --- a/beacon_node/execution_layer/src/block_hash.rs +++ b/beacon_node/execution_layer/src/block_hash.rs @@ -146,7 +146,7 @@ mod test { } #[test] - fn test_rlp_encode_merge_block() { + fn test_rlp_encode_bellatrix_block() { let header = ExecutionBlockHeader { parent_hash: Hash256::from_str("927ca537f06c783a3a2635b8805eef1c8c2124f7444ad4a3389898dd832f2dbe").unwrap(), ommers_hash: Hash256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index d422994c595..ce1e0fec5dd 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -25,8 +25,8 @@ pub use types::{ Withdrawal, Withdrawals, }; use types::{ - ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadMerge, - KzgProofs, + ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, + ExecutionPayloadElectra, KzgProofs, }; use types::{Graffiti, GRAFFITI_BYTES_LEN}; @@ -36,8 +36,8 @@ pub mod json_structures; mod new_payload_request; pub use new_payload_request::{ - NewPayloadRequest, NewPayloadRequestCapella, NewPayloadRequestDeneb, NewPayloadRequestElectra, - NewPayloadRequestMerge, + NewPayloadRequest, NewPayloadRequestBellatrix, NewPayloadRequestCapella, + NewPayloadRequestDeneb, NewPayloadRequestElectra, }; pub const LATEST_TAG: &str = "latest"; @@ -155,7 +155,7 @@ pub struct ExecutionBlock { /// Representation of an execution block with enough detail to reconstruct a payload. #[superstruct( - variants(Merge, Capella, Deneb, Electra), + variants(Bellatrix, Capella, Deneb, Electra), variant_attributes( derive(Clone, Debug, PartialEq, Serialize, Deserialize,), serde(bound = "E: EthSpec", rename_all = "camelCase"), @@ -204,26 +204,28 @@ impl TryFrom> for ExecutionBlockWithTransactions fn try_from(payload: ExecutionPayload) -> Result { let json_payload = match payload { - ExecutionPayload::Merge(block) => Self::Merge(ExecutionBlockWithTransactionsMerge { - parent_hash: block.parent_hash, - fee_recipient: block.fee_recipient, - state_root: block.state_root, - receipts_root: block.receipts_root, - logs_bloom: block.logs_bloom, - prev_randao: block.prev_randao, - block_number: block.block_number, - gas_limit: block.gas_limit, - gas_used: block.gas_used, - timestamp: block.timestamp, - extra_data: block.extra_data, - base_fee_per_gas: block.base_fee_per_gas, - block_hash: block.block_hash, - transactions: block - .transactions - .iter() - .map(|tx| Transaction::decode(&Rlp::new(tx))) - .collect::, _>>()?, - }), + ExecutionPayload::Bellatrix(block) => { + Self::Bellatrix(ExecutionBlockWithTransactionsBellatrix { + parent_hash: block.parent_hash, + fee_recipient: block.fee_recipient, + state_root: block.state_root, + receipts_root: block.receipts_root, + logs_bloom: block.logs_bloom, + prev_randao: block.prev_randao, + block_number: block.block_number, + gas_limit: block.gas_limit, + gas_used: block.gas_used, + timestamp: block.timestamp, + extra_data: block.extra_data, + base_fee_per_gas: block.base_fee_per_gas, + block_hash: block.block_hash, + transactions: block + .transactions + .iter() + .map(|tx| Transaction::decode(&Rlp::new(tx))) + .collect::, _>>()?, + }) + } ExecutionPayload::Capella(block) => { Self::Capella(ExecutionBlockWithTransactionsCapella { parent_hash: block.parent_hash, @@ -423,7 +425,7 @@ pub struct ProposeBlindedBlockResponse { } #[superstruct( - variants(Merge, Capella, Deneb, Electra), + variants(Bellatrix, Capella, Deneb, Electra), variant_attributes(derive(Clone, Debug, PartialEq),), map_into(ExecutionPayload), map_ref_into(ExecutionPayloadRef), @@ -432,8 +434,11 @@ pub struct ProposeBlindedBlockResponse { )] #[derive(Clone, Debug, PartialEq)] pub struct GetPayloadResponse { - #[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))] - pub execution_payload: ExecutionPayloadMerge, + #[superstruct( + only(Bellatrix), + partial_getter(rename = "execution_payload_bellatrix") + )] + pub execution_payload: ExecutionPayloadBellatrix, #[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))] pub execution_payload: ExecutionPayloadCapella, #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))] @@ -482,8 +487,8 @@ impl From> { fn from(response: GetPayloadResponse) -> Self { match response { - GetPayloadResponse::Merge(inner) => ( - ExecutionPayload::Merge(inner.execution_payload), + GetPayloadResponse::Bellatrix(inner) => ( + ExecutionPayload::Bellatrix(inner.execution_payload), inner.block_value, None, ), @@ -529,14 +534,14 @@ impl ExecutionPayloadBodyV1 { header: ExecutionPayloadHeader, ) -> Result, String> { match header { - ExecutionPayloadHeader::Merge(header) => { + ExecutionPayloadHeader::Bellatrix(header) => { if self.withdrawals.is_some() { return Err(format!( "block {} is merge but payload body has withdrawals", header.block_hash )); } - Ok(ExecutionPayload::Merge(ExecutionPayloadMerge { + Ok(ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix { parent_hash: header.parent_hash, fee_recipient: header.fee_recipient, state_root: header.state_root, @@ -627,6 +632,9 @@ impl ExecutionPayloadBodyV1 { withdrawals, blob_gas_used: header.blob_gas_used, excess_blob_gas: header.excess_blob_gas, + // TODO(electra) + deposit_receipts: <_>::default(), + withdrawal_requests: <_>::default(), })) } else { Err(format!( diff --git a/beacon_node/execution_layer/src/engine_api/http.rs b/beacon_node/execution_layer/src/engine_api/http.rs index 405416e4f9d..93705a16925 100644 --- a/beacon_node/execution_layer/src/engine_api/http.rs +++ b/beacon_node/execution_layer/src/engine_api/http.rs @@ -737,7 +737,7 @@ impl HttpJsonRpc { ) -> Result>, Error> { let params = json!([block_hash, true]); Ok(Some(match fork { - ForkName::Merge => ExecutionBlockWithTransactions::Merge( + ForkName::Bellatrix => ExecutionBlockWithTransactions::Bellatrix( self.rpc_request( ETH_GET_BLOCK_BY_HASH, params, @@ -868,7 +868,7 @@ impl HttpJsonRpc { ) .await?; - Ok(GetPayloadResponse::Merge(GetPayloadResponseMerge { + Ok(GetPayloadResponse::Bellatrix(GetPayloadResponseBellatrix { execution_payload: payload_v1.into(), // Set the V1 payload values from the EE to be zero. This simulates // the pre-block-value functionality of always choosing the builder @@ -885,7 +885,7 @@ impl HttpJsonRpc { let params = json!([JsonPayloadIdRequest::from(payload_id)]); match fork_name { - ForkName::Merge => { + ForkName::Bellatrix => { let response: JsonGetPayloadResponseV1 = self .rpc_request( ENGINE_GET_PAYLOAD_V2, @@ -939,7 +939,7 @@ impl HttpJsonRpc { .await?; Ok(JsonGetPayloadResponse::V4(response).into()) } - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => Err( + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => Err( Error::UnsupportedForkVariant(format!("called get_payload_v3 with {}", fork_name)), ), } @@ -1180,7 +1180,7 @@ impl HttpJsonRpc { ) -> Result { let engine_capabilities = self.get_engine_capabilities(None).await?; match new_payload_request { - NewPayloadRequest::Merge(_) | NewPayloadRequest::Capella(_) => { + NewPayloadRequest::Bellatrix(_) | NewPayloadRequest::Capella(_) => { if engine_capabilities.new_payload_v2 { self.new_payload_v2(new_payload_request.into_execution_payload()) .await @@ -1218,7 +1218,7 @@ impl HttpJsonRpc { ) -> Result, Error> { let engine_capabilities = self.get_engine_capabilities(None).await?; match fork_name { - ForkName::Merge | ForkName::Capella => { + ForkName::Bellatrix | ForkName::Capella => { if engine_capabilities.get_payload_v2 { self.get_payload_v2(fork_name, payload_id).await } else if engine_capabilities.new_payload_v1 { @@ -1659,8 +1659,8 @@ mod test { .assert_request_equals( |client| async move { let _ = client - .new_payload_v1::(ExecutionPayload::Merge( - ExecutionPayloadMerge { + .new_payload_v1::(ExecutionPayload::Bellatrix( + ExecutionPayloadBellatrix { parent_hash: ExecutionBlockHash::repeat_byte(0), fee_recipient: Address::repeat_byte(1), state_root: Hash256::repeat_byte(1), @@ -1706,8 +1706,8 @@ mod test { Tester::new(false) .assert_auth_failure(|client| async move { client - .new_payload_v1::(ExecutionPayload::Merge( - ExecutionPayloadMerge { + .new_payload_v1::(ExecutionPayload::Bellatrix( + ExecutionPayloadBellatrix { parent_hash: ExecutionBlockHash::repeat_byte(0), fee_recipient: Address::repeat_byte(1), state_root: Hash256::repeat_byte(1), @@ -1917,7 +1917,7 @@ mod test { .unwrap() .into(); - let expected = ExecutionPayload::Merge(ExecutionPayloadMerge { + let expected = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix { parent_hash: ExecutionBlockHash::from_str("0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a").unwrap(), fee_recipient: Address::from_str("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(), state_root: Hash256::from_str("0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45").unwrap(), @@ -1942,7 +1942,7 @@ mod test { // engine_newPayloadV1 REQUEST validation |client| async move { let _ = client - .new_payload_v1::(ExecutionPayload::Merge(ExecutionPayloadMerge{ + .new_payload_v1::(ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix{ parent_hash: ExecutionBlockHash::from_str("0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a").unwrap(), fee_recipient: Address::from_str("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(), state_root: Hash256::from_str("0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45").unwrap(), @@ -1996,7 +1996,7 @@ mod test { })], |client| async move { let response = client - .new_payload_v1::(ExecutionPayload::Merge(ExecutionPayloadMerge::default())) + .new_payload_v1::(ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix::default())) .await .unwrap(); diff --git a/beacon_node/execution_layer/src/engine_api/json_structures.rs b/beacon_node/execution_layer/src/engine_api/json_structures.rs index d784aa4cd9c..50d3519e129 100644 --- a/beacon_node/execution_layer/src/engine_api/json_structures.rs +++ b/beacon_node/execution_layer/src/engine_api/json_structures.rs @@ -103,8 +103,8 @@ pub struct JsonExecutionPayload { pub excess_blob_gas: u64, } -impl From> for JsonExecutionPayloadV1 { - fn from(payload: ExecutionPayloadMerge) -> Self { +impl From> for JsonExecutionPayloadV1 { + fn from(payload: ExecutionPayloadBellatrix) -> Self { JsonExecutionPayloadV1 { parent_hash: payload.parent_hash, fee_recipient: payload.fee_recipient, @@ -210,7 +210,7 @@ impl From> for JsonExecutionPayloadV4 impl From> for JsonExecutionPayload { fn from(execution_payload: ExecutionPayload) -> Self { match execution_payload { - ExecutionPayload::Merge(payload) => JsonExecutionPayload::V1(payload.into()), + ExecutionPayload::Bellatrix(payload) => JsonExecutionPayload::V1(payload.into()), ExecutionPayload::Capella(payload) => JsonExecutionPayload::V2(payload.into()), ExecutionPayload::Deneb(payload) => JsonExecutionPayload::V3(payload.into()), ExecutionPayload::Electra(payload) => JsonExecutionPayload::V4(payload.into()), @@ -218,9 +218,9 @@ impl From> for JsonExecutionPayload { } } -impl From> for ExecutionPayloadMerge { +impl From> for ExecutionPayloadBellatrix { fn from(payload: JsonExecutionPayloadV1) -> Self { - ExecutionPayloadMerge { + ExecutionPayloadBellatrix { parent_hash: payload.parent_hash, fee_recipient: payload.fee_recipient, state_root: payload.state_root, @@ -319,6 +319,9 @@ impl From> for ExecutionPayloadElectra .into(), blob_gas_used: payload.blob_gas_used, excess_blob_gas: payload.excess_blob_gas, + // TODO(electra) + deposit_receipts: Default::default(), + withdrawal_requests: Default::default(), } } } @@ -326,7 +329,7 @@ impl From> for ExecutionPayloadElectra impl From> for ExecutionPayload { fn from(json_execution_payload: JsonExecutionPayload) -> Self { match json_execution_payload { - JsonExecutionPayload::V1(payload) => ExecutionPayload::Merge(payload.into()), + JsonExecutionPayload::V1(payload) => ExecutionPayload::Bellatrix(payload.into()), JsonExecutionPayload::V2(payload) => ExecutionPayload::Capella(payload.into()), JsonExecutionPayload::V3(payload) => ExecutionPayload::Deneb(payload.into()), JsonExecutionPayload::V4(payload) => ExecutionPayload::Electra(payload.into()), @@ -366,7 +369,7 @@ impl From> for GetPayloadResponse { fn from(json_get_payload_response: JsonGetPayloadResponse) -> Self { match json_get_payload_response { JsonGetPayloadResponse::V1(response) => { - GetPayloadResponse::Merge(GetPayloadResponseMerge { + GetPayloadResponse::Bellatrix(GetPayloadResponseBellatrix { execution_payload: response.execution_payload.into(), block_value: response.block_value, }) diff --git a/beacon_node/execution_layer/src/engine_api/new_payload_request.rs b/beacon_node/execution_layer/src/engine_api/new_payload_request.rs index 6b6df13b704..8d2e3d5ad06 100644 --- a/beacon_node/execution_layer/src/engine_api/new_payload_request.rs +++ b/beacon_node/execution_layer/src/engine_api/new_payload_request.rs @@ -8,11 +8,12 @@ use types::{ ExecutionPayloadRef, Hash256, VersionedHash, }; use types::{ - ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadMerge, + ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, + ExecutionPayloadElectra, }; #[superstruct( - variants(Merge, Capella, Deneb, Electra), + variants(Bellatrix, Capella, Deneb, Electra), variant_attributes(derive(Clone, Debug, PartialEq),), map_into(ExecutionPayload), map_ref_into(ExecutionPayloadRef), @@ -27,8 +28,11 @@ use types::{ )] #[derive(Clone, Debug, PartialEq)] pub struct NewPayloadRequest<'block, E: EthSpec> { - #[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))] - pub execution_payload: &'block ExecutionPayloadMerge, + #[superstruct( + only(Bellatrix), + partial_getter(rename = "execution_payload_bellatrix") + )] + pub execution_payload: &'block ExecutionPayloadBellatrix, #[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))] pub execution_payload: &'block ExecutionPayloadCapella, #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))] @@ -44,7 +48,7 @@ pub struct NewPayloadRequest<'block, E: EthSpec> { impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { pub fn parent_hash(&self) -> ExecutionBlockHash { match self { - Self::Merge(payload) => payload.execution_payload.parent_hash, + Self::Bellatrix(payload) => payload.execution_payload.parent_hash, Self::Capella(payload) => payload.execution_payload.parent_hash, Self::Deneb(payload) => payload.execution_payload.parent_hash, Self::Electra(payload) => payload.execution_payload.parent_hash, @@ -53,7 +57,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { pub fn block_hash(&self) -> ExecutionBlockHash { match self { - Self::Merge(payload) => payload.execution_payload.block_hash, + Self::Bellatrix(payload) => payload.execution_payload.block_hash, Self::Capella(payload) => payload.execution_payload.block_hash, Self::Deneb(payload) => payload.execution_payload.block_hash, Self::Electra(payload) => payload.execution_payload.block_hash, @@ -62,7 +66,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { pub fn block_number(&self) -> u64 { match self { - Self::Merge(payload) => payload.execution_payload.block_number, + Self::Bellatrix(payload) => payload.execution_payload.block_number, Self::Capella(payload) => payload.execution_payload.block_number, Self::Deneb(payload) => payload.execution_payload.block_number, Self::Electra(payload) => payload.execution_payload.block_number, @@ -71,7 +75,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<'block, E> { match self { - Self::Merge(request) => ExecutionPayloadRef::Merge(request.execution_payload), + Self::Bellatrix(request) => ExecutionPayloadRef::Bellatrix(request.execution_payload), Self::Capella(request) => ExecutionPayloadRef::Capella(request.execution_payload), Self::Deneb(request) => ExecutionPayloadRef::Deneb(request.execution_payload), Self::Electra(request) => ExecutionPayloadRef::Electra(request.execution_payload), @@ -80,7 +84,9 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> { pub fn into_execution_payload(self) -> ExecutionPayload { match self { - Self::Merge(request) => ExecutionPayload::Merge(request.execution_payload.clone()), + Self::Bellatrix(request) => { + ExecutionPayload::Bellatrix(request.execution_payload.clone()) + } Self::Capella(request) => ExecutionPayload::Capella(request.execution_payload.clone()), Self::Deneb(request) => ExecutionPayload::Deneb(request.execution_payload.clone()), Self::Electra(request) => ExecutionPayload::Electra(request.execution_payload.clone()), @@ -150,9 +156,11 @@ impl<'a, E: EthSpec> TryFrom> for NewPayloadRequest<'a, E> BeaconBlockRef::Base(_) | BeaconBlockRef::Altair(_) => { Err(Self::Error::IncorrectStateVariant) } - BeaconBlockRef::Merge(block_ref) => Ok(Self::Merge(NewPayloadRequestMerge { - execution_payload: &block_ref.body.execution_payload.execution_payload, - })), + BeaconBlockRef::Bellatrix(block_ref) => { + Ok(Self::Bellatrix(NewPayloadRequestBellatrix { + execution_payload: &block_ref.body.execution_payload.execution_payload, + })) + } BeaconBlockRef::Capella(block_ref) => Ok(Self::Capella(NewPayloadRequestCapella { execution_payload: &block_ref.body.execution_payload.execution_payload, })), @@ -185,9 +193,11 @@ impl<'a, E: EthSpec> TryFrom> for NewPayloadRequest<' fn try_from(payload: ExecutionPayloadRef<'a, E>) -> Result { match payload { - ExecutionPayloadRef::Merge(payload) => Ok(Self::Merge(NewPayloadRequestMerge { - execution_payload: payload, - })), + ExecutionPayloadRef::Bellatrix(payload) => { + Ok(Self::Bellatrix(NewPayloadRequestBellatrix { + execution_payload: payload, + })) + } ExecutionPayloadRef::Capella(payload) => Ok(Self::Capella(NewPayloadRequestCapella { execution_payload: payload, })), diff --git a/beacon_node/execution_layer/src/lib.rs b/beacon_node/execution_layer/src/lib.rs index 60f450a39de..d441596edda 100644 --- a/beacon_node/execution_layer/src/lib.rs +++ b/beacon_node/execution_layer/src/lib.rs @@ -50,8 +50,8 @@ use types::{ AbstractExecPayload, BlobsList, ExecutionPayloadDeneb, KzgProofs, SignedBlindedBeaconBlock, }; use types::{ - BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadCapella, - ExecutionPayloadElectra, ExecutionPayloadMerge, FullPayload, ProposerPreparationData, + BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadBellatrix, + ExecutionPayloadCapella, ExecutionPayloadElectra, FullPayload, ProposerPreparationData, PublicKeyBytes, Signature, Slot, }; @@ -98,8 +98,8 @@ impl TryFrom> for ProvenancedPayload) -> Result { let block_proposal_contents = match value { - BuilderBid::Merge(builder_bid) => BlockProposalContents::Payload { - payload: ExecutionPayloadHeader::Merge(builder_bid.header).into(), + BuilderBid::Bellatrix(builder_bid) => BlockProposalContents::Payload { + payload: ExecutionPayloadHeader::Bellatrix(builder_bid.header).into(), block_value: builder_bid.value, }, BuilderBid::Capella(builder_bid) => BlockProposalContents::Payload { @@ -1804,7 +1804,7 @@ impl ExecutionLayer { // Handle default payload body. if header.block_hash() == ExecutionBlockHash::zero() { let payload = match fork { - ForkName::Merge => ExecutionPayloadMerge::default().into(), + ForkName::Bellatrix => ExecutionPayloadBellatrix::default().into(), ForkName::Capella => ExecutionPayloadCapella::default().into(), ForkName::Deneb => ExecutionPayloadDeneb::default().into(), ForkName::Electra => ExecutionPayloadElectra::default().into(), @@ -1873,7 +1873,7 @@ impl ExecutionLayer { if hash == ExecutionBlockHash::zero() { return match fork { - ForkName::Merge => Ok(Some(ExecutionPayloadMerge::default().into())), + ForkName::Bellatrix => Ok(Some(ExecutionPayloadBellatrix::default().into())), ForkName::Capella => Ok(Some(ExecutionPayloadCapella::default().into())), ForkName::Deneb => Ok(Some(ExecutionPayloadDeneb::default().into())), ForkName::Electra => Ok(Some(ExecutionPayloadElectra::default().into())), @@ -1902,22 +1902,22 @@ impl ExecutionLayer { }; let payload = match block { - ExecutionBlockWithTransactions::Merge(merge_block) => { - ExecutionPayload::Merge(ExecutionPayloadMerge { - parent_hash: merge_block.parent_hash, - fee_recipient: merge_block.fee_recipient, - state_root: merge_block.state_root, - receipts_root: merge_block.receipts_root, - logs_bloom: merge_block.logs_bloom, - prev_randao: merge_block.prev_randao, - block_number: merge_block.block_number, - gas_limit: merge_block.gas_limit, - gas_used: merge_block.gas_used, - timestamp: merge_block.timestamp, - extra_data: merge_block.extra_data, - base_fee_per_gas: merge_block.base_fee_per_gas, - block_hash: merge_block.block_hash, - transactions: convert_transactions(merge_block.transactions)?, + ExecutionBlockWithTransactions::Bellatrix(bellatrix_block) => { + ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix { + parent_hash: bellatrix_block.parent_hash, + fee_recipient: bellatrix_block.fee_recipient, + state_root: bellatrix_block.state_root, + receipts_root: bellatrix_block.receipts_root, + logs_bloom: bellatrix_block.logs_bloom, + prev_randao: bellatrix_block.prev_randao, + block_number: bellatrix_block.block_number, + gas_limit: bellatrix_block.gas_limit, + gas_used: bellatrix_block.gas_used, + timestamp: bellatrix_block.timestamp, + extra_data: bellatrix_block.extra_data, + base_fee_per_gas: bellatrix_block.base_fee_per_gas, + block_hash: bellatrix_block.block_hash, + transactions: convert_transactions(bellatrix_block.transactions)?, }) } ExecutionBlockWithTransactions::Capella(capella_block) => { @@ -2003,6 +2003,11 @@ impl ExecutionLayer { withdrawals, blob_gas_used: electra_block.blob_gas_used, excess_blob_gas: electra_block.excess_blob_gas, + // TODO(electra) + // deposit_receipts: electra_block.deposit_receipts, + // withdrawal_requests: electra_block.withdrawal_requests, + deposit_receipts: <_>::default(), + withdrawal_requests: <_>::default(), }) } }; diff --git a/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs b/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs index bac2304fa85..e80c6b23705 100644 --- a/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs +++ b/beacon_node/execution_layer/src/test_utils/execution_block_generator.rs @@ -21,9 +21,9 @@ use std::sync::Arc; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; use types::{ - Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadCapella, - ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadHeader, ExecutionPayloadMerge, - ForkName, Hash256, Transaction, Transactions, Uint256, + Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix, + ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, + ExecutionPayloadHeader, ForkName, Hash256, Transaction, Transactions, Uint256, }; use super::DEFAULT_TERMINAL_BLOCK; @@ -92,7 +92,7 @@ impl Block { match self { Block::PoS(payload) => Some(payload.clone().try_into().unwrap()), Block::PoW(block) => Some( - ExecutionPayload::Merge(ExecutionPayloadMerge { + ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix { block_hash: block.block_hash, ..Default::default() }) @@ -232,7 +232,7 @@ impl ExecutionBlockGenerator { Some(fork_time) if timestamp >= fork_time => ForkName::Deneb, _ => match self.shanghai_time { Some(fork_time) if timestamp >= fork_time => ForkName::Capella, - _ => ForkName::Merge, + _ => ForkName::Bellatrix, }, }, } @@ -569,7 +569,7 @@ impl ExecutionBlockGenerator { attributes: &PayloadAttributes, ) -> Result, String> { let mut execution_payload = match attributes { - PayloadAttributes::V1(pa) => ExecutionPayload::Merge(ExecutionPayloadMerge { + PayloadAttributes::V1(pa) => ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix { parent_hash: head_block_hash, fee_recipient: pa.suggested_fee_recipient, receipts_root: Hash256::repeat_byte(42), @@ -586,7 +586,7 @@ impl ExecutionBlockGenerator { transactions: vec![].into(), }), PayloadAttributes::V2(pa) => match self.get_fork_at_timestamp(pa.timestamp) { - ForkName::Merge => ExecutionPayload::Merge(ExecutionPayloadMerge { + ForkName::Bellatrix => ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix { parent_hash: head_block_hash, fee_recipient: pa.suggested_fee_recipient, receipts_root: Hash256::repeat_byte(42), @@ -659,13 +659,15 @@ impl ExecutionBlockGenerator { withdrawals: pa.withdrawals.clone().into(), blob_gas_used: 0, excess_blob_gas: 0, + deposit_receipts: vec![].into(), + withdrawal_requests: vec![].into(), }), _ => unreachable!(), }, }; match execution_payload.fork_name() { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {} + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => {} ForkName::Deneb | ForkName::Electra => { // get random number between 0 and Max Blobs let mut rng = self.rng.lock(); @@ -782,14 +784,14 @@ pub fn generate_genesis_header( let empty_transactions_root = Transactions::::empty().tree_hash_root(); match genesis_fork { ForkName::Base | ForkName::Altair => None, - ForkName::Merge => { + ForkName::Bellatrix => { if post_transition_merge { - let mut header = ExecutionPayloadHeader::Merge(<_>::default()); + let mut header = ExecutionPayloadHeader::Bellatrix(<_>::default()); *header.block_hash_mut() = genesis_block_hash.unwrap_or_default(); *header.transactions_root_mut() = empty_transactions_root; Some(header) } else { - Some(ExecutionPayloadHeader::::Merge(<_>::default())) + Some(ExecutionPayloadHeader::::Bellatrix(<_>::default())) } } ForkName::Capella => { diff --git a/beacon_node/execution_layer/src/test_utils/handle_rpc.rs b/beacon_node/execution_layer/src/test_utils/handle_rpc.rs index c16bd43c82c..1dc8f0ab83e 100644 --- a/beacon_node/execution_layer/src/test_utils/handle_rpc.rs +++ b/beacon_node/execution_layer/src/test_utils/handle_rpc.rs @@ -135,7 +135,7 @@ pub async fn handle_rpc( .get_fork_at_timestamp(*request.timestamp()); // validate method called correctly according to fork time match fork { - ForkName::Merge => { + ForkName::Bellatrix => { if matches!(request, JsonExecutionPayload::V2(_)) { return Err(( format!( @@ -395,7 +395,7 @@ pub async fn handle_rpc( .read() .get_fork_at_timestamp(*pa.timestamp()) { - ForkName::Merge => { + ForkName::Bellatrix => { get_param::>(params, 1) .map(|opt| opt.map(JsonPayloadAttributes::V1)) .transpose() @@ -427,7 +427,7 @@ pub async fn handle_rpc( .read() .get_fork_at_timestamp(*pa.timestamp()) { - ForkName::Merge => { + ForkName::Bellatrix => { if matches!(pa, JsonPayloadAttributes::V2(_)) { return Err(( format!( diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 756e0b793f8..c9ae1e60cdc 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -15,7 +15,7 @@ use task_executor::TaskExecutor; use tempfile::NamedTempFile; use tree_hash::TreeHash; use types::builder_bid::{ - BuilderBid, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra, BuilderBidMerge, + BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra, SignedBuilderBid, }; use types::{ @@ -77,7 +77,7 @@ pub trait BidStuff { impl BidStuff for BuilderBid { fn set_fee_recipient(&mut self, fee_recipient: Address) { match self.to_mut().header_mut() { - ExecutionPayloadHeaderRefMut::Merge(header) => { + ExecutionPayloadHeaderRefMut::Bellatrix(header) => { header.fee_recipient = fee_recipient; } ExecutionPayloadHeaderRefMut::Capella(header) => { @@ -94,7 +94,7 @@ impl BidStuff for BuilderBid { fn set_gas_limit(&mut self, gas_limit: u64) { match self.to_mut().header_mut() { - ExecutionPayloadHeaderRefMut::Merge(header) => { + ExecutionPayloadHeaderRefMut::Bellatrix(header) => { header.gas_limit = gas_limit; } ExecutionPayloadHeaderRefMut::Capella(header) => { @@ -115,7 +115,7 @@ impl BidStuff for BuilderBid { fn set_parent_hash(&mut self, parent_hash: Hash256) { match self.to_mut().header_mut() { - ExecutionPayloadHeaderRefMut::Merge(header) => { + ExecutionPayloadHeaderRefMut::Bellatrix(header) => { header.parent_hash = ExecutionBlockHash::from_root(parent_hash); } ExecutionPayloadHeaderRefMut::Capella(header) => { @@ -132,7 +132,7 @@ impl BidStuff for BuilderBid { fn set_prev_randao(&mut self, prev_randao: Hash256) { match self.to_mut().header_mut() { - ExecutionPayloadHeaderRefMut::Merge(header) => { + ExecutionPayloadHeaderRefMut::Bellatrix(header) => { header.prev_randao = prev_randao; } ExecutionPayloadHeaderRefMut::Capella(header) => { @@ -149,7 +149,7 @@ impl BidStuff for BuilderBid { fn set_block_number(&mut self, block_number: u64) { match self.to_mut().header_mut() { - ExecutionPayloadHeaderRefMut::Merge(header) => { + ExecutionPayloadHeaderRefMut::Bellatrix(header) => { header.block_number = block_number; } ExecutionPayloadHeaderRefMut::Capella(header) => { @@ -166,7 +166,7 @@ impl BidStuff for BuilderBid { fn set_timestamp(&mut self, timestamp: u64) { match self.to_mut().header_mut() { - ExecutionPayloadHeaderRefMut::Merge(header) => { + ExecutionPayloadHeaderRefMut::Bellatrix(header) => { header.timestamp = timestamp; } ExecutionPayloadHeaderRefMut::Capella(header) => { @@ -183,7 +183,7 @@ impl BidStuff for BuilderBid { fn set_withdrawals_root(&mut self, withdrawals_root: Hash256) { match self.to_mut().header_mut() { - ExecutionPayloadHeaderRefMut::Merge(_) => { + ExecutionPayloadHeaderRefMut::Bellatrix(_) => { panic!("no withdrawals before capella") } ExecutionPayloadHeaderRefMut::Capella(header) => { @@ -336,7 +336,7 @@ pub fn serve( SignedBlindedBeaconBlock::Base(_) | types::SignedBeaconBlock::Altair(_) => { return Err(reject("invalid fork")); } - SignedBlindedBeaconBlock::Merge(block) => { + SignedBlindedBeaconBlock::Bellatrix(block) => { block.message.body.execution_payload.tree_hash_root() } SignedBlindedBeaconBlock::Capella(block) => { @@ -480,7 +480,7 @@ pub fn serve( .get_randao_mix(head_state.current_epoch()) .map_err(|_| reject("couldn't get prev randao"))?; let expected_withdrawals = match fork { - ForkName::Base | ForkName::Altair | ForkName::Merge => None, + ForkName::Base | ForkName::Altair | ForkName::Bellatrix => None, ForkName::Capella | ForkName::Deneb | ForkName::Electra => Some( builder .beacon_client @@ -496,7 +496,7 @@ pub fn serve( // first to avoid polluting the execution block generator with invalid payload attributes // NOTE: this was part of an effort to add payload attribute uniqueness checks, // which was abandoned because it broke too many tests in subtle ways. - ForkName::Merge | ForkName::Capella => PayloadAttributes::new( + ForkName::Bellatrix | ForkName::Capella => PayloadAttributes::new( timestamp, *prev_randao, fee_recipient, @@ -577,9 +577,9 @@ pub fn serve( value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), pubkey: builder.builder_sk.public_key().compress(), }), - ForkName::Merge => BuilderBid::Merge(BuilderBidMerge { + ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { header: payload - .as_merge() + .as_bellatrix() .map_err(|_| reject("incorrect payload variant"))? .into(), value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), @@ -627,9 +627,9 @@ pub fn serve( value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), pubkey: builder.builder_sk.public_key().compress(), }), - ForkName::Merge => BuilderBid::Merge(BuilderBidMerge { + ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { header: payload - .as_merge() + .as_bellatrix() .map_err(|_| reject("incorrect payload variant"))? .into(), value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), diff --git a/beacon_node/execution_layer/src/test_utils/mock_execution_layer.rs b/beacon_node/execution_layer/src/test_utils/mock_execution_layer.rs index 6717bbc2ab3..da9b2817f69 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_execution_layer.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_execution_layer.rs @@ -138,7 +138,7 @@ impl MockExecutionLayer { &payload_attributes, forkchoice_update_params, builder_params, - ForkName::Merge, + ForkName::Bellatrix, &self.spec, None, BlockProductionVersion::FullV2, @@ -178,7 +178,7 @@ impl MockExecutionLayer { &payload_attributes, forkchoice_update_params, builder_params, - ForkName::Merge, + ForkName::Bellatrix, &self.spec, None, BlockProductionVersion::BlindedV2, diff --git a/beacon_node/http_api/src/build_block_contents.rs b/beacon_node/http_api/src/build_block_contents.rs index 7e3778b3fbb..05a6735b327 100644 --- a/beacon_node/http_api/src/build_block_contents.rs +++ b/beacon_node/http_api/src/build_block_contents.rs @@ -12,7 +12,7 @@ pub fn build_block_contents( Ok(ProduceBlockV3Response::Blinded(block.block)) } BeaconBlockResponseWrapper::Full(block) => match fork_name { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => Ok( + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => Ok( ProduceBlockV3Response::Full(FullBlockContents::Block(block.block)), ), ForkName::Deneb | ForkName::Electra => { diff --git a/beacon_node/http_api/src/builder_states.rs b/beacon_node/http_api/src/builder_states.rs index 90203f2d60c..a540113ab43 100644 --- a/beacon_node/http_api/src/builder_states.rs +++ b/beacon_node/http_api/src/builder_states.rs @@ -53,7 +53,7 @@ fn get_next_withdrawals_sanity_checks( } let fork = chain.spec.fork_name_at_slot::(proposal_slot); - if let ForkName::Base | ForkName::Altair | ForkName::Merge = fork { + if let ForkName::Base | ForkName::Altair | ForkName::Bellatrix = fork { return Err(warp_utils::reject::custom_bad_request( "the specified state is a pre-capella state.".to_string(), )); diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index cc680c523ef..5580f820fbb 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -4301,7 +4301,7 @@ pub fn serve( |task_spawner: TaskSpawner, chain: Arc>| { task_spawner.spawn_async_with_rejection(Priority::P1, async move { let current_slot = chain.slot_clock.now_or_genesis().unwrap_or(Slot::new(0)); - let merge_readiness = chain.check_merge_readiness(current_slot).await; + let merge_readiness = chain.check_bellatrix_readiness(current_slot).await; Ok::<_, warp::reject::Rejection>( warp::reply::json(&api_types::GenericResponse::from(merge_readiness)) .into_response(), @@ -4363,6 +4363,12 @@ pub fn serve( api_types::EventTopic::BlockReward => { event_handler.subscribe_block_reward() } + api_types::EventTopic::AttesterSlashing => { + event_handler.subscribe_attester_slashing() + } + api_types::EventTopic::ProposerSlashing => { + event_handler.subscribe_proposer_slashing() + } }; receivers.push( diff --git a/beacon_node/http_api/src/publish_blocks.rs b/beacon_node/http_api/src/publish_blocks.rs index 52e77753ce4..0d176e6a53a 100644 --- a/beacon_node/http_api/src/publish_blocks.rs +++ b/beacon_node/http_api/src/publish_blocks.rs @@ -20,8 +20,8 @@ use tokio::sync::mpsc::UnboundedSender; use tree_hash::TreeHash; use types::{ AbstractExecPayload, BeaconBlockRef, BlobSidecarList, EthSpec, ExecPayload, ExecutionBlockHash, - ForkName, FullPayload, FullPayloadMerge, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, - VariableList, + ForkName, FullPayload, FullPayloadBellatrix, Hash256, SignedBeaconBlock, + SignedBlindedBeaconBlock, VariableList, }; use warp::http::StatusCode; use warp::{reply::Response, Rejection, Reply}; @@ -80,7 +80,7 @@ pub async fn publish_block { crate::publish_pubsub_message(&sender, PubsubMessage::BeaconBlock(block)) .map_err(|_| BlockError::BeaconChainError(BeaconChainError::UnableToPublish))?; @@ -331,8 +331,8 @@ pub async fn reconstruct_block( let fork_name = chain .spec .fork_name_at_epoch(block.slot().epoch(T::EthSpec::slots_per_epoch())); - if fork_name == ForkName::Merge { - let payload: FullPayload = FullPayloadMerge::default().into(); + if fork_name == ForkName::Bellatrix { + let payload: FullPayload = FullPayloadBellatrix::default().into(); ProvenancedPayload::Local(FullPayloadContents::Payload(payload.into())) } else { Err(warp_utils::reject::custom_server_error( diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 87d6a8405d1..790cc5b70b4 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -5230,6 +5230,8 @@ impl ApiTester { EventTopic::Block, EventTopic::Head, EventTopic::FinalizedCheckpoint, + EventTopic::AttesterSlashing, + EventTopic::ProposerSlashing, ]; let mut events_future = self .client @@ -5369,6 +5371,42 @@ impl ApiTester { .await; assert_eq!(reorg_event.as_slice(), &[expected_reorg]); + // Test attester slashing event + let mut attester_slashing_event_future = self + .client + .get_events::(&[EventTopic::AttesterSlashing]) + .await + .unwrap(); + + self.harness.add_attester_slashing(vec![1, 2, 3]).unwrap(); + + let attester_slashing_event = poll_events( + &mut attester_slashing_event_future, + 1, + Duration::from_millis(10000), + ) + .await; + + assert!(attester_slashing_event.len() == 1); + + // Test proposer slashing event + let mut proposer_slashing_event_future = self + .client + .get_events::(&[EventTopic::ProposerSlashing]) + .await + .unwrap(); + + self.harness.add_proposer_slashing(1).unwrap(); + + let proposer_slashing_event = poll_events( + &mut proposer_slashing_event_future, + 1, + Duration::from_millis(10000), + ) + .await; + + assert!(proposer_slashing_event.len() == 1); + self } diff --git a/beacon_node/lighthouse_network/src/config.rs b/beacon_node/lighthouse_network/src/config.rs index 03f530db4d1..91c5b62d0b2 100644 --- a/beacon_node/lighthouse_network/src/config.rs +++ b/beacon_node/lighthouse_network/src/config.rs @@ -438,7 +438,7 @@ pub fn gossipsub_config( let topic_bytes = message.topic.as_str().as_bytes(); match fork_context.current_fork() { ForkName::Altair - | ForkName::Merge + | ForkName::Bellatrix | ForkName::Capella | ForkName::Deneb | ForkName::Electra => { @@ -461,7 +461,7 @@ pub fn gossipsub_config( } } let message_domain_valid_snappy = gossipsub_config_params.message_domain_valid_snappy; - let is_merge_enabled = fork_context.fork_exists(ForkName::Merge); + let is_bellatrix_enabled = fork_context.fork_exists(ForkName::Bellatrix); let gossip_message_id = move |message: &gossipsub::Message| { gossipsub::MessageId::from( &Sha256::digest( @@ -481,7 +481,7 @@ pub fn gossipsub_config( gossipsub::ConfigBuilder::default() .max_transmit_size(gossip_max_size( - is_merge_enabled, + is_bellatrix_enabled, gossipsub_config_params.gossip_max_size, )) .heartbeat_interval(load.heartbeat_interval) diff --git a/beacon_node/lighthouse_network/src/discovery/mod.rs b/beacon_node/lighthouse_network/src/discovery/mod.rs index 8cc2ea86c0c..5c937a1e0be 100644 --- a/beacon_node/lighthouse_network/src/discovery/mod.rs +++ b/beacon_node/lighthouse_network/src/discovery/mod.rs @@ -398,16 +398,32 @@ impl Discovery { /// automatically update the external address. /// /// If the external address needs to be modified, use `update_enr_udp_socket. - pub fn update_enr_tcp_port(&mut self, port: u16) -> Result<(), String> { + /// + /// This returns Ok(true) if the ENR was updated, otherwise Ok(false) if nothing was done. + pub fn update_enr_tcp_port(&mut self, port: u16, v6: bool) -> Result { + let enr_field = if v6 { + if self.discv5.external_enr().read().tcp6() == Some(port) { + // The field is already set to the same value, nothing to do + return Ok(false); + } + "tcp6" + } else { + if self.discv5.external_enr().read().tcp4() == Some(port) { + // The field is already set to the same value, nothing to do + return Ok(false); + } + "tcp" + }; + self.discv5 - .enr_insert("tcp", &port) + .enr_insert(enr_field, &port) .map_err(|e| format!("{:?}", e))?; // replace the global version *self.network_globals.local_enr.write() = self.discv5.local_enr(); // persist modified enr to disk enr::save_enr_to_disk(Path::new(&self.enr_dir), &self.local_enr(), &self.log); - Ok(()) + Ok(true) } // TODO: Group these functions here once the ENR is shared across discv5 and lighthouse and @@ -415,16 +431,35 @@ impl Discovery { // This currently doesn't support ipv6. All of these functions should be removed and // addressed properly in the following issue. // https://github.com/sigp/lighthouse/issues/4706 - pub fn update_enr_quic_port(&mut self, port: u16) -> Result<(), String> { + pub fn update_enr_quic_port(&mut self, port: u16, v6: bool) -> Result { + let enr_field = if v6 { + if self.discv5.external_enr().read().quic6() == Some(port) { + // The field is already set to the same value, nothing to do + return Ok(false); + } + "quic6" + } else { + if self.discv5.external_enr().read().quic4() == Some(port) { + // The field is already set to the same value, nothing to do + return Ok(false); + } + "quic" + }; + let current_field = self.discv5.external_enr().read().quic4(); + if current_field == Some(port) { + // The current field is already set, no need to update. + return Ok(false); + } + self.discv5 - .enr_insert("quic", &port) + .enr_insert(enr_field, &port) .map_err(|e| format!("{:?}", e))?; // replace the global version *self.network_globals.local_enr.write() = self.discv5.local_enr(); // persist modified enr to disk enr::save_enr_to_disk(Path::new(&self.enr_dir), &self.local_enr(), &self.log); - Ok(()) + Ok(true) } /// Updates the local ENR UDP socket. @@ -1057,7 +1092,7 @@ impl NetworkBehaviour for Discovery { return; } - self.update_enr_tcp_port(port) + self.update_enr_tcp_port(port, false) } (Some(Protocol::Udp(port)), Some(Protocol::QuicV1)) => { if !self.update_ports.quic4 { @@ -1065,7 +1100,7 @@ impl NetworkBehaviour for Discovery { return; } - self.update_enr_quic_port(port) + self.update_enr_quic_port(port, false) } _ => { debug!(self.log, "Encountered unacceptable multiaddr for listening (unsupported transport)"; "addr" => ?addr); @@ -1079,7 +1114,7 @@ impl NetworkBehaviour for Discovery { return; } - self.update_enr_tcp_port(port) + self.update_enr_tcp_port(port, true) } (Some(Protocol::Udp(port)), Some(Protocol::QuicV1)) => { if !self.update_ports.quic6 { @@ -1087,7 +1122,7 @@ impl NetworkBehaviour for Discovery { return; } - self.update_enr_quic_port(port) + self.update_enr_quic_port(port, true) } _ => { debug!(self.log, "Encountered unacceptable multiaddr for listening (unsupported transport)"; "addr" => ?addr); @@ -1103,9 +1138,10 @@ impl NetworkBehaviour for Discovery { let local_enr: Enr = self.discv5.local_enr(); match attempt_enr_update { - Ok(_) => { + Ok(true) => { info!(self.log, "Updated local ENR"; "enr" => local_enr.to_base64(), "seq" => local_enr.seq(), "id"=> %local_enr.node_id(), "ip4" => ?local_enr.ip4(), "udp4"=> ?local_enr.udp4(), "tcp4" => ?local_enr.tcp4(), "tcp6" => ?local_enr.tcp6(), "udp6" => ?local_enr.udp6()) } + Ok(false) => {} // Nothing to do, ENR already configured Err(e) => warn!(self.log, "Failed to update ENR"; "error" => ?e), } } diff --git a/beacon_node/lighthouse_network/src/rpc/codec/base.rs b/beacon_node/lighthouse_network/src/rpc/codec/base.rs index 287f0a3f5fd..42a31d3480a 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec/base.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec/base.rs @@ -184,13 +184,13 @@ mod tests { fn fork_context(fork_name: ForkName) -> ForkContext { let mut chain_spec = Spec::default_spec(); let altair_fork_epoch = Epoch::new(1); - let merge_fork_epoch = Epoch::new(2); + let bellatrix_fork_epoch = Epoch::new(2); let capella_fork_epoch = Epoch::new(3); let deneb_fork_epoch = Epoch::new(4); let electra_fork_epoch = Epoch::new(5); chain_spec.altair_fork_epoch = Some(altair_fork_epoch); - chain_spec.bellatrix_fork_epoch = Some(merge_fork_epoch); + chain_spec.bellatrix_fork_epoch = Some(bellatrix_fork_epoch); chain_spec.capella_fork_epoch = Some(capella_fork_epoch); chain_spec.deneb_fork_epoch = Some(deneb_fork_epoch); chain_spec.electra_fork_epoch = Some(electra_fork_epoch); @@ -198,7 +198,7 @@ mod tests { let current_slot = match fork_name { ForkName::Base => Slot::new(0), ForkName::Altair => altair_fork_epoch.start_slot(Spec::slots_per_epoch()), - ForkName::Merge => merge_fork_epoch.start_slot(Spec::slots_per_epoch()), + ForkName::Bellatrix => bellatrix_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Capella => capella_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Deneb => deneb_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Electra => electra_fork_epoch.start_slot(Spec::slots_per_epoch()), diff --git a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs index 4cbb6582583..482d1d96b4a 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs @@ -18,8 +18,8 @@ use tokio_util::codec::{Decoder, Encoder}; use types::{ BlobSidecar, ChainSpec, EthSpec, ForkContext, ForkName, Hash256, LightClientBootstrap, LightClientFinalityUpdate, LightClientOptimisticUpdate, RuntimeVariableList, SignedBeaconBlock, - SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockCapella, - SignedBeaconBlockDeneb, SignedBeaconBlockElectra, SignedBeaconBlockMerge, + SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix, + SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra, }; use unsigned_varint::codec::Uvi; @@ -403,8 +403,8 @@ fn context_bytes( SignedBeaconBlock::Capella { .. } => { fork_context.to_context_bytes(ForkName::Capella) } - SignedBeaconBlock::Merge { .. } => { - fork_context.to_context_bytes(ForkName::Merge) + SignedBeaconBlock::Bellatrix { .. } => { + fork_context.to_context_bytes(ForkName::Bellatrix) } SignedBeaconBlock::Altair { .. } => { fork_context.to_context_bytes(ForkName::Altair) @@ -658,8 +658,10 @@ fn handle_rpc_response( Some(ForkName::Base) => Ok(Some(RPCResponse::BlocksByRange(Arc::new( SignedBeaconBlock::Base(SignedBeaconBlockBase::from_ssz_bytes(decoded_buffer)?), )))), - Some(ForkName::Merge) => Ok(Some(RPCResponse::BlocksByRange(Arc::new( - SignedBeaconBlock::Merge(SignedBeaconBlockMerge::from_ssz_bytes(decoded_buffer)?), + Some(ForkName::Bellatrix) => Ok(Some(RPCResponse::BlocksByRange(Arc::new( + SignedBeaconBlock::Bellatrix(SignedBeaconBlockBellatrix::from_ssz_bytes( + decoded_buffer, + )?), )))), Some(ForkName::Capella) => Ok(Some(RPCResponse::BlocksByRange(Arc::new( SignedBeaconBlock::Capella(SignedBeaconBlockCapella::from_ssz_bytes( @@ -689,8 +691,10 @@ fn handle_rpc_response( Some(ForkName::Base) => Ok(Some(RPCResponse::BlocksByRoot(Arc::new( SignedBeaconBlock::Base(SignedBeaconBlockBase::from_ssz_bytes(decoded_buffer)?), )))), - Some(ForkName::Merge) => Ok(Some(RPCResponse::BlocksByRoot(Arc::new( - SignedBeaconBlock::Merge(SignedBeaconBlockMerge::from_ssz_bytes(decoded_buffer)?), + Some(ForkName::Bellatrix) => Ok(Some(RPCResponse::BlocksByRoot(Arc::new( + SignedBeaconBlock::Bellatrix(SignedBeaconBlockBellatrix::from_ssz_bytes( + decoded_buffer, + )?), )))), Some(ForkName::Capella) => Ok(Some(RPCResponse::BlocksByRoot(Arc::new( SignedBeaconBlock::Capella(SignedBeaconBlockCapella::from_ssz_bytes( @@ -743,7 +747,7 @@ mod tests { use crate::types::{EnrAttestationBitfield, EnrSyncCommitteeBitfield}; use types::{ blob_sidecar::BlobIdentifier, BeaconBlock, BeaconBlockAltair, BeaconBlockBase, - BeaconBlockMerge, EmptyBlock, Epoch, FullPayload, Signature, Slot, + BeaconBlockBellatrix, EmptyBlock, Epoch, FullPayload, Signature, Slot, }; type Spec = types::MainnetEthSpec; @@ -751,13 +755,13 @@ mod tests { fn fork_context(fork_name: ForkName) -> ForkContext { let mut chain_spec = Spec::default_spec(); let altair_fork_epoch = Epoch::new(1); - let merge_fork_epoch = Epoch::new(2); + let bellatrix_fork_epoch = Epoch::new(2); let capella_fork_epoch = Epoch::new(3); let deneb_fork_epoch = Epoch::new(4); let electra_fork_epoch = Epoch::new(5); chain_spec.altair_fork_epoch = Some(altair_fork_epoch); - chain_spec.bellatrix_fork_epoch = Some(merge_fork_epoch); + chain_spec.bellatrix_fork_epoch = Some(bellatrix_fork_epoch); chain_spec.capella_fork_epoch = Some(capella_fork_epoch); chain_spec.deneb_fork_epoch = Some(deneb_fork_epoch); chain_spec.electra_fork_epoch = Some(electra_fork_epoch); @@ -765,7 +769,7 @@ mod tests { let current_slot = match fork_name { ForkName::Base => Slot::new(0), ForkName::Altair => altair_fork_epoch.start_slot(Spec::slots_per_epoch()), - ForkName::Merge => merge_fork_epoch.start_slot(Spec::slots_per_epoch()), + ForkName::Bellatrix => bellatrix_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Capella => capella_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Deneb => deneb_fork_epoch.start_slot(Spec::slots_per_epoch()), ForkName::Electra => electra_fork_epoch.start_slot(Spec::slots_per_epoch()), @@ -790,32 +794,38 @@ mod tests { Arc::new(BlobSidecar::empty()) } - /// Merge block with length < max_rpc_size. - fn merge_block_small(fork_context: &ForkContext, spec: &ChainSpec) -> SignedBeaconBlock { - let mut block: BeaconBlockMerge<_, FullPayload> = - BeaconBlockMerge::empty(&Spec::default_spec()); + /// Bellatrix block with length < max_rpc_size. + fn bellatrix_block_small( + fork_context: &ForkContext, + spec: &ChainSpec, + ) -> SignedBeaconBlock { + let mut block: BeaconBlockBellatrix<_, FullPayload> = + BeaconBlockBellatrix::empty(&Spec::default_spec()); let tx = VariableList::from(vec![0; 1024]); let txs = VariableList::from(std::iter::repeat(tx).take(5000).collect::>()); block.body.execution_payload.execution_payload.transactions = txs; - let block = BeaconBlock::Merge(block); + let block = BeaconBlock::Bellatrix(block); assert!(block.ssz_bytes_len() <= max_rpc_size(fork_context, spec.max_chunk_size as usize)); SignedBeaconBlock::from_block(block, Signature::empty()) } - /// Merge block with length > MAX_RPC_SIZE. - /// 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. - fn merge_block_large(fork_context: &ForkContext, spec: &ChainSpec) -> SignedBeaconBlock { - let mut block: BeaconBlockMerge<_, FullPayload> = - BeaconBlockMerge::empty(&Spec::default_spec()); + /// Bellatrix block with length > MAX_RPC_SIZE. + /// The max limit for a Bellatrix block is in the order of ~16GiB which wouldn't fit in memory. + /// Hence, we generate a Bellatrix block just greater than `MAX_RPC_SIZE` to test rejection on the rpc layer. + fn bellatrix_block_large( + fork_context: &ForkContext, + spec: &ChainSpec, + ) -> SignedBeaconBlock { + let mut block: BeaconBlockBellatrix<_, FullPayload> = + BeaconBlockBellatrix::empty(&Spec::default_spec()); let tx = VariableList::from(vec![0; 1024]); let txs = VariableList::from(std::iter::repeat(tx).take(100000).collect::>()); block.body.execution_payload.execution_payload.transactions = txs; - let block = BeaconBlock::Merge(block); + let block = BeaconBlock::Bellatrix(block); assert!(block.ssz_bytes_len() > max_rpc_size(fork_context, spec.max_chunk_size as usize)); SignedBeaconBlock::from_block(block, Signature::empty()) } @@ -1172,25 +1182,27 @@ mod tests { Ok(Some(RPCResponse::BlocksByRange(Arc::new(altair_block())))) ); - let merge_block_small = merge_block_small(&fork_context(ForkName::Merge), &chain_spec); - let merge_block_large = merge_block_large(&fork_context(ForkName::Merge), &chain_spec); + let bellatrix_block_small = + bellatrix_block_small(&fork_context(ForkName::Bellatrix), &chain_spec); + let bellatrix_block_large = + bellatrix_block_large(&fork_context(ForkName::Bellatrix), &chain_spec); assert_eq!( encode_then_decode_response( SupportedProtocol::BlocksByRangeV2, RPCCodedResponse::Success(RPCResponse::BlocksByRange(Arc::new( - merge_block_small.clone() + bellatrix_block_small.clone() ))), - ForkName::Merge, + ForkName::Bellatrix, &chain_spec, ), Ok(Some(RPCResponse::BlocksByRange(Arc::new( - merge_block_small.clone() + bellatrix_block_small.clone() )))) ); let mut encoded = - encode_without_length_checks(merge_block_large.as_ssz_bytes(), ForkName::Merge) + encode_without_length_checks(bellatrix_block_large.as_ssz_bytes(), ForkName::Bellatrix) .unwrap(); assert!( @@ -1198,7 +1210,7 @@ mod tests { decode_response( SupportedProtocol::BlocksByRangeV2, &mut encoded, - ForkName::Merge, + ForkName::Bellatrix, &chain_spec, ) .unwrap_err(), @@ -1248,16 +1260,18 @@ mod tests { encode_then_decode_response( SupportedProtocol::BlocksByRootV2, RPCCodedResponse::Success(RPCResponse::BlocksByRoot(Arc::new( - merge_block_small.clone() + bellatrix_block_small.clone() ))), - ForkName::Merge, + ForkName::Bellatrix, &chain_spec, ), - Ok(Some(RPCResponse::BlocksByRoot(Arc::new(merge_block_small)))) + Ok(Some(RPCResponse::BlocksByRoot(Arc::new( + bellatrix_block_small + )))) ); let mut encoded = - encode_without_length_checks(merge_block_large.as_ssz_bytes(), ForkName::Merge) + encode_without_length_checks(bellatrix_block_large.as_ssz_bytes(), ForkName::Bellatrix) .unwrap(); assert!( @@ -1265,7 +1279,7 @@ mod tests { decode_response( SupportedProtocol::BlocksByRootV2, &mut encoded, - ForkName::Merge, + ForkName::Bellatrix, &chain_spec, ) .unwrap_err(), diff --git a/beacon_node/lighthouse_network/src/rpc/protocol.rs b/beacon_node/lighthouse_network/src/rpc/protocol.rs index f65586087c2..12a7f09338e 100644 --- a/beacon_node/lighthouse_network/src/rpc/protocol.rs +++ b/beacon_node/lighthouse_network/src/rpc/protocol.rs @@ -17,8 +17,8 @@ use tokio_util::{ compat::{Compat, FuturesAsyncReadCompatExt}, }; use types::{ - BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockCapella, BeaconBlockElectra, - BeaconBlockMerge, BlobSidecar, ChainSpec, EmptyBlock, EthSpec, ForkContext, ForkName, + BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockBellatrix, BeaconBlockCapella, + BeaconBlockElectra, BlobSidecar, ChainSpec, EmptyBlock, EthSpec, ForkContext, ForkName, LightClientBootstrap, LightClientBootstrapAltair, LightClientFinalityUpdate, LightClientFinalityUpdateAltair, LightClientOptimisticUpdate, LightClientOptimisticUpdateAltair, MainnetEthSpec, Signature, SignedBeaconBlock, @@ -53,8 +53,8 @@ lazy_static! { .as_ssz_bytes() .len(); - pub static ref SIGNED_BEACON_BLOCK_MERGE_MIN: usize = SignedBeaconBlock::::from_block( - BeaconBlock::Merge(BeaconBlockMerge::::empty(&MainnetEthSpec::default_spec())), + pub static ref SIGNED_BEACON_BLOCK_BELLATRIX_MIN: usize = SignedBeaconBlock::::from_block( + BeaconBlock::Bellatrix(BeaconBlockBellatrix::::empty(&MainnetEthSpec::default_spec())), Signature::empty(), ) .as_ssz_bytes() @@ -74,14 +74,14 @@ lazy_static! { .as_ssz_bytes() .len(); - /// The `BeaconBlockMerge` block has an `ExecutionPayload` field which has a max size ~16 GiB for future proofing. + /// The `BeaconBlockBellatrix` block has an `ExecutionPayload` field which has a max size ~16 GiB for future proofing. /// We calculate the value from its fields instead of constructing the block and checking the length. /// Note: This is only the theoretical upper bound. We further bound the max size we receive over the network /// with `max_chunk_size`. - pub static ref SIGNED_BEACON_BLOCK_MERGE_MAX: usize = + pub static ref SIGNED_BEACON_BLOCK_BELLATRIX_MAX: usize = // Size of a full altair block *SIGNED_BEACON_BLOCK_ALTAIR_MAX - + types::ExecutionPayload::::max_execution_payload_merge_size() // adding max size of execution payload (~16gb) + + types::ExecutionPayload::::max_execution_payload_bellatrix_size() // adding max size of execution payload (~16gb) + ssz::BYTES_PER_LENGTH_OFFSET; // Adding the additional ssz offset for the `ExecutionPayload` field pub static ref SIGNED_BEACON_BLOCK_CAPELLA_MAX: usize = *SIGNED_BEACON_BLOCK_CAPELLA_MAX_WITHOUT_PAYLOAD @@ -134,7 +134,7 @@ const REQUEST_TIMEOUT: u64 = 15; pub fn max_rpc_size(fork_context: &ForkContext, max_chunk_size: usize) -> usize { match fork_context.current_fork() { ForkName::Altair | ForkName::Base => max_chunk_size / 10, - ForkName::Merge => max_chunk_size, + ForkName::Bellatrix => max_chunk_size, ForkName::Capella => max_chunk_size, ForkName::Deneb => max_chunk_size, ForkName::Electra => max_chunk_size, @@ -154,20 +154,20 @@ pub fn rpc_block_limits_by_fork(current_fork: ForkName) -> RpcLimits { *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair blocks *SIGNED_BEACON_BLOCK_ALTAIR_MAX, // Altair block is larger than base blocks ), - ForkName::Merge => RpcLimits::new( - *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and merge blocks - *SIGNED_BEACON_BLOCK_MERGE_MAX, // Merge block is larger than base and altair blocks + ForkName::Bellatrix => RpcLimits::new( + *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and bellatrix blocks + *SIGNED_BEACON_BLOCK_BELLATRIX_MAX, // Bellatrix block is larger than base and altair blocks ), ForkName::Capella => RpcLimits::new( - *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and merge blocks + *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and bellatrix blocks *SIGNED_BEACON_BLOCK_CAPELLA_MAX, // Capella block is larger than base, altair and merge blocks ), ForkName::Deneb => RpcLimits::new( - *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and merge blocks + *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and bellatrix blocks *SIGNED_BEACON_BLOCK_DENEB_MAX, // Deneb block is larger than all prior fork blocks ), ForkName::Electra => RpcLimits::new( - *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and merge blocks + *SIGNED_BEACON_BLOCK_BASE_MIN, // Base block is smaller than altair and bellatrix blocks *SIGNED_BEACON_BLOCK_ELECTRA_MAX, // Electra block is larger than Deneb block ), } @@ -178,7 +178,9 @@ fn rpc_light_client_finality_update_limits_by_fork(current_fork: ForkName) -> Rp match ¤t_fork { ForkName::Base => RpcLimits::new(0, 0), - ForkName::Altair | ForkName::Merge => RpcLimits::new(altair_fixed_len, altair_fixed_len), + ForkName::Altair | ForkName::Bellatrix => { + RpcLimits::new(altair_fixed_len, altair_fixed_len) + } ForkName::Capella => { RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_FINALITY_UPDATE_CAPELLA_MAX) } @@ -196,7 +198,9 @@ fn rpc_light_client_optimistic_update_limits_by_fork(current_fork: ForkName) -> match ¤t_fork { ForkName::Base => RpcLimits::new(0, 0), - ForkName::Altair | ForkName::Merge => RpcLimits::new(altair_fixed_len, altair_fixed_len), + ForkName::Altair | ForkName::Bellatrix => { + RpcLimits::new(altair_fixed_len, altair_fixed_len) + } ForkName::Capella => RpcLimits::new( altair_fixed_len, *LIGHT_CLIENT_OPTIMISTIC_UPDATE_CAPELLA_MAX, @@ -216,7 +220,9 @@ fn rpc_light_client_bootstrap_limits_by_fork(current_fork: ForkName) -> RpcLimit match ¤t_fork { ForkName::Base => RpcLimits::new(0, 0), - ForkName::Altair | ForkName::Merge => RpcLimits::new(altair_fixed_len, altair_fixed_len), + ForkName::Altair | ForkName::Bellatrix => { + RpcLimits::new(altair_fixed_len, altair_fixed_len) + } ForkName::Capella => RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_BOOTSTRAP_CAPELLA_MAX), ForkName::Deneb => RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_BOOTSTRAP_DENEB_MAX), ForkName::Electra => RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_BOOTSTRAP_ELECTRA_MAX), diff --git a/beacon_node/lighthouse_network/src/service/mod.rs b/beacon_node/lighthouse_network/src/service/mod.rs index f91a5b471ad..1397c80fbd3 100644 --- a/beacon_node/lighthouse_network/src/service/mod.rs +++ b/beacon_node/lighthouse_network/src/service/mod.rs @@ -1681,12 +1681,16 @@ impl Network { libp2p::upnp::Event::NewExternalAddr(addr) => { info!(self.log, "UPnP route established"; "addr" => %addr); let mut iter = addr.iter(); - // Skip Ip address. - iter.next(); + let is_ip6 = { + let addr = iter.next(); + matches!(addr, Some(MProtocol::Ip6(_))) + }; match iter.next() { Some(multiaddr::Protocol::Udp(udp_port)) => match iter.next() { Some(multiaddr::Protocol::QuicV1) => { - if let Err(e) = self.discovery_mut().update_enr_quic_port(udp_port) { + if let Err(e) = + self.discovery_mut().update_enr_quic_port(udp_port, is_ip6) + { warn!(self.log, "Failed to update ENR"; "error" => e); } } @@ -1695,7 +1699,7 @@ impl Network { } }, Some(multiaddr::Protocol::Tcp(tcp_port)) => { - if let Err(e) = self.discovery_mut().update_enr_tcp_port(tcp_port) { + if let Err(e) = self.discovery_mut().update_enr_tcp_port(tcp_port, is_ip6) { warn!(self.log, "Failed to update ENR"; "error" => e); } } diff --git a/beacon_node/lighthouse_network/src/types/pubsub.rs b/beacon_node/lighthouse_network/src/types/pubsub.rs index 68864c76638..4c7e115aed7 100644 --- a/beacon_node/lighthouse_network/src/types/pubsub.rs +++ b/beacon_node/lighthouse_network/src/types/pubsub.rs @@ -10,8 +10,8 @@ use types::{ Attestation, AttesterSlashing, AttesterSlashingBase, AttesterSlashingElectra, BlobSidecar, EthSpec, ForkContext, ForkName, LightClientFinalityUpdate, LightClientOptimisticUpdate, ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockAltair, - SignedBeaconBlockBase, SignedBeaconBlockCapella, SignedBeaconBlockDeneb, - SignedBeaconBlockElectra, SignedBeaconBlockMerge, SignedBlsToExecutionChange, + SignedBeaconBlockBase, SignedBeaconBlockBellatrix, SignedBeaconBlockCapella, + SignedBeaconBlockDeneb, SignedBeaconBlockElectra, SignedBlsToExecutionChange, SignedContributionAndProof, SignedVoluntaryExit, SubnetId, SyncCommitteeMessage, SyncSubnetId, }; @@ -179,8 +179,8 @@ impl PubsubMessage { SignedBeaconBlockAltair::from_ssz_bytes(data) .map_err(|e| format!("{:?}", e))?, ), - Some(ForkName::Merge) => SignedBeaconBlock::::Merge( - SignedBeaconBlockMerge::from_ssz_bytes(data) + Some(ForkName::Bellatrix) => SignedBeaconBlock::::Bellatrix( + SignedBeaconBlockBellatrix::from_ssz_bytes(data) .map_err(|e| format!("{:?}", e))?, ), Some(ForkName::Capella) => SignedBeaconBlock::::Capella( @@ -219,7 +219,7 @@ impl PubsubMessage { Some( ForkName::Base | ForkName::Altair - | ForkName::Merge + | ForkName::Bellatrix | ForkName::Capella, ) | None => Err(format!( @@ -244,7 +244,7 @@ impl PubsubMessage { match fork_context.from_context_bytes(gossip_topic.fork_digest) { Some(ForkName::Base) | Some(ForkName::Altair) - | Some(ForkName::Merge) + | Some(ForkName::Bellatrix) | Some(ForkName::Capella) | Some(ForkName::Deneb) => AttesterSlashing::Base( AttesterSlashingBase::from_ssz_bytes(data) diff --git a/beacon_node/lighthouse_network/src/types/topics.rs b/beacon_node/lighthouse_network/src/types/topics.rs index 7cbb5bac57f..c5f4b0c9ebb 100644 --- a/beacon_node/lighthouse_network/src/types/topics.rs +++ b/beacon_node/lighthouse_network/src/types/topics.rs @@ -47,7 +47,7 @@ pub fn fork_core_topics(fork_name: &ForkName, spec: &ChainSpec) -> V match fork_name { ForkName::Base => BASE_CORE_TOPICS.to_vec(), ForkName::Altair => ALTAIR_CORE_TOPICS.to_vec(), - ForkName::Merge => vec![], + ForkName::Bellatrix => vec![], ForkName::Capella => CAPELLA_CORE_TOPICS.to_vec(), ForkName::Deneb => { // All of deneb blob topics are core topics diff --git a/beacon_node/lighthouse_network/tests/common.rs b/beacon_node/lighthouse_network/tests/common.rs index 40ae341bd26..32e3a034666 100644 --- a/beacon_node/lighthouse_network/tests/common.rs +++ b/beacon_node/lighthouse_network/tests/common.rs @@ -21,13 +21,13 @@ use tempfile::Builder as TempBuilder; pub fn fork_context(fork_name: ForkName) -> ForkContext { let mut chain_spec = E::default_spec(); let altair_fork_epoch = Epoch::new(1); - let merge_fork_epoch = Epoch::new(2); + let bellatrix_fork_epoch = Epoch::new(2); let capella_fork_epoch = Epoch::new(3); let deneb_fork_epoch = Epoch::new(4); let electra_fork_epoch = Epoch::new(5); chain_spec.altair_fork_epoch = Some(altair_fork_epoch); - chain_spec.bellatrix_fork_epoch = Some(merge_fork_epoch); + chain_spec.bellatrix_fork_epoch = Some(bellatrix_fork_epoch); chain_spec.capella_fork_epoch = Some(capella_fork_epoch); chain_spec.deneb_fork_epoch = Some(deneb_fork_epoch); chain_spec.electra_fork_epoch = Some(electra_fork_epoch); @@ -35,7 +35,7 @@ pub fn fork_context(fork_name: ForkName) -> ForkContext { let current_slot = match fork_name { ForkName::Base => Slot::new(0), ForkName::Altair => altair_fork_epoch.start_slot(E::slots_per_epoch()), - ForkName::Merge => merge_fork_epoch.start_slot(E::slots_per_epoch()), + ForkName::Bellatrix => bellatrix_fork_epoch.start_slot(E::slots_per_epoch()), ForkName::Capella => capella_fork_epoch.start_slot(E::slots_per_epoch()), ForkName::Deneb => deneb_fork_epoch.start_slot(E::slots_per_epoch()), ForkName::Electra => electra_fork_epoch.start_slot(E::slots_per_epoch()), diff --git a/beacon_node/lighthouse_network/tests/rpc_tests.rs b/beacon_node/lighthouse_network/tests/rpc_tests.rs index 5a21b462d43..a60af4db3db 100644 --- a/beacon_node/lighthouse_network/tests/rpc_tests.rs +++ b/beacon_node/lighthouse_network/tests/rpc_tests.rs @@ -13,37 +13,37 @@ use std::time::Duration; use tokio::runtime::Runtime; use tokio::time::sleep; use types::{ - BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockMerge, BlobSidecar, ChainSpec, + BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockBellatrix, BlobSidecar, ChainSpec, EmptyBlock, Epoch, EthSpec, ForkContext, ForkName, Hash256, MinimalEthSpec, Signature, SignedBeaconBlock, Slot, }; type E = MinimalEthSpec; -/// Merge block with length < max_rpc_size. -fn merge_block_small(fork_context: &ForkContext, spec: &ChainSpec) -> BeaconBlock { - let mut block = BeaconBlockMerge::::empty(spec); +/// Bellatrix block with length < max_rpc_size. +fn bellatrix_block_small(fork_context: &ForkContext, spec: &ChainSpec) -> BeaconBlock { + let mut block = BeaconBlockBellatrix::::empty(spec); let tx = VariableList::from(vec![0; 1024]); let txs = VariableList::from(std::iter::repeat(tx).take(5000).collect::>()); block.body.execution_payload.execution_payload.transactions = txs; - let block = BeaconBlock::Merge(block); + let block = BeaconBlock::Bellatrix(block); assert!(block.ssz_bytes_len() <= max_rpc_size(fork_context, spec.max_chunk_size as usize)); block } -/// Merge block with length > MAX_RPC_SIZE. -/// 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. -fn merge_block_large(fork_context: &ForkContext, spec: &ChainSpec) -> BeaconBlock { - let mut block = BeaconBlockMerge::::empty(spec); +/// Bellatrix block with length > MAX_RPC_SIZE. +/// The max limit for a bellatrix block is in the order of ~16GiB which wouldn't fit in memory. +/// Hence, we generate a bellatrix block just greater than `MAX_RPC_SIZE` to test rejection on the rpc layer. +fn bellatrix_block_large(fork_context: &ForkContext, spec: &ChainSpec) -> BeaconBlock { + let mut block = BeaconBlockBellatrix::::empty(spec); let tx = VariableList::from(vec![0; 1024]); let txs = VariableList::from(std::iter::repeat(tx).take(100000).collect::>()); block.body.execution_payload.execution_payload.transactions = txs; - let block = BeaconBlock::Merge(block); + let block = BeaconBlock::Bellatrix(block); assert!(block.ssz_bytes_len() > max_rpc_size(fork_context, spec.max_chunk_size as usize)); block } @@ -167,7 +167,7 @@ fn test_tcp_blocks_by_range_chunked_rpc() { let (mut sender, mut receiver) = common::build_node_pair( Arc::downgrade(&rt), &log, - ForkName::Merge, + ForkName::Bellatrix, &spec, Protocol::Tcp, ) @@ -187,9 +187,10 @@ fn test_tcp_blocks_by_range_chunked_rpc() { let signed_full_block = SignedBeaconBlock::from_block(full_block, Signature::empty()); let rpc_response_altair = Response::BlocksByRange(Some(Arc::new(signed_full_block))); - let full_block = merge_block_small(&common::fork_context(ForkName::Merge), &spec); + let full_block = bellatrix_block_small(&common::fork_context(ForkName::Bellatrix), &spec); let signed_full_block = SignedBeaconBlock::from_block(full_block, Signature::empty()); - let rpc_response_merge_small = Response::BlocksByRange(Some(Arc::new(signed_full_block))); + let rpc_response_bellatrix_small = + Response::BlocksByRange(Some(Arc::new(signed_full_block))); // keep count of the number of messages received let mut messages_received = 0; @@ -216,7 +217,7 @@ fn test_tcp_blocks_by_range_chunked_rpc() { } else if messages_received < 4 { assert_eq!(response, rpc_response_altair.clone()); } else { - assert_eq!(response, rpc_response_merge_small.clone()); + assert_eq!(response, rpc_response_bellatrix_small.clone()); } messages_received += 1; warn!(log, "Chunk received"); @@ -249,13 +250,13 @@ fn test_tcp_blocks_by_range_chunked_rpc() { warn!(log, "Receiver got request"); for i in 0..messages_to_send { // Send first third of responses as base blocks, - // second as altair and third as merge. + // second as altair and third as bellatrix. let rpc_response = if i < 2 { rpc_response_base.clone() } else if i < 4 { rpc_response_altair.clone() } else { - rpc_response_merge_small.clone() + rpc_response_bellatrix_small.clone() }; receiver.send_response(peer_id, id, rpc_response.clone()); } @@ -368,7 +369,7 @@ fn test_blobs_by_range_chunked_rpc() { warn!(log, "Receiver got request"); for _ in 0..messages_to_send { // Send first third of responses as base blocks, - // second as altair and third as merge. + // second as altair and third as bellatrix. receiver.send_response(peer_id, id, rpc_response.clone()); } // send the stream termination @@ -411,7 +412,7 @@ fn test_tcp_blocks_by_range_over_limit() { let (mut sender, mut receiver) = common::build_node_pair( Arc::downgrade(&rt), &log, - ForkName::Merge, + ForkName::Bellatrix, &spec, Protocol::Tcp, ) @@ -421,9 +422,10 @@ fn test_tcp_blocks_by_range_over_limit() { let rpc_request = Request::BlocksByRange(BlocksByRangeRequest::new(0, messages_to_send)); // BlocksByRange Response - let full_block = merge_block_large(&common::fork_context(ForkName::Merge), &spec); + let full_block = bellatrix_block_large(&common::fork_context(ForkName::Bellatrix), &spec); let signed_full_block = SignedBeaconBlock::from_block(full_block, Signature::empty()); - let rpc_response_merge_large = Response::BlocksByRange(Some(Arc::new(signed_full_block))); + let rpc_response_bellatrix_large = + Response::BlocksByRange(Some(Arc::new(signed_full_block))); let request_id = messages_to_send as usize; // build the sender future @@ -458,7 +460,7 @@ fn test_tcp_blocks_by_range_over_limit() { // send the response warn!(log, "Receiver got request"); for _ in 0..messages_to_send { - let rpc_response = rpc_response_merge_large.clone(); + let rpc_response = rpc_response_bellatrix_large.clone(); receiver.send_response(peer_id, id, rpc_response.clone()); } // send the stream termination @@ -736,7 +738,7 @@ fn test_tcp_blocks_by_root_chunked_rpc() { let (mut sender, mut receiver) = common::build_node_pair( Arc::downgrade(&rt), &log, - ForkName::Merge, + ForkName::Bellatrix, &spec, Protocol::Tcp, ) @@ -764,9 +766,10 @@ fn test_tcp_blocks_by_root_chunked_rpc() { let signed_full_block = SignedBeaconBlock::from_block(full_block, Signature::empty()); let rpc_response_altair = Response::BlocksByRoot(Some(Arc::new(signed_full_block))); - let full_block = merge_block_small(&common::fork_context(ForkName::Merge), &spec); + let full_block = bellatrix_block_small(&common::fork_context(ForkName::Bellatrix), &spec); let signed_full_block = SignedBeaconBlock::from_block(full_block, Signature::empty()); - let rpc_response_merge_small = Response::BlocksByRoot(Some(Arc::new(signed_full_block))); + let rpc_response_bellatrix_small = + Response::BlocksByRoot(Some(Arc::new(signed_full_block))); // keep count of the number of messages received let mut messages_received = 0; @@ -790,7 +793,7 @@ fn test_tcp_blocks_by_root_chunked_rpc() { } else if messages_received < 4 { assert_eq!(response, rpc_response_altair.clone()); } else { - assert_eq!(response, rpc_response_merge_small.clone()); + assert_eq!(response, rpc_response_bellatrix_small.clone()); } messages_received += 1; debug!(log, "Chunk received"); @@ -822,13 +825,13 @@ fn test_tcp_blocks_by_root_chunked_rpc() { debug!(log, "Receiver got request"); for i in 0..messages_to_send { - // Send equal base, altair and merge blocks + // Send equal base, altair and bellatrix blocks let rpc_response = if i < 2 { rpc_response_base.clone() } else if i < 4 { rpc_response_altair.clone() } else { - rpc_response_merge_small.clone() + rpc_response_bellatrix_small.clone() }; receiver.send_response(peer_id, id, rpc_response); debug!(log, "Sending message"); diff --git a/beacon_node/network/src/metrics.rs b/beacon_node/network/src/metrics.rs index d3804fbed8d..d096b33f6cb 100644 --- a/beacon_node/network/src/metrics.rs +++ b/beacon_node/network/src/metrics.rs @@ -244,6 +244,19 @@ lazy_static! { "sync_parent_block_lookups", "Number of parent block lookups underway" ); + pub static ref SYNC_LOOKUP_CREATED: Result = try_create_int_counter( + "sync_lookups_created_total", + "Total count of sync lookups created", + ); + pub static ref SYNC_LOOKUP_DROPPED: Result = try_create_int_counter_vec( + "sync_lookups_dropped_total", + "Total count of sync lookups dropped by reason", + &["reason"] + ); + pub static ref SYNC_LOOKUP_COMPLETED: Result = try_create_int_counter( + "sync_lookups_completed_total", + "Total count of sync lookups completed", + ); /* * Block Delay Metrics diff --git a/beacon_node/network/src/network_beacon_processor/gossip_methods.rs b/beacon_node/network/src/network_beacon_processor/gossip_methods.rs index dab5310f2b5..6dc671243d5 100644 --- a/beacon_node/network/src/network_beacon_processor/gossip_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/gossip_methods.rs @@ -778,24 +778,28 @@ impl NetworkBeaconProcessor { "block_root" => %block_root, ); } + Err(BlockError::BlockIsAlreadyKnown(_)) => { + debug!( + self.log, + "Ignoring gossip blob already imported"; + "block_root" => ?block_root, + "blob_index" => blob_index, + ); + } Err(err) => { debug!( self.log, "Invalid gossip blob"; "outcome" => ?err, - "block root" => ?block_root, - "block slot" => blob_slot, - "blob index" => blob_index, + "block_root" => ?block_root, + "block_slot" => blob_slot, + "blob_index" => blob_index, ); self.gossip_penalize_peer( peer_id, PeerAction::MidToleranceError, "bad_gossip_blob_ssz", ); - trace!( - self.log, - "Invalid gossip blob ssz"; - ); } } } diff --git a/beacon_node/network/src/network_beacon_processor/rpc_methods.rs b/beacon_node/network/src/network_beacon_processor/rpc_methods.rs index 1e72dc42578..2a0c7ea089b 100644 --- a/beacon_node/network/src/network_beacon_processor/rpc_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/rpc_methods.rs @@ -433,9 +433,10 @@ impl NetworkBeaconProcessor { ForkName::Deneb | ForkName::Electra => { self.chain.spec.max_request_blocks_deneb } - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { - self.chain.spec.max_request_blocks - } + ForkName::Base + | ForkName::Altair + | ForkName::Bellatrix + | ForkName::Capella => self.chain.spec.max_request_blocks, } }); if *req.count() > max_request_size { diff --git a/beacon_node/network/src/network_beacon_processor/sync_methods.rs b/beacon_node/network/src/network_beacon_processor/sync_methods.rs index 887974c6e0b..daa9a2cf197 100644 --- a/beacon_node/network/src/network_beacon_processor/sync_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/sync_methods.rs @@ -33,8 +33,6 @@ pub enum ChainSegmentProcessId { RangeBatchId(ChainId, Epoch), /// Processing ID for a backfill syncing batch. BackSyncBatchId(Epoch), - /// Processing Id of the parent lookup of a block. - ParentLookup(Hash256), } /// Returned when a chain segment import fails. @@ -396,41 +394,6 @@ impl NetworkBeaconProcessor { } } } - // this is a parent lookup request from the sync manager - ChainSegmentProcessId::ParentLookup(chain_head) => { - debug!( - self.log, "Processing parent lookup"; - "chain_hash" => %chain_head, - "blocks" => downloaded_blocks.len() - ); - // parent blocks are ordered from highest slot to lowest, so we need to process in - // reverse - match self - .process_blocks(downloaded_blocks.iter().rev(), notify_execution_layer) - .await - { - (imported_blocks, Err(e)) => { - debug!(self.log, "Parent lookup failed"; "error" => %e.message); - match e.peer_action { - Some(penalty) => BatchProcessResult::FaultyFailure { - imported_blocks: imported_blocks > 0, - penalty, - }, - None => BatchProcessResult::NonFaultyFailure, - } - } - (imported_blocks, Ok(_)) => { - debug!( - self.log, "Parent lookup processed successfully"; - "chain_hash" => %chain_head, - "imported_blocks" => imported_blocks - ); - BatchProcessResult::Success { - was_non_empty: imported_blocks > 0, - } - } - } - } }; self.send_sync_message(SyncMessage::BatchProcessed { sync_type, result }); diff --git a/beacon_node/network/src/network_beacon_processor/tests.rs b/beacon_node/network/src/network_beacon_processor/tests.rs index dd58eb83555..4ba4c4ddd1d 100644 --- a/beacon_node/network/src/network_beacon_processor/tests.rs +++ b/beacon_node/network/src/network_beacon_processor/tests.rs @@ -311,9 +311,7 @@ impl TestRig { block_root, RpcBlock::new_without_blobs(Some(block_root), self.next_block.clone()), std::time::Duration::default(), - BlockProcessType::ParentLookup { - chain_hash: Hash256::random(), - }, + BlockProcessType::SingleBlock { id: 0 }, ) .unwrap(); } diff --git a/beacon_node/network/src/sync/backfill_sync/mod.rs b/beacon_node/network/src/sync/backfill_sync/mod.rs index 67fe871accf..9075bb15f08 100644 --- a/beacon_node/network/src/sync/backfill_sync/mod.rs +++ b/beacon_node/network/src/sync/backfill_sync/mod.rs @@ -87,16 +87,17 @@ pub enum ProcessResult { } /// The ways a backfill sync can fail. +// The info in the enum variants is displayed in logging, clippy thinks it's dead code. #[derive(Debug)] pub enum BackFillError { /// A batch failed to be downloaded. - BatchDownloadFailed(BatchId), + BatchDownloadFailed(#[allow(dead_code)] BatchId), /// A batch could not be processed. - BatchProcessingFailed(BatchId), + BatchProcessingFailed(#[allow(dead_code)] BatchId), /// A batch entered an invalid state. - BatchInvalidState(BatchId, String), + BatchInvalidState(#[allow(dead_code)] BatchId, #[allow(dead_code)] String), /// The sync algorithm entered an invalid state. - InvalidSyncState(String), + InvalidSyncState(#[allow(dead_code)] String), /// The chain became paused. Paused, } diff --git a/beacon_node/network/src/sync/block_lookups/common.rs b/beacon_node/network/src/sync/block_lookups/common.rs index 7193dd6e216..06e00ea6d1e 100644 --- a/beacon_node/network/src/sync/block_lookups/common.rs +++ b/beacon_node/network/src/sync/block_lookups/common.rs @@ -1,21 +1,19 @@ -use crate::sync::block_lookups::parent_lookup::PARENT_FAIL_TOLERANCE; use crate::sync::block_lookups::single_block_lookup::{ LookupRequestError, SingleBlockLookup, SingleLookupRequestState, }; use crate::sync::block_lookups::{ - BlobRequestState, BlockLookups, BlockRequestState, PeerId, SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS, -}; -use crate::sync::manager::{BlockProcessType, Id, SingleLookupReqId}; -use crate::sync::network_context::{ - BlobsByRootSingleBlockRequest, BlocksByRootSingleRequest, SyncNetworkContext, + BlobRequestState, BlockRequestState, PeerId, SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS, }; +use crate::sync::manager::{BlockProcessType, Id, SLOT_IMPORT_TOLERANCE}; +use crate::sync::network_context::SyncNetworkContext; use beacon_chain::block_verification_types::RpcBlock; -use beacon_chain::data_availability_checker::ChildComponents; use beacon_chain::BeaconChainTypes; use std::sync::Arc; -use std::time::Duration; use types::blob_sidecar::FixedBlobSidecarList; -use types::{Hash256, SignedBeaconBlock}; +use types::SignedBeaconBlock; + +use super::single_block_lookup::DownloadResult; +use super::SingleLookupId; #[derive(Debug, Copy, Clone)] pub enum ResponseType { @@ -23,20 +21,15 @@ pub enum ResponseType { Blob, } -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -pub enum LookupType { - Current, - Parent, -} +/// The maximum depth we will search for a parent block. In principle we should have sync'd any +/// canonical chain to its head once the peer connects. A chain should not appear where it's depth +/// is further back than the most recent head slot. +pub(crate) const PARENT_DEPTH_TOLERANCE: usize = SLOT_IMPORT_TOLERANCE * 2; -impl LookupType { - fn max_attempts(&self) -> u8 { - match self { - LookupType::Current => SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS, - LookupType::Parent => PARENT_FAIL_TOLERANCE, - } - } -} +/// Wrapper around bool to prevent mixing this argument with `BlockIsProcessed` +pub(crate) struct AwaitingParent(pub bool); +/// Wrapper around bool to prevent mixing this argument with `AwaitingParent` +pub(crate) struct BlockIsProcessed(pub bool); /// This trait unifies common single block lookup functionality across blocks and blobs. This /// includes making requests, verifying responses, and handling processing results. A @@ -47,121 +40,75 @@ impl LookupType { /// safety when handling a block/blob response ensuring we only mutate the correct corresponding /// state. pub trait RequestState { - /// The type of the request . - type RequestType; - /// The type created after validation. type VerifiedResponseType: Clone; - /// We convert a `VerifiedResponseType` to this type prior to sending it to the beacon processor. - type ReconstructedResponseType; - - /* Request building methods */ - - /// Construct a new request. - fn build_request( - &mut self, - lookup_type: LookupType, - ) -> Result<(PeerId, Self::RequestType), LookupRequestError> { - // Verify and construct request. - self.too_many_attempts(lookup_type)?; - let peer = self.get_peer()?; - let request = self.new_request(); - Ok((peer, request)) - } - - /// Construct a new request and send it. - fn build_request_and_send( + /// Potentially makes progress on this request if it's in a progress-able state + fn continue_request( &mut self, id: Id, - lookup_type: LookupType, + awaiting_parent: AwaitingParent, + downloaded_block_expected_blobs: Option, + block_is_processed: BlockIsProcessed, cx: &mut SyncNetworkContext, ) -> Result<(), LookupRequestError> { - // Check if request is necessary. - if !self.get_state().is_awaiting_download() { - return Ok(()); - } - - // Construct request. - let (peer_id, request) = self.build_request(lookup_type)?; - - // Update request state. - let req_counter = self.get_state_mut().on_download_start(peer_id); - - // Make request - let id = SingleLookupReqId { - id, - req_counter, - lookup_type, - }; - Self::make_request(id, peer_id, request, cx) - } - - /// Verify the current request has not exceeded the maximum number of attempts. - fn too_many_attempts(&self, lookup_type: LookupType) -> Result<(), LookupRequestError> { - let request_state = self.get_state(); - - if request_state.failed_attempts() >= lookup_type.max_attempts() { - let cannot_process = request_state.more_failed_processing_attempts(); - Err(LookupRequestError::TooManyAttempts { cannot_process }) - } else { - Ok(()) + // Attempt to progress awaiting downloads + if self.get_state().is_awaiting_download() { + // Verify the current request has not exceeded the maximum number of attempts. + let request_state = self.get_state(); + if request_state.failed_attempts() >= SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS { + let cannot_process = request_state.more_failed_processing_attempts(); + return Err(LookupRequestError::TooManyAttempts { cannot_process }); + } + + let peer_id = self + .get_state_mut() + .use_rand_available_peer() + .ok_or(LookupRequestError::NoPeers)?; + + // make_request returns true only if a request needs to be made + if self.make_request(id, peer_id, downloaded_block_expected_blobs, cx)? { + self.get_state_mut().on_download_start()?; + } else { + self.get_state_mut().on_completed_request()?; + } + + // Otherwise, attempt to progress awaiting processing + // If this request is awaiting a parent lookup to be processed, do not send for processing. + // The request will be rejected with unknown parent error. + } else if !awaiting_parent.0 + && (block_is_processed.0 || matches!(Self::response_type(), ResponseType::Block)) + { + // maybe_start_processing returns Some if state == AwaitingProcess. This pattern is + // useful to conditionally access the result data. + if let Some(result) = self.get_state_mut().maybe_start_processing() { + return Self::send_for_processing(id, result, cx); + } } - } - /// Get the next peer to request. Draws from the set of peers we think should have both the - /// block and blob first. If that fails, we draw from the set of peers that may have either. - fn get_peer(&mut self) -> Result { - self.get_state_mut() - .use_rand_available_peer() - .ok_or(LookupRequestError::NoPeers) + Ok(()) } - /// Initialize `Self::RequestType`. - fn new_request(&self) -> Self::RequestType; - - /// Send the request to the network service. + /// Request the network context to prepare a request of a component of `block_root`. If the + /// request is not necessary because the component is already known / processed, return false. + /// Return true if it sent a request and we can expect an event back from the network. fn make_request( - id: SingleLookupReqId, + &self, + id: Id, peer_id: PeerId, - request: Self::RequestType, + downloaded_block_expected_blobs: Option, cx: &mut SyncNetworkContext, - ) -> Result<(), LookupRequestError>; + ) -> Result; /* Response handling methods */ - /// A getter for the parent root of the response. Returns an `Option` because we won't know - /// the blob parent if we don't end up getting any blobs in the response. - fn get_parent_root(verified_response: &Self::VerifiedResponseType) -> Option; - - /// Caches the verified response in the lookup if necessary. This is only necessary for lookups - /// triggered by `UnknownParent` errors. - fn add_to_child_components( - verified_response: Self::VerifiedResponseType, - components: &mut ChildComponents, - ); - - /// Convert a verified response to the type we send to the beacon processor. - fn verified_to_reconstructed( - block_root: Hash256, - verified: Self::VerifiedResponseType, - ) -> Self::ReconstructedResponseType; - /// Send the response to the beacon processor. - fn send_reconstructed_for_processing( + fn send_for_processing( id: Id, - bl: &BlockLookups, - block_root: Hash256, - verified: Self::ReconstructedResponseType, - duration: Duration, + result: DownloadResult, cx: &SyncNetworkContext, ) -> Result<(), LookupRequestError>; - /// Register a failure to process the block or blob. - fn register_failure_downloading(&mut self) { - self.get_state_mut().on_download_failure() - } - /* Utility methods */ /// Returns the `ResponseType` associated with this trait implementation. Useful in logging. @@ -171,64 +118,44 @@ pub trait RequestState { fn request_state_mut(request: &mut SingleBlockLookup) -> &mut Self; /// A getter for a reference to the `SingleLookupRequestState` associated with this trait. - fn get_state(&self) -> &SingleLookupRequestState; + fn get_state(&self) -> &SingleLookupRequestState; /// A getter for a mutable reference to the SingleLookupRequestState associated with this trait. - fn get_state_mut(&mut self) -> &mut SingleLookupRequestState; + fn get_state_mut(&mut self) -> &mut SingleLookupRequestState; } -impl RequestState for BlockRequestState { - type RequestType = BlocksByRootSingleRequest; +impl RequestState for BlockRequestState { type VerifiedResponseType = Arc>; - type ReconstructedResponseType = RpcBlock; - - fn new_request(&self) -> Self::RequestType { - BlocksByRootSingleRequest(self.requested_block_root) - } fn make_request( - id: SingleLookupReqId, + &self, + id: SingleLookupId, peer_id: PeerId, - request: Self::RequestType, + _: Option, cx: &mut SyncNetworkContext, - ) -> Result<(), LookupRequestError> { - cx.block_lookup_request(id, peer_id, request) + ) -> Result { + cx.block_lookup_request(id, peer_id, self.requested_block_root) .map_err(LookupRequestError::SendFailed) } - fn get_parent_root(verified_response: &Arc>) -> Option { - Some(verified_response.parent_root()) - } - - fn add_to_child_components( - verified_response: Arc>, - components: &mut ChildComponents, - ) { - components.merge_block(verified_response); - } - - fn verified_to_reconstructed( - block_root: Hash256, - block: Arc>, - ) -> RpcBlock { - RpcBlock::new_without_blobs(Some(block_root), block) - } - - fn send_reconstructed_for_processing( - id: Id, - bl: &BlockLookups, - block_root: Hash256, - constructed: RpcBlock, - duration: Duration, + fn send_for_processing( + id: SingleLookupId, + download_result: DownloadResult, cx: &SyncNetworkContext, ) -> Result<(), LookupRequestError> { - bl.send_block_for_processing( + let DownloadResult { + value, block_root, - constructed, - duration, + seen_timestamp, + peer_id: _, + } = download_result; + cx.send_block_for_processing( + block_root, + RpcBlock::new_without_blobs(Some(block_root), value), + seen_timestamp, BlockProcessType::SingleBlock { id }, - cx, ) + .map_err(LookupRequestError::SendFailed) } fn response_type() -> ResponseType { @@ -237,73 +164,51 @@ impl RequestState for BlockRequestState { fn request_state_mut(request: &mut SingleBlockLookup) -> &mut Self { &mut request.block_request_state } - fn get_state(&self) -> &SingleLookupRequestState { + fn get_state(&self) -> &SingleLookupRequestState { &self.state } - fn get_state_mut(&mut self) -> &mut SingleLookupRequestState { + fn get_state_mut(&mut self) -> &mut SingleLookupRequestState { &mut self.state } } impl RequestState for BlobRequestState { - type RequestType = BlobsByRootSingleBlockRequest; type VerifiedResponseType = FixedBlobSidecarList; - type ReconstructedResponseType = FixedBlobSidecarList; - - fn new_request(&self) -> Self::RequestType { - BlobsByRootSingleBlockRequest { - block_root: self.block_root, - indices: self.requested_ids.indices(), - } - } fn make_request( - id: SingleLookupReqId, + &self, + id: Id, peer_id: PeerId, - request: Self::RequestType, + downloaded_block_expected_blobs: Option, cx: &mut SyncNetworkContext, - ) -> Result<(), LookupRequestError> { - cx.blob_lookup_request(id, peer_id, request) - .map_err(LookupRequestError::SendFailed) - } - - fn get_parent_root(verified_response: &FixedBlobSidecarList) -> Option { - verified_response - .into_iter() - .filter_map(|blob| blob.as_ref()) - .map(|blob| blob.block_parent_root()) - .next() - } - - fn add_to_child_components( - verified_response: FixedBlobSidecarList, - components: &mut ChildComponents, - ) { - components.merge_blobs(verified_response); - } - - fn verified_to_reconstructed( - _block_root: Hash256, - blobs: FixedBlobSidecarList, - ) -> FixedBlobSidecarList { - blobs + ) -> Result { + cx.blob_lookup_request( + id, + peer_id, + self.block_root, + downloaded_block_expected_blobs, + ) + .map_err(LookupRequestError::SendFailed) } - fn send_reconstructed_for_processing( + fn send_for_processing( id: Id, - bl: &BlockLookups, - block_root: Hash256, - verified: FixedBlobSidecarList, - duration: Duration, + download_result: DownloadResult, cx: &SyncNetworkContext, ) -> Result<(), LookupRequestError> { - bl.send_blobs_for_processing( + let DownloadResult { + value, + block_root, + seen_timestamp, + peer_id: _, + } = download_result; + cx.send_blobs_for_processing( block_root, - verified, - duration, + value, + seen_timestamp, BlockProcessType::SingleBlob { id }, - cx, ) + .map_err(LookupRequestError::SendFailed) } fn response_type() -> ResponseType { @@ -312,10 +217,10 @@ impl RequestState for BlobRequestState { fn request_state_mut(request: &mut SingleBlockLookup) -> &mut Self { &mut request.blob_request_state } - fn get_state(&self) -> &SingleLookupRequestState { + fn get_state(&self) -> &SingleLookupRequestState { &self.state } - fn get_state_mut(&mut self) -> &mut SingleLookupRequestState { + fn get_state_mut(&mut self) -> &mut SingleLookupRequestState { &mut self.state } } diff --git a/beacon_node/network/src/sync/block_lookups/mod.rs b/beacon_node/network/src/sync/block_lookups/mod.rs index a2909b49dd1..6852761d8bf 100644 --- a/beacon_node/network/src/sync/block_lookups/mod.rs +++ b/beacon_node/network/src/sync/block_lookups/mod.rs @@ -1,20 +1,15 @@ -use self::single_block_lookup::SingleBlockLookup; -use super::manager::BlockProcessingResult; -use super::network_context::{LookupFailure, LookupVerifyError}; -use super::BatchProcessResult; -use super::{manager::BlockProcessType, network_context::SyncNetworkContext}; +use self::parent_chain::{compute_parent_chains, NodeChain}; +pub use self::single_block_lookup::DownloadResult; +use self::single_block_lookup::{LookupRequestError, LookupResult, SingleBlockLookup}; +use super::manager::{BlockProcessType, BlockProcessingResult}; +use super::network_context::{RpcProcessingResult, SyncNetworkContext}; use crate::metrics; -use crate::network_beacon_processor::ChainSegmentProcessId; -use crate::sync::block_lookups::common::LookupType; -use crate::sync::block_lookups::parent_lookup::{ParentLookup, RequestError}; -use crate::sync::block_lookups::single_block_lookup::{CachedChild, LookupRequestError}; -use crate::sync::manager::{Id, SingleLookupReqId}; -use beacon_chain::block_verification_types::{AsBlock, RpcBlock}; -pub use beacon_chain::data_availability_checker::ChildComponents; -use beacon_chain::data_availability_checker::{ - AvailabilityCheckErrorCategory, DataAvailabilityChecker, -}; -use beacon_chain::validator_monitor::timestamp_now; +use crate::sync::block_lookups::common::{ResponseType, PARENT_DEPTH_TOLERANCE}; +use crate::sync::block_lookups::parent_chain::find_oldest_fork_ancestor; +use crate::sync::manager::Id; +use crate::sync::network_context::LookupFailure; +use beacon_chain::block_verification_types::AsBlock; +use beacon_chain::data_availability_checker::AvailabilityCheckErrorCategory; use beacon_chain::{AvailabilityProcessingStatus, BeaconChainTypes, BlockError}; pub use common::RequestState; use fnv::FnvHashMap; @@ -22,735 +17,423 @@ use lighthouse_network::{PeerAction, PeerId}; use lru_cache::LRUTimeCache; pub use single_block_lookup::{BlobRequestState, BlockRequestState}; use slog::{debug, error, trace, warn, Logger}; -use smallvec::SmallVec; -use std::collections::{HashMap, VecDeque}; +use std::collections::hash_map::Entry; use std::sync::Arc; use std::time::Duration; use store::Hash256; -use types::blob_sidecar::FixedBlobSidecarList; -use types::Slot; +use types::{BlobSidecar, EthSpec, SignedBeaconBlock}; pub mod common; -mod parent_lookup; +pub mod parent_chain; mod single_block_lookup; #[cfg(test)] mod tests; -pub type DownloadedBlock = (Hash256, RpcBlock); - const FAILED_CHAINS_CACHE_EXPIRY_SECONDS: u64 = 60; -pub const SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS: u8 = 3; +pub const SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS: u8 = 4; + +pub enum BlockComponent { + Block(DownloadResult>>), + Blob(DownloadResult>>), +} + +impl BlockComponent { + fn parent_root(&self) -> Hash256 { + match self { + BlockComponent::Block(block) => block.value.parent_root(), + BlockComponent::Blob(blob) => blob.value.block_parent_root(), + } + } + fn get_type(&self) -> &'static str { + match self { + BlockComponent::Block(_) => "block", + BlockComponent::Blob(_) => "blob", + } + } +} + +pub type SingleLookupId = u32; enum Action { Retry, - ParentUnknown { parent_root: Hash256, slot: Slot }, + ParentUnknown { parent_root: Hash256 }, Drop, Continue, } pub struct BlockLookups { - /// Parent chain lookups being downloaded. - parent_lookups: SmallVec<[ParentLookup; 3]>, - - processing_parent_lookups: HashMap, SingleBlockLookup)>, - /// A cache of failed chain lookups to prevent duplicate searches. failed_chains: LRUTimeCache, - single_block_lookups: FnvHashMap>, - - pub(crate) da_checker: Arc>, + // TODO: Why not index lookups by block_root? + single_block_lookups: FnvHashMap>, /// The logger for the import manager. log: Logger, } impl BlockLookups { - pub fn new(da_checker: Arc>, log: Logger) -> Self { + pub fn new(log: Logger) -> Self { Self { - parent_lookups: Default::default(), - processing_parent_lookups: Default::default(), failed_chains: LRUTimeCache::new(Duration::from_secs( FAILED_CHAINS_CACHE_EXPIRY_SECONDS, )), single_block_lookups: Default::default(), - da_checker, log, } } #[cfg(test)] - pub(crate) fn active_single_lookups(&self) -> Vec { - self.single_block_lookups.keys().cloned().collect() + pub(crate) fn insert_failed_chain(&mut self, block_root: Hash256) { + self.failed_chains.insert(block_root); } #[cfg(test)] - pub(crate) fn active_parent_lookups(&self) -> Vec { - self.parent_lookups - .iter() - .map(|r| r.chain_hash()) - .collect::>() + pub(crate) fn get_failed_chains(&mut self) -> Vec { + self.failed_chains.keys().cloned().collect() } #[cfg(test)] - pub(crate) fn failed_chains_contains(&mut self, chain_hash: &Hash256) -> bool { - self.failed_chains.contains(chain_hash) + pub(crate) fn active_single_lookups(&self) -> Vec<(Id, Hash256, Option)> { + self.single_block_lookups + .iter() + .map(|(id, e)| (*id, e.block_root(), e.awaiting_parent())) + .collect() + } + + /// Returns a vec of all parent lookup chains by tip, in descending slot order (tip first) + pub(crate) fn active_parent_lookups(&self) -> Vec { + compute_parent_chains( + &self + .single_block_lookups + .values() + .map(|lookup| lookup.into()) + .collect::>(), + ) } /* Lookup requests */ - /// Creates a lookup for the block with the given `block_root` and immediately triggers it. - pub fn search_block( + /// Creates a parent lookup for the block with the given `block_root` and immediately triggers it. + /// If a parent lookup exists or is triggered, a current lookup will be created. + pub fn search_child_and_parent( &mut self, block_root: Hash256, - peer_source: &[PeerId], + block_component: BlockComponent, + peer_id: PeerId, cx: &mut SyncNetworkContext, ) { - self.new_current_lookup(block_root, None, peer_source, cx) + let parent_root = block_component.parent_root(); + + let parent_lookup_exists = + self.search_parent_of_child(parent_root, block_root, &[peer_id], cx); + // Only create the child lookup if the parent exists + if parent_lookup_exists { + // `search_parent_of_child` ensures that parent root is not a failed chain + self.new_current_lookup( + block_root, + Some(block_component), + Some(parent_root), + &[peer_id], + cx, + ); + } } - /// Creates a lookup for the block with the given `block_root`, while caching other block - /// components we've already received. The block components are cached here because we haven't - /// imported its parent and therefore can't fully validate it and store it in the data - /// availability cache. - /// - /// The request is immediately triggered. - pub fn search_child_block( + /// Seach a block whose parent root is unknown. + /// Returns true if the lookup is created or already exists + pub fn search_unknown_block( &mut self, block_root: Hash256, - child_components: ChildComponents, peer_source: &[PeerId], cx: &mut SyncNetworkContext, ) { - self.new_current_lookup(block_root, Some(child_components), peer_source, cx) + self.new_current_lookup(block_root, None, None, peer_source, cx); } - /// Attempts to trigger the request matching the given `block_root`. - pub fn trigger_single_lookup( + /// A block or blob triggers the search of a parent. + /// Check if this new lookup extends a bad chain: + /// - Extending `child_block_root_trigger` would exceed the max depth + /// - `block_root_to_search` is a failed chain + /// Returns true if the lookup is created or already exists + pub fn search_parent_of_child( &mut self, - mut single_block_lookup: SingleBlockLookup, + block_root_to_search: Hash256, + child_block_root_trigger: Hash256, + peers: &[PeerId], cx: &mut SyncNetworkContext, - ) { - let block_root = single_block_lookup.block_root(); - match single_block_lookup.request_block_and_blobs(cx) { - Ok(()) => self.add_single_lookup(single_block_lookup), - Err(e) => { - debug!(self.log, "Single block lookup failed"; - "error" => ?e, - "block_root" => ?block_root, - ); + ) -> bool { + let parent_chains = self.active_parent_lookups(); + + for (chain_idx, parent_chain) in parent_chains.iter().enumerate() { + if parent_chain.ancestor() == child_block_root_trigger + && parent_chain.len() >= PARENT_DEPTH_TOLERANCE + { + debug!(self.log, "Parent lookup chain too long"; "block_root" => ?block_root_to_search); + + // Searching for this parent would extend a parent chain over the max + // Insert the tip only to failed chains + self.failed_chains.insert(parent_chain.tip); + + // Note: Drop only the chain that's too long until it merges with another chain + // that's not too long. Consider this attack: there's a chain of valid unknown + // blocks A -> B. A malicious peer builds `PARENT_DEPTH_TOLERANCE` garbage + // blocks on top of A forming A -> C. The malicious peer forces us to fetch C + // from it, which will result in parent A hitting the chain_too_long error. Then + // the valid chain A -> B is dropped too. + if let Ok(block_to_drop) = find_oldest_fork_ancestor(parent_chains, chain_idx) { + // Drop all lookups descending from the child of the too long parent chain + if let Some((lookup_id, lookup)) = self + .single_block_lookups + .iter() + .find(|(_, l)| l.block_root() == block_to_drop) + { + for &peer_id in lookup.all_used_peers() { + cx.report_peer( + peer_id, + PeerAction::LowToleranceError, + "chain_too_long", + ); + } + self.drop_lookup_and_children(*lookup_id); + } + } + + return false; } } - } - - /// Adds a lookup to the `single_block_lookups` map. - pub fn add_single_lookup(&mut self, single_block_lookup: SingleBlockLookup) { - self.single_block_lookups - .insert(single_block_lookup.id, single_block_lookup); - metrics::set_gauge( - &metrics::SYNC_SINGLE_BLOCK_LOOKUPS, - self.single_block_lookups.len() as i64, - ); + // `block_root_to_search` is a failed chain check happens inside new_current_lookup + self.new_current_lookup(block_root_to_search, None, None, peers, cx) } /// Searches for a single block hash. If the blocks parent is unknown, a chain of blocks is /// constructed. - pub fn new_current_lookup( + /// Returns true if the lookup is created or already exists + fn new_current_lookup( &mut self, block_root: Hash256, - child_components: Option>, + block_component: Option>, + awaiting_parent: Option, peers: &[PeerId], cx: &mut SyncNetworkContext, - ) { + ) -> bool { + // If this block or it's parent is part of a known failed chain, ignore it. + if self.failed_chains.contains(&block_root) { + debug!(self.log, "Block is from a past failed chain. Dropping"; "block_root" => ?block_root); + for peer_id in peers { + cx.report_peer(*peer_id, PeerAction::MidToleranceError, "failed_chain"); + } + return false; + } + // Do not re-request a block that is already being requested if let Some((_, lookup)) = self .single_block_lookups .iter_mut() .find(|(_id, lookup)| lookup.is_for_block(block_root)) { + trace!(self.log, "Adding peer to existing single block lookup"; "block_root" => %block_root); lookup.add_peers(peers); - if let Some(components) = child_components { - lookup.add_child_components(components); + if let Some(block_component) = block_component { + let component_type = block_component.get_type(); + let imported = lookup.add_child_components(block_component); + if !imported { + debug!(self.log, "Lookup child component ignored"; "block_root" => %block_root, "type" => component_type); + } } - return; - } - - if let Some(parent_lookup) = self.parent_lookups.iter_mut().find(|parent_req| { - parent_req.is_for_block(block_root) || parent_req.contains_block(&block_root) - }) { - parent_lookup.add_peers(peers); - - // If the block was already downloaded, or is being downloaded in this moment, do not - // request it. - trace!(self.log, "Already searching for block in a parent lookup request"; "block_root" => ?block_root); - return; + return true; } - if self - .processing_parent_lookups - .values() - .any(|(hashes, _last_parent_request)| hashes.contains(&block_root)) - { - // we are already processing this block, ignore it. - trace!(self.log, "Already processing block in a parent request"; "block_root" => ?block_root); - return; + // Ensure that awaiting parent exists, otherwise this lookup won't be able to make progress + if let Some(awaiting_parent) = awaiting_parent { + if !self + .single_block_lookups + .iter() + .any(|(_, lookup)| lookup.is_for_block(awaiting_parent)) + { + return false; + } } - let msg = if child_components.is_some() { + let msg = if block_component.is_some() { "Searching for components of a block with unknown parent" } else { "Searching for block components" }; - - let lookup = SingleBlockLookup::new( - block_root, - child_components, - peers, - self.da_checker.clone(), - cx.next_id(), - LookupType::Current, - ); - debug!( self.log, "{}", msg; "peer_ids" => ?peers, "block" => ?block_root, ); - self.trigger_single_lookup(lookup, cx); - } + metrics::inc_counter(&metrics::SYNC_LOOKUP_CREATED); - /// If a block is attempted to be processed but we do not know its parent, this function is - /// called in order to find the block's parent. - pub fn search_parent( - &mut self, - slot: Slot, - block_root: Hash256, - parent_root: Hash256, - peer_id: PeerId, - cx: &mut SyncNetworkContext, - ) { - // If this block or it's parent is part of a known failed chain, ignore it. - if self.failed_chains.contains(&parent_root) || self.failed_chains.contains(&block_root) { - debug!(self.log, "Block is from a past failed chain. Dropping"; - "block_root" => ?block_root, "block_slot" => slot); - return; - } + // If we know that this lookup has unknown parent (is awaiting a parent lookup to resolve), + // signal here to hold processing downloaded data. + let mut lookup = SingleBlockLookup::new(block_root, peers, cx.next_id(), awaiting_parent); - // Make sure this block is not already downloaded, and that neither it or its parent is - // being searched for. - if let Some(parent_lookup) = self.parent_lookups.iter_mut().find(|parent_req| { - parent_req.contains_block(&parent_root) || parent_req.is_for_block(parent_root) - }) { - parent_lookup.add_peer(peer_id); - // we are already searching for this block, ignore it - debug!(self.log, "Already searching for parent block"; - "block_root" => ?block_root, "parent_root" => ?parent_root); - return; + // Add block components to the new request + if let Some(block_component) = block_component { + lookup.add_child_components(block_component); } - if self - .processing_parent_lookups - .iter() - .any(|(chain_hash, (hashes, _peers))| { - chain_hash == &block_root - || hashes.contains(&block_root) - || hashes.contains(&parent_root) - }) - { - // we are already processing this block, ignore it. - debug!(self.log, "Already processing parent block"; - "block_root" => ?block_root, "parent_root" => ?parent_root); - return; - } - let parent_lookup = ParentLookup::new( - block_root, - parent_root, - peer_id, - self.da_checker.clone(), - cx, - ); - - debug!(self.log, "Created new parent lookup"; "block_root" => ?block_root, "parent_root" => ?parent_root); + let id = lookup.id; + let lookup = match self.single_block_lookups.entry(id) { + Entry::Vacant(entry) => entry.insert(lookup), + Entry::Occupied(_) => { + // Should never happen + warn!(self.log, "Lookup exists with same id"; "id" => id); + return false; + } + }; - self.request_parent(parent_lookup, cx); + let result = lookup.continue_requests(cx); + self.on_lookup_result(id, result, "new_current_lookup", cx); + self.update_metrics(); + true } /* Lookup responses */ - /// Get a single block lookup by its ID. This method additionally ensures the `req_counter` - /// matches the current `req_counter` for the lookup. This ensures any stale responses from requests - /// that have been retried are ignored. - fn get_single_lookup>( + /// Process a block or blob response received from a single lookup request. + pub fn on_download_response>( &mut self, - id: SingleLookupReqId, - ) -> Option> { - let mut lookup = self.single_block_lookups.remove(&id.id)?; - - let request_state = R::request_state_mut(&mut lookup); - if request_state - .get_state() - .is_current_req_counter(id.req_counter) - { - Some(lookup) - } else { - // We don't want to drop the lookup, just ignore the old response. - self.single_block_lookups.insert(id.id, lookup); - None - } - } - - /// Checks whether a single block lookup is waiting for a parent lookup to complete. This is - /// necessary because we want to make sure all parents are processed before sending a child - /// for processing, otherwise the block will fail validation and will be returned to the network - /// layer with an `UnknownParent` error. - pub fn has_pending_parent_request(&self, block_root: Hash256) -> bool { - self.parent_lookups - .iter() - .any(|parent_lookup| parent_lookup.chain_hash() == block_root) + id: SingleLookupId, + peer_id: PeerId, + response: RpcProcessingResult, + cx: &mut SyncNetworkContext, + ) { + let result = self.on_download_response_inner::(id, peer_id, response, cx); + self.on_lookup_result(id, result, "download_response", cx); } /// Process a block or blob response received from a single lookup request. - pub fn single_lookup_response>( + pub fn on_download_response_inner>( &mut self, - lookup_id: SingleLookupReqId, + id: SingleLookupId, peer_id: PeerId, - response: R::VerifiedResponseType, - seen_timestamp: Duration, + response: RpcProcessingResult, cx: &mut SyncNetworkContext, - ) { - let id = lookup_id.id; - let response_type = R::response_type(); + ) -> Result { + // Downscore peer even if lookup is not known + // Only downscore lookup verify errors. RPC errors are downscored in the network handler. + if let Err(LookupFailure::LookupVerifyError(e)) = &response { + // Note: the error is displayed in full debug form on the match below + cx.report_peer(peer_id, PeerAction::LowToleranceError, e.into()); + } - let Some(mut lookup) = self.get_single_lookup::(lookup_id) else { + let response_type = R::response_type(); + let Some(lookup) = self.single_block_lookups.get_mut(&id) else { // We don't have the ability to cancel in-flight RPC requests. So this can happen // if we started this RPC request, and later saw the block/blobs via gossip. - debug!( - self.log, - "Block returned for single block lookup not present"; - "response_type" => ?response_type, - ); - return; + debug!(self.log, "Block returned for single block lookup not present"; "id" => id); + return Err(LookupRequestError::UnknownLookup); }; - let expected_block_root = lookup.block_root(); - debug!(self.log, - "Peer returned response for single lookup"; - "peer_id" => %peer_id , - "id" => ?id, - "block_root" => ?expected_block_root, - "response_type" => ?response_type, - ); + let block_root = lookup.block_root(); + let request_state = R::request_state_mut(lookup).get_state_mut(); - match self.handle_verified_response::( - seen_timestamp, - cx, - BlockProcessType::SingleBlock { id: lookup.id }, - response, - &mut lookup, - ) { - Ok(_) => { - self.single_block_lookups.insert(id, lookup); - } - Err(e) => { + match response { + Ok((response, seen_timestamp)) => { debug!(self.log, - "Single lookup request failed"; - "error" => ?e, - "block_root" => ?expected_block_root, + "Received lookup download success"; + "block_root" => %block_root, + "peer_id" => %peer_id, + "response_type" => ?response_type, ); - } - } - metrics::set_gauge( - &metrics::SYNC_SINGLE_BLOCK_LOOKUPS, - self.single_block_lookups.len() as i64, - ); - } - - /// Consolidates error handling for `single_lookup_response`. An `Err` here should always mean - /// the lookup is dropped. - fn handle_verified_response>( - &self, - seen_timestamp: Duration, - cx: &mut SyncNetworkContext, - process_type: BlockProcessType, - verified_response: R::VerifiedResponseType, - lookup: &mut SingleBlockLookup, - ) -> Result<(), LookupRequestError> { - let id = lookup.id; - let block_root = lookup.block_root(); - - let cached_child = lookup.add_response::(verified_response.clone()); - match cached_child { - CachedChild::Ok(block) => { - // If we have an outstanding parent request for this block, delay sending the response until - // all parent blocks have been processed, otherwise we will fail validation with an - // `UnknownParent`. - let delay_send = match lookup.lookup_type { - LookupType::Parent => false, - LookupType::Current => self.has_pending_parent_request(lookup.block_root()), - }; - - if !delay_send { - R::request_state_mut(lookup) - .get_state_mut() - .on_download_success() - .map_err(LookupRequestError::BadState)?; - self.send_block_for_processing( - block_root, - block, - seen_timestamp, - process_type, - cx, - )? - } - } - CachedChild::DownloadIncomplete => { - R::request_state_mut(lookup) - .get_state_mut() - .on_download_success() - .map_err(LookupRequestError::BadState)?; - // If this was the result of a block request, we can't determine if the block peer - // did anything wrong. If we already had both a block and blobs response processed, - // we should penalize the blobs peer because they did not provide all blobs on the - // initial request. - if lookup.both_components_downloaded() { - lookup.penalize_blob_peer(cx); - lookup.blob_request_state.state.on_download_failure(); - } - lookup.request_block_and_blobs(cx)?; - } - CachedChild::NotRequired => { - R::request_state_mut(lookup) - .get_state_mut() - .on_download_success() - .map_err(LookupRequestError::BadState)?; - - R::send_reconstructed_for_processing( - id, - self, + // Register the download peer here. Once we have received some data over the wire we + // attribute it to this peer for scoring latter regardless of how the request was + // done. + request_state.on_download_success(DownloadResult { + value: response, block_root, - R::verified_to_reconstructed(block_root, verified_response), seen_timestamp, - cx, - )? - } - CachedChild::Err(e) => { - warn!(self.log, "Consistency error in cached block"; - "error" => ?e, - "block_root" => ?block_root - ); - lookup.handle_consistency_failure(cx); - lookup.request_block_and_blobs(cx)?; - } - } - Ok(()) - } - - /// Get a parent block lookup by its ID. This method additionally ensures the `req_counter` - /// matches the current `req_counter` for the lookup. This any stale responses from requests - /// that have been retried are ignored. - fn get_parent_lookup>( - &mut self, - id: SingleLookupReqId, - ) -> Option> { - let mut parent_lookup = if let Some(pos) = self - .parent_lookups - .iter() - .position(|request| request.current_parent_request.id == id.id) - { - self.parent_lookups.remove(pos) - } else { - return None; - }; - - if R::request_state_mut(&mut parent_lookup.current_parent_request) - .get_state() - .is_current_req_counter(id.req_counter) - { - Some(parent_lookup) - } else { - self.parent_lookups.push(parent_lookup); - None - } - } - - /// Process a response received from a parent lookup request. - pub fn parent_lookup_response>( - &mut self, - id: SingleLookupReqId, - peer_id: PeerId, - response: R::VerifiedResponseType, - seen_timestamp: Duration, - cx: &mut SyncNetworkContext, - ) { - let Some(mut parent_lookup) = self.get_parent_lookup::(id) else { - debug!(self.log, "Response for a parent lookup request that was not found"; "peer_id" => %peer_id); - return; - }; - - debug!(self.log, - "Peer returned response for parent lookup"; - "peer_id" => %peer_id , - "id" => ?id, - "block_root" => ?parent_lookup.current_parent_request.block_request_state.requested_block_root, - "response_type" => ?R::response_type(), - ); - - match self.parent_lookup_response_inner::( - peer_id, - response, - seen_timestamp, - cx, - &mut parent_lookup, - ) { - Ok(()) => { - self.parent_lookups.push(parent_lookup); + peer_id, + })?; + // continue_request will send for processing as the request state is AwaitingProcessing } Err(e) => { - self.handle_parent_request_error(&mut parent_lookup, cx, e); - } - } - - metrics::set_gauge( - &metrics::SYNC_PARENT_BLOCK_LOOKUPS, - self.parent_lookups.len() as i64, - ); - } - - /// Consolidates error handling for `parent_lookup_response`. An `Err` here should always mean - /// the lookup is dropped. - fn parent_lookup_response_inner>( - &mut self, - peer_id: PeerId, - response: R::VerifiedResponseType, - seen_timestamp: Duration, - cx: &mut SyncNetworkContext, - parent_lookup: &mut ParentLookup, - ) -> Result<(), RequestError> { - // check if the parent of this block isn't in the failed cache. If it is, this chain should - // be dropped and the peer downscored. - if let Some(parent_root) = R::get_parent_root(&response) { - if self.failed_chains.contains(&parent_root) { - let request_state = R::request_state_mut(&mut parent_lookup.current_parent_request); - request_state.register_failure_downloading(); - debug!( - self.log, - "Parent chain ignored due to past failure"; - "block" => %parent_root, + debug!(self.log, + "Received lookup download failure"; + "block_root" => %block_root, + "peer_id" => %peer_id, + "response_type" => ?response_type, + "error" => %e, ); - // Add the root block to failed chains - self.failed_chains.insert(parent_lookup.chain_hash()); - cx.report_peer( - peer_id, - PeerAction::MidToleranceError, - "bbroot_failed_chains", - ); - return Ok(()); + request_state.on_download_failure()?; + // continue_request will retry a download as the request state is AwaitingDownload } } - self.handle_verified_response::( - seen_timestamp, - cx, - BlockProcessType::ParentLookup { - chain_hash: parent_lookup.chain_hash(), - }, - response, - &mut parent_lookup.current_parent_request, - )?; - - Ok(()) - } - - /// Handle logging and peer scoring for `RequestError`s during parent lookup requests. - fn handle_parent_request_error( - &mut self, - parent_lookup: &mut ParentLookup, - cx: &SyncNetworkContext, - e: RequestError, - ) { - debug!(self.log, "Failed to request parent"; "error" => e.as_static()); - match e { - RequestError::SendFailed(_) => { - // Probably shutting down, nothing to do here. Drop the request - } - RequestError::ChainTooLong => { - self.failed_chains.insert(parent_lookup.chain_hash()); - // This indicates faulty peers. - for &peer_id in parent_lookup.all_used_peers() { - cx.report_peer(peer_id, PeerAction::LowToleranceError, e.as_static()) - } - } - RequestError::TooManyAttempts { cannot_process } => { - // We only consider the chain failed if we were unable to process it. - // We could have failed because one peer continually failed to send us - // bad blocks. We still allow other peers to send us this chain. Note - // that peers that do this, still get penalised. - if cannot_process { - self.failed_chains.insert(parent_lookup.chain_hash()); - } - // This indicates faulty peers. - for &peer_id in parent_lookup.all_used_peers() { - cx.report_peer(peer_id, PeerAction::LowToleranceError, e.as_static()) - } - } - RequestError::NoPeers => { - // This happens if the peer disconnects while the block is being - // processed. Drop the request without extra penalty - } - RequestError::BadState(..) => { - warn!(self.log, "Failed to request parent"; "error" => e.as_static()); - } - } + lookup.continue_requests(cx) } /* Error responses */ - pub fn peer_disconnected(&mut self, peer_id: &PeerId, cx: &mut SyncNetworkContext) { + pub fn peer_disconnected(&mut self, peer_id: &PeerId) { /* Check disconnection for single lookups */ self.single_block_lookups.retain(|_, req| { let should_drop_lookup = - req.should_drop_lookup_on_disconnected_peer(peer_id, cx, &self.log); + req.should_drop_lookup_on_disconnected_peer(peer_id ); + + if should_drop_lookup { + debug!(self.log, "Dropping single lookup after peer disconnection"; "block_root" => %req.block_root()); + } !should_drop_lookup }); - - /* Check disconnection for parent lookups */ - while let Some(pos) = self - .parent_lookups - .iter_mut() - .position(|req| req.check_peer_disconnected(peer_id).is_err()) - { - let parent_lookup = self.parent_lookups.remove(pos); - debug!(self.log, "Dropping parent lookup after peer disconnected"; &parent_lookup); - self.request_parent(parent_lookup, cx); - } } - /// An RPC error has occurred during a parent lookup. This function handles this case. - pub fn parent_lookup_failed>( - &mut self, - id: SingleLookupReqId, - peer_id: &PeerId, - cx: &mut SyncNetworkContext, - error: LookupFailure, - ) { - // Only downscore lookup verify errors. RPC errors are downscored in the network handler. - if let LookupFailure::LookupVerifyError(e) = &error { - // Downscore peer even if lookup is not known - self.downscore_on_rpc_error(peer_id, e, cx); - } - - let Some(mut parent_lookup) = self.get_parent_lookup::(id) else { - debug!(self.log, - "RPC failure for a block parent lookup request that was not found"; - "peer_id" => %peer_id, - "error" => %error - ); - return; - }; - R::request_state_mut(&mut parent_lookup.current_parent_request) - .register_failure_downloading(); - debug!(self.log, "Parent lookup block request failed"; - "chain_hash" => %parent_lookup.chain_hash(), "id" => ?id, "error" => %error - ); - - self.request_parent(parent_lookup, cx); - - metrics::set_gauge( - &metrics::SYNC_PARENT_BLOCK_LOOKUPS, - self.parent_lookups.len() as i64, - ); - } + /* Processing responses */ - /// An RPC error has occurred during a single lookup. This function handles this case.\ - pub fn single_block_lookup_failed>( + pub fn on_processing_result( &mut self, - id: SingleLookupReqId, - peer_id: &PeerId, + process_type: BlockProcessType, + result: BlockProcessingResult, cx: &mut SyncNetworkContext, - error: LookupFailure, ) { - // Only downscore lookup verify errors. RPC errors are downscored in the network handler. - if let LookupFailure::LookupVerifyError(e) = &error { - // Downscore peer even if lookup is not known - self.downscore_on_rpc_error(peer_id, e, cx); - } - - let log = self.log.clone(); - let Some(mut lookup) = self.get_single_lookup::(id) else { - debug!(log, "Error response to dropped lookup"; "error" => %error); - return; + let lookup_result = match process_type { + BlockProcessType::SingleBlock { id } => { + self.on_processing_result_inner::>(id, result, cx) + } + BlockProcessType::SingleBlob { id } => { + self.on_processing_result_inner::>(id, result, cx) + } }; - let block_root = lookup.block_root(); - let request_state = R::request_state_mut(&mut lookup); - let response_type = R::response_type(); - trace!(log, - "Single lookup failed"; - "block_root" => ?block_root, - "error" => %error, - "peer_id" => %peer_id, - "response_type" => ?response_type - ); - let id = id.id; - request_state.register_failure_downloading(); - if let Err(e) = lookup.request_block_and_blobs(cx) { - debug!(self.log, - "Single lookup retry failed"; - "error" => ?e, - "block_root" => ?block_root, - ); - } else { - self.single_block_lookups.insert(id, lookup); - } - - metrics::set_gauge( - &metrics::SYNC_SINGLE_BLOCK_LOOKUPS, - self.single_block_lookups.len() as i64, - ); + self.on_lookup_result(process_type.id(), lookup_result, "processing_result", cx); } - /* Processing responses */ - - pub fn single_block_component_processed>( + pub fn on_processing_result_inner>( &mut self, - target_id: Id, + lookup_id: SingleLookupId, result: BlockProcessingResult, cx: &mut SyncNetworkContext, - ) { - let Some(mut lookup) = self.single_block_lookups.remove(&target_id) else { - debug!(self.log, "Unknown single block lookup"; "target_id" => target_id); - return; + ) -> Result { + let Some(lookup) = self.single_block_lookups.get_mut(&lookup_id) else { + debug!(self.log, "Unknown single block lookup"; "id" => lookup_id); + return Err(LookupRequestError::UnknownLookup); }; let block_root = lookup.block_root(); - let request_state = R::request_state_mut(&mut lookup); + let request_state = R::request_state_mut(lookup).get_state_mut(); - let peer_id = match request_state.get_state().processing_peer() { - Ok(peer_id) => peer_id, - Err(e) => { - debug!(self.log, "Attempting to process single block lookup in bad state"; "id" => target_id, "response_type" => ?R::response_type(), "error" => e); - return; - } - }; debug!( self.log, - "Block component processed for lookup"; - "response_type" => ?R::response_type(), + "Received lookup processing result"; + "component" => ?R::response_type(), "block_root" => ?block_root, "result" => ?result, - "id" => target_id, ); let action = match result { BlockProcessingResult::Ok(AvailabilityProcessingStatus::Imported(_)) - | BlockProcessingResult::Err(BlockError::BlockIsAlreadyKnown { .. }) => { + | BlockProcessingResult::Err(BlockError::BlockIsAlreadyKnown(_)) => { // Successfully imported - trace!(self.log, "Single block processing succeeded"; "block" => %block_root); - Action::Drop + request_state.on_processing_success()?; + Action::Continue } BlockProcessingResult::Ok(AvailabilityProcessingStatus::MissingComponents( @@ -759,54 +442,53 @@ impl BlockLookups { )) => { // `on_processing_success` is called here to ensure the request state is updated prior to checking // if both components have been processed. - if R::request_state_mut(&mut lookup) - .get_state_mut() - .on_processing_success() - .is_err() - { - warn!( - self.log, - "Single block processing state incorrect"; - "action" => "dropping single block request" - ); - Action::Drop + request_state.on_processing_success()?; + // If this was the result of a block request, we can't determined if the block peer did anything // wrong. If we already had both a block and blobs response processed, we should penalize the // blobs peer because they did not provide all blobs on the initial request. - } else if lookup.both_components_processed() { - lookup.penalize_blob_peer(cx); - - // Try it again if possible. - lookup.blob_request_state.state.on_processing_failure(); - Action::Retry - } else { - Action::Continue + if lookup.both_components_processed() { + if let Some(blob_peer) = lookup + .blob_request_state + .state + .on_post_process_validation_failure()? + { + cx.report_peer( + blob_peer, + PeerAction::MidToleranceError, + "sent_incomplete_blobs", + ); + } } + Action::Retry } BlockProcessingResult::Ignored => { // Beacon processor signalled to ignore the block processing result. // This implies that the cpu is overloaded. Drop the request. warn!( self.log, - "Single block processing was ignored, cpu might be overloaded"; - "action" => "dropping single block request" + "Lookup component processing ignored, cpu might be overloaded"; + "component" => ?R::response_type(), ); Action::Drop } BlockProcessingResult::Err(e) => { - let root = lookup.block_root(); - trace!(self.log, "Single block processing failed"; "block" => %root, "error" => %e); match e { BlockError::BeaconChainError(e) => { // Internal error - error!(self.log, "Beacon chain error processing single block"; "block_root" => %root, "error" => ?e); + error!(self.log, "Beacon chain error processing lookup component"; "block_root" => %block_root, "error" => ?e); Action::Drop } BlockError::ParentUnknown(block) => { - let slot = block.slot(); - let parent_root = block.parent_root(); - lookup.add_child_components(block.into()); - Action::ParentUnknown { parent_root, slot } + // Reverts the status of this request to `AwaitingProcessing` holding the + // downloaded data. A future call to `continue_requests` will re-submit it + // once there are no pending parent requests. + // Note: `BlockError::ParentUnknown` is only returned when processing + // blocks, not blobs. + request_state.revert_to_awaiting_processing()?; + Action::ParentUnknown { + parent_root: block.parent_root(), + } } ref e @ BlockError::ExecutionPayloadError(ref epe) if !epe.penalize_peer() => { // These errors indicate that the execution layer is offline @@ -814,35 +496,35 @@ impl BlockLookups { debug!( self.log, "Single block lookup failed. Execution layer is offline / unsynced / misconfigured"; - "root" => %root, + "block_root" => %block_root, "error" => ?e ); Action::Drop } - BlockError::AvailabilityCheck(e) => match e.category() { - AvailabilityCheckErrorCategory::Internal => { - warn!(self.log, "Internal availability check failure"; "root" => %root, "peer_id" => %peer_id, "error" => ?e); - lookup.block_request_state.state.on_download_failure(); - lookup.blob_request_state.state.on_download_failure(); - Action::Retry - } - AvailabilityCheckErrorCategory::Malicious => { - warn!(self.log, "Availability check failure"; "root" => %root, "peer_id" => %peer_id, "error" => ?e); - lookup.handle_availability_check_failure(cx); - Action::Retry - } - }, + BlockError::AvailabilityCheck(e) + if e.category() == AvailabilityCheckErrorCategory::Internal => + { + // There errors indicate internal problems and should not downscore the peer + warn!(self.log, "Internal availability check failure"; "block_root" => %block_root, "error" => ?e); + + // Here we choose *not* to call `on_processing_failure` because this could result in a bad + // lookup state transition. This error invalidates both blob and block requests, and we don't know the + // state of both requests. Blobs may have already successfullly processed for example. + // We opt to drop the lookup instead. + Action::Drop + } other => { - warn!(self.log, "Peer sent invalid block in single block lookup"; "root" => %root, "error" => ?other, "peer_id" => %peer_id); - if let Ok(block_peer) = lookup.block_request_state.state.processing_peer() { - cx.report_peer( - block_peer, - PeerAction::MidToleranceError, - "single_block_failure", - ); + debug!(self.log, "Invalid lookup component"; "block_root" => %block_root, "component" => ?R::response_type(), "error" => ?other); + let peer_id = request_state.on_processing_failure()?; + cx.report_peer( + peer_id, + PeerAction::MidToleranceError, + match R::response_type() { + ResponseType::Block => "lookup_block_processing_failure", + ResponseType::Blob => "lookup_blobs_processing_failure", + }, + ); - lookup.block_request_state.state.on_processing_failure(); - } Action::Retry } } @@ -851,466 +533,96 @@ impl BlockLookups { match action { Action::Retry => { - if let Err(e) = lookup.request_block_and_blobs(cx) { - warn!(self.log, "Single block lookup failed"; "block_root" => %block_root, "error" => ?e); - // Failed with too many retries, drop with noop - self.update_metrics(); - } else { - self.single_block_lookups.insert(target_id, lookup); - } + // Trigger download for all components in case `MissingComponents` failed the blob + // request. Also if blobs are `AwaitingProcessing` and need to be progressed + lookup.continue_requests(cx) } - Action::ParentUnknown { parent_root, slot } => { - // TODO: Consider including all peers from the lookup, claiming to know this block, not - // just the one that sent this specific block - self.search_parent(slot, block_root, parent_root, peer_id, cx); - self.single_block_lookups.insert(target_id, lookup); + Action::ParentUnknown { parent_root } => { + let peers = lookup.all_available_peers().cloned().collect::>(); + lookup.set_awaiting_parent(parent_root); + debug!(self.log, "Marking lookup as awaiting parent"; "lookup" => %block_root, "parent_root" => %parent_root); + self.search_parent_of_child(parent_root, block_root, &peers, cx); + Ok(LookupResult::Pending) } Action::Drop => { - // drop with noop - self.update_metrics(); + // Drop with noop + Err(LookupRequestError::Failed) } Action::Continue => { - self.single_block_lookups.insert(target_id, lookup); + // Drop this completed lookup only + Ok(LookupResult::Completed) } } } - pub fn parent_block_processed( - &mut self, - chain_hash: Hash256, - result: BlockProcessingResult, - cx: &mut SyncNetworkContext, - ) { - let index = self - .parent_lookups - .iter() - .enumerate() - .find(|(_, lookup)| lookup.chain_hash() == chain_hash) - .map(|(index, _)| index); - - let Some(mut parent_lookup) = index.map(|index| self.parent_lookups.remove(index)) else { - return debug!(self.log, "Process response for a parent lookup request that was not found"; "chain_hash" => %chain_hash); - }; + /// Makes progress on the immediate children of `block_root` + pub fn continue_child_lookups(&mut self, block_root: Hash256, cx: &mut SyncNetworkContext) { + let mut lookup_results = vec![]; // < need to buffer lookup results to not re-borrow &mut self - match &result { - BlockProcessingResult::Ok(status) => match status { - AvailabilityProcessingStatus::Imported(block_root) => { - debug!(self.log, "Parent block processing succeeded"; &parent_lookup, "block_root" => ?block_root) - } - AvailabilityProcessingStatus::MissingComponents(_, block_root) => { - debug!(self.log, "Parent missing parts, triggering single block lookup"; &parent_lookup,"block_root" => ?block_root) - } - }, - BlockProcessingResult::Err(e) => { - debug!(self.log, "Parent block processing failed"; &parent_lookup, "error" => %e) - } - BlockProcessingResult::Ignored => { - debug!( - self.log, - "Parent block processing job was ignored"; - "action" => "re-requesting block", - &parent_lookup - ); + for (id, lookup) in self.single_block_lookups.iter_mut() { + if lookup.awaiting_parent() == Some(block_root) { + lookup.resolve_awaiting_parent(); + debug!(self.log, "Continuing child lookup"; "parent_root" => %block_root, "block_root" => %lookup.block_root()); + let result = lookup.continue_requests(cx); + lookup_results.push((*id, result)); } } - match result { - BlockProcessingResult::Ok(AvailabilityProcessingStatus::MissingComponents( - _, - block_root, - )) => { - let expected_block_root = parent_lookup.current_parent_request.block_root(); - if block_root != expected_block_root { - warn!( - self.log, - "Parent block processing result/request root mismatch"; - "request" =>?expected_block_root, - "result" => ?block_root - ); - return; - } - - // We only send parent blocks + blobs for processing together. This means a - // `MissingComponents` response here indicates missing blobs. Therefore we always - // register a blob processing failure here. - parent_lookup - .current_parent_request - .blob_request_state - .state - .on_processing_failure(); - match parent_lookup - .current_parent_request - .request_block_and_blobs(cx) - { - Ok(()) => self.parent_lookups.push(parent_lookup), - Err(e) => self.handle_parent_request_error(&mut parent_lookup, cx, e.into()), - } - } - BlockProcessingResult::Err(BlockError::ParentUnknown(block)) => { - parent_lookup.add_unknown_parent_block(block); - self.request_parent(parent_lookup, cx); - } - BlockProcessingResult::Ok(AvailabilityProcessingStatus::Imported(_)) - | BlockProcessingResult::Err(BlockError::BlockIsAlreadyKnown(_)) => { - let (chain_hash, blocks, hashes, block_request) = - parent_lookup.parts_for_processing(); - - let blocks = self.add_child_block_to_chain(chain_hash, blocks, cx).into(); - - let process_id = ChainSegmentProcessId::ParentLookup(chain_hash); - - // Check if the beacon processor is available - let Some(beacon_processor) = cx.beacon_processor_if_enabled() else { - return trace!( - self.log, - "Dropping parent chain segment that was ready for processing."; - "chain_hash" => %chain_hash, - ); - }; - - match beacon_processor.send_chain_segment(process_id, blocks) { - Ok(_) => { - self.processing_parent_lookups - .insert(chain_hash, (hashes, block_request)); - } - Err(e) => { - error!( - self.log, - "Failed to send chain segment to processor"; - "error" => ?e - ); - } - } - } - ref e @ BlockProcessingResult::Err(BlockError::ExecutionPayloadError(ref epe)) - if !epe.penalize_peer() => - { - // These errors indicate that the execution layer is offline - // and failed to validate the execution payload. Do not downscore peer. - debug!( - self.log, - "Parent lookup failed. Execution layer is offline"; - "chain_hash" => %chain_hash, - "error" => ?e - ); - } - BlockProcessingResult::Err(outcome) => { - self.handle_parent_block_error(outcome, cx, parent_lookup); - } - BlockProcessingResult::Ignored => { - // Beacon processor signalled to ignore the block processing result. - // This implies that the cpu is overloaded. Drop the request. - warn!( - self.log, - "Parent block processing was ignored, cpu might be overloaded"; - "action" => "dropping parent request" - ); - } + for (id, result) in lookup_results { + self.on_lookup_result(id, result, "continue_child_lookups", cx); } - - metrics::set_gauge( - &metrics::SYNC_PARENT_BLOCK_LOOKUPS, - self.parent_lookups.len() as i64, - ); } - /// Find the child block that spawned the parent lookup request and add it to the chain - /// to send for processing. - fn add_child_block_to_chain( - &mut self, - chain_hash: Hash256, - mut blocks: VecDeque>, - cx: &mut SyncNetworkContext, - ) -> VecDeque> { - // Find the child block that spawned the parent lookup request and add it to the chain - // to send for processing. - if let Some(child_lookup_id) = self - .single_block_lookups - .iter() - .find_map(|(id, lookup)| (lookup.block_root() == chain_hash).then_some(*id)) - { - let Some(child_lookup) = self.single_block_lookups.get_mut(&child_lookup_id) else { - debug!(self.log, "Missing child for parent lookup request"; "child_root" => ?chain_hash); - return blocks; - }; - match child_lookup.get_cached_child_block() { - CachedChild::Ok(rpc_block) => { - // Insert this block at the front. This order is important because we later check - // for linear roots in `filter_chain_segment` - blocks.push_front(rpc_block); - } - CachedChild::DownloadIncomplete => { - trace!(self.log, "Parent lookup chain complete, awaiting child response"; "chain_hash" => ?chain_hash); - } - CachedChild::NotRequired => { - warn!(self.log, "Child not cached for parent lookup"; "chain_hash" => %chain_hash); - } - CachedChild::Err(e) => { - warn!( - self.log, - "Consistency error in child block triggering chain or parent lookups"; - "error" => ?e, - "chain_hash" => ?chain_hash - ); - child_lookup.handle_consistency_failure(cx); - if let Err(e) = child_lookup.request_block_and_blobs(cx) { - debug!(self.log, - "Failed to request block and blobs, dropping lookup"; - "error" => ?e - ); - self.single_block_lookups.remove(&child_lookup_id); - } - } - } - } else { - debug!(self.log, "Missing child for parent lookup request"; "child_root" => ?chain_hash); - }; - blocks - } - - /// Handle the peer scoring, retries, and logging related to a `BlockError` returned from - /// processing a block + blobs for a parent lookup. - fn handle_parent_block_error( - &mut self, - outcome: BlockError<::EthSpec>, - cx: &mut SyncNetworkContext, - mut parent_lookup: ParentLookup, - ) { - // We should always have a block peer. - let block_peer_id = match parent_lookup.block_processing_peer() { - Ok(peer_id) => peer_id, - Err(e) => { - warn!(self.log, "Parent lookup in bad state"; "chain_hash" => %parent_lookup.chain_hash(), "error" => e); - return; - } - }; + /// Drops `dropped_id` lookup and all its children recursively. Lookups awaiting a parent need + /// the parent to make progress to resolve, therefore we must drop them if the parent is + /// dropped. + pub fn drop_lookup_and_children(&mut self, dropped_id: SingleLookupId) { + if let Some(dropped_lookup) = self.single_block_lookups.remove(&dropped_id) { + debug!(self.log, "Dropping child lookup"; "id" => ?dropped_id, "block_root" => %dropped_lookup.block_root()); - // We may not have a blob peer, if there were no blobs required for this block. - let blob_peer_id = parent_lookup.blob_processing_peer().ok(); + let child_lookups = self + .single_block_lookups + .iter() + .filter(|(_, lookup)| lookup.awaiting_parent() == Some(dropped_lookup.block_root())) + .map(|(id, _)| *id) + .collect::>(); - // all else we consider the chain a failure and downvote the peer that sent - // us the last block - warn!( - self.log, "Invalid parent chain"; - "score_adjustment" => %PeerAction::MidToleranceError, - "outcome" => ?outcome, - "block_peer_id" => %block_peer_id, - ); - // This currently can be a host of errors. We permit this due to the partial - // ambiguity. - cx.report_peer( - block_peer_id, - PeerAction::MidToleranceError, - "parent_request_err", - ); - // Don't downscore the same peer twice - if let Some(blob_peer_id) = blob_peer_id { - if block_peer_id != blob_peer_id { - debug!( - self.log, "Additionally down-scoring blob peer"; - "score_adjustment" => %PeerAction::MidToleranceError, - "outcome" => ?outcome, - "blob_peer_id" => %blob_peer_id, - ); - cx.report_peer( - blob_peer_id, - PeerAction::MidToleranceError, - "parent_request_err", - ); + for id in child_lookups { + self.drop_lookup_and_children(id); } } - - // Try again if possible - parent_lookup.processing_failed(); - self.request_parent(parent_lookup, cx); } - pub fn parent_chain_processed( + /// Common handler a lookup request error, drop it and update metrics + fn on_lookup_result( &mut self, - chain_hash: Hash256, - result: BatchProcessResult, + id: SingleLookupId, + result: Result, + source: &str, cx: &mut SyncNetworkContext, ) { - let Some((_hashes, request)) = self.processing_parent_lookups.remove(&chain_hash) else { - return debug!(self.log, "Chain process response for a parent lookup request that was not found"; "chain_hash" => %chain_hash, "result" => ?result); - }; - - debug!(self.log, "Parent chain processed"; "chain_hash" => %chain_hash, "result" => ?result); match result { - BatchProcessResult::Success { .. } => { - let Some(id) = self - .single_block_lookups - .iter() - .find_map(|(id, req)| (req.block_root() == chain_hash).then_some(*id)) - else { - warn!(self.log, "No id found for single block lookup"; "chain_hash" => %chain_hash); - return; - }; - - let Some(lookup) = self.single_block_lookups.get_mut(&id) else { - warn!(self.log, "No id found for single block lookup"; "chain_hash" => %chain_hash); - return; - }; - - match lookup.get_cached_child_block() { - CachedChild::Ok(rpc_block) => { - // This is the correct block, send it for processing - if self - .send_block_for_processing( - chain_hash, - rpc_block, - timestamp_now(), - BlockProcessType::SingleBlock { id }, - cx, - ) - .is_err() - { - // Remove to avoid inconsistencies - self.single_block_lookups.remove(&id); - } - } - CachedChild::DownloadIncomplete => { - trace!(self.log, "Parent chain complete, awaiting child response"; "chain_hash" => %chain_hash); - } - CachedChild::NotRequired => { - warn!(self.log, "Child not cached for parent lookup"; "chain_hash" => %chain_hash); - } - CachedChild::Err(e) => { - warn!( - self.log, - "Consistency error in child block triggering parent lookup"; - "chain_hash" => %chain_hash, - "error" => ?e - ); - lookup.handle_consistency_failure(cx); - if let Err(e) = lookup.request_block_and_blobs(cx) { - debug!(self.log, - "Failed to request block and blobs, dropping lookup"; - "error" => ?e - ); - self.single_block_lookups.remove(&id); - } - } - } - } - BatchProcessResult::FaultyFailure { - imported_blocks: _, - penalty, - } => { - self.failed_chains.insert(chain_hash); - for peer_source in request.all_used_peers() { - cx.report_peer(*peer_source, penalty, "parent_chain_failure") + Ok(LookupResult::Pending) => {} // no action + Ok(LookupResult::Completed) => { + if let Some(lookup) = self.single_block_lookups.remove(&id) { + debug!(self.log, "Dropping completed lookup"; "block" => %lookup.block_root()); + metrics::inc_counter(&metrics::SYNC_LOOKUP_COMPLETED); + // Block imported, continue the requests of pending child blocks + self.continue_child_lookups(lookup.block_root(), cx); + self.update_metrics(); } } - BatchProcessResult::NonFaultyFailure => { - // We might request this chain again if there is need but otherwise, don't try again + Err(error) => { + debug!(self.log, "Dropping lookup on request error"; "id" => id, "source" => source, "error" => ?error); + metrics::inc_counter_vec(&metrics::SYNC_LOOKUP_DROPPED, &[error.into()]); + self.drop_lookup_and_children(id); + self.update_metrics(); } } - - metrics::set_gauge( - &metrics::SYNC_PARENT_BLOCK_LOOKUPS, - self.parent_lookups.len() as i64, - ); } /* Helper functions */ - fn send_block_for_processing( - &self, - block_root: Hash256, - block: RpcBlock, - duration: Duration, - process_type: BlockProcessType, - cx: &SyncNetworkContext, - ) -> Result<(), LookupRequestError> { - match cx.beacon_processor_if_enabled() { - Some(beacon_processor) => { - debug!(self.log, "Sending block for processing"; "block" => ?block_root, "process" => ?process_type); - if let Err(e) = beacon_processor.send_rpc_beacon_block( - block_root, - block, - duration, - process_type, - ) { - error!( - self.log, - "Failed to send sync block to processor"; - "error" => ?e - ); - Err(LookupRequestError::SendFailed( - "beacon processor send failure", - )) - } else { - Ok(()) - } - } - None => { - trace!(self.log, "Dropping block ready for processing. Beacon processor not available"; "block" => %block_root); - Err(LookupRequestError::SendFailed( - "beacon processor unavailable", - )) - } - } - } - - fn send_blobs_for_processing( - &self, - block_root: Hash256, - blobs: FixedBlobSidecarList, - duration: Duration, - process_type: BlockProcessType, - cx: &SyncNetworkContext, - ) -> Result<(), LookupRequestError> { - match cx.beacon_processor_if_enabled() { - Some(beacon_processor) => { - trace!(self.log, "Sending blobs for processing"; "block" => ?block_root, "process_type" => ?process_type); - if let Err(e) = - beacon_processor.send_rpc_blobs(block_root, blobs, duration, process_type) - { - error!( - self.log, - "Failed to send sync blobs to processor"; - "error" => ?e - ); - Err(LookupRequestError::SendFailed( - "beacon processor send failure", - )) - } else { - Ok(()) - } - } - None => { - trace!(self.log, "Dropping blobs ready for processing. Beacon processor not available"; "block_root" => %block_root); - Err(LookupRequestError::SendFailed( - "beacon processor unavailable", - )) - } - } - } - - /// Attempts to request the next unknown parent. This method handles peer scoring and dropping - /// the lookup in the event of failure. - fn request_parent( - &mut self, - mut parent_lookup: ParentLookup, - cx: &mut SyncNetworkContext, - ) { - let response = parent_lookup.request_parent(cx); - - match response { - Err(e) => { - self.handle_parent_request_error(&mut parent_lookup, cx, e); - } - Ok(_) => self.parent_lookups.push(parent_lookup), - } - - // We remove and add back again requests so we want this updated regardless of outcome. - metrics::set_gauge( - &metrics::SYNC_PARENT_BLOCK_LOOKUPS, - self.parent_lookups.len() as i64, - ); - } - /// Drops all the single block requests and returns how many requests were dropped. pub fn drop_single_block_requests(&mut self) -> usize { let requests_to_drop = self.single_block_lookups.len(); @@ -1318,34 +630,10 @@ impl BlockLookups { requests_to_drop } - /// Drops all the parent chain requests and returns how many requests were dropped. - pub fn drop_parent_chain_requests(&mut self) -> usize { - self.parent_lookups.drain(..).len() - } - - pub fn downscore_on_rpc_error( - &self, - peer_id: &PeerId, - error: &LookupVerifyError, - cx: &SyncNetworkContext, - ) { - // Note: logging the report event here with the full error display. The log inside - // `report_peer` only includes a smaller string, like "invalid_data" - let error_str: &'static str = error.into(); - - debug!(self.log, "reporting peer for sync lookup error"; "error" => error_str); - cx.report_peer(*peer_id, PeerAction::LowToleranceError, error_str); - } - pub fn update_metrics(&self) { metrics::set_gauge( &metrics::SYNC_SINGLE_BLOCK_LOOKUPS, self.single_block_lookups.len() as i64, ); - - metrics::set_gauge( - &metrics::SYNC_PARENT_BLOCK_LOOKUPS, - self.parent_lookups.len() as i64, - ); } } diff --git a/beacon_node/network/src/sync/block_lookups/parent_chain.rs b/beacon_node/network/src/sync/block_lookups/parent_chain.rs new file mode 100644 index 00000000000..55f2cfe1292 --- /dev/null +++ b/beacon_node/network/src/sync/block_lookups/parent_chain.rs @@ -0,0 +1,198 @@ +use super::single_block_lookup::SingleBlockLookup; +use beacon_chain::BeaconChainTypes; +use std::collections::{HashMap, HashSet}; +use types::Hash256; + +/// Summary of a lookup of which we may not know it's parent_root yet +pub(crate) struct Node { + block_root: Hash256, + parent_root: Option, +} + +impl From<&SingleBlockLookup> for Node { + fn from(value: &SingleBlockLookup) -> Self { + Self { + block_root: value.block_root(), + parent_root: value.awaiting_parent(), + } + } +} + +/// Wrapper around a chain of block roots that have a least one element (tip) +pub(crate) struct NodeChain { + // Parent chain blocks in descending slot order + pub(crate) chain: Vec, + pub(crate) tip: Hash256, +} + +impl NodeChain { + /// Returns the block_root of the oldest ancestor (min slot) of this chain + pub(crate) fn ancestor(&self) -> Hash256 { + self.chain.last().copied().unwrap_or(self.tip) + } + pub(crate) fn len(&self) -> usize { + self.chain.len() + } +} + +/// Given a set of nodes that reference each other, returns a list of chains with unique tips that +/// contain at least two elements. In descending slot order (tip first). +pub(crate) fn compute_parent_chains(nodes: &[Node]) -> Vec { + let mut child_to_parent = HashMap::new(); + let mut parent_to_child = HashMap::>::new(); + for node in nodes { + child_to_parent.insert(node.block_root, node.parent_root); + if let Some(parent_root) = node.parent_root { + parent_to_child + .entry(parent_root) + .or_default() + .push(node.block_root); + } + } + + let mut parent_chains = vec![]; + + // Iterate blocks with no children + for tip in nodes { + let mut block_root = tip.block_root; + if parent_to_child.get(&block_root).is_none() { + let mut chain = vec![]; + + // Resolve chain of blocks + while let Some(parent_root) = child_to_parent.get(&block_root) { + // block_root is a known block that may or may not have a parent root + chain.push(block_root); + if let Some(parent_root) = parent_root { + block_root = *parent_root; + } else { + break; + } + } + + if chain.len() > 1 { + parent_chains.push(NodeChain { + chain, + tip: tip.block_root, + }); + } + } + } + + parent_chains +} + +/// Given a list of node chains, find the oldest node of a specific chain that is not contained in +/// any other chain. +pub(crate) fn find_oldest_fork_ancestor( + parent_chains: Vec, + chain_idx: usize, +) -> Result { + let mut other_blocks = HashSet::new(); + + // Register blocks from other chains + for (i, parent_chain) in parent_chains.iter().enumerate() { + if i != chain_idx { + for block in &parent_chain.chain { + other_blocks.insert(block); + } + } + } + + // Should never happen + let parent_chain = parent_chains + .get(chain_idx) + .ok_or("chain_idx out of bounds")?; + // Find the first block in the target parent chain that is not in other parent chains + // Iterate in ascending slot order + for block in parent_chain.chain.iter().rev() { + if !other_blocks.contains(block) { + return Ok(*block); + } + } + + // No match means that the chain is fully contained within another chain. This should never + // happen, but if that was the case just return the tip + Ok(parent_chain.tip) +} + +#[cfg(test)] +mod tests { + use super::{compute_parent_chains, find_oldest_fork_ancestor, Node}; + use types::Hash256; + + fn h(n: u64) -> Hash256 { + Hash256::from_low_u64_be(n) + } + + fn n(block: u64) -> Node { + Node { + block_root: h(block), + parent_root: None, + } + } + + fn np(parent: u64, block: u64) -> Node { + Node { + block_root: h(block), + parent_root: Some(h(parent)), + } + } + + fn compute_parent_chains_test(nodes: &[Node], expected_chain: Vec>) { + assert_eq!( + compute_parent_chains(nodes) + .iter() + .map(|c| c.chain.clone()) + .collect::>(), + expected_chain + ); + } + + fn find_oldest_fork_ancestor_test(nodes: &[Node], expected: Hash256) { + let chains = compute_parent_chains(nodes); + println!( + "chains {:?}", + chains.iter().map(|c| &c.chain).collect::>() + ); + assert_eq!(find_oldest_fork_ancestor(chains, 0).unwrap(), expected); + } + + #[test] + fn compute_parent_chains_empty_case() { + compute_parent_chains_test(&[], vec![]); + } + + #[test] + fn compute_parent_chains_single_branch() { + compute_parent_chains_test(&[n(0), np(0, 1), np(1, 2)], vec![vec![h(2), h(1), h(0)]]); + } + + #[test] + fn compute_parent_chains_single_branch_with_solo() { + compute_parent_chains_test( + &[n(0), np(0, 1), np(1, 2), np(3, 4)], + vec![vec![h(2), h(1), h(0)]], + ); + } + + #[test] + fn compute_parent_chains_two_forking_branches() { + compute_parent_chains_test( + &[n(0), np(0, 1), np(1, 2), np(1, 3)], + vec![vec![h(2), h(1), h(0)], vec![h(3), h(1), h(0)]], + ); + } + + #[test] + fn compute_parent_chains_two_independent_branches() { + compute_parent_chains_test( + &[n(0), np(0, 1), np(1, 2), n(3), np(3, 4)], + vec![vec![h(2), h(1), h(0)], vec![h(4), h(3)]], + ); + } + + #[test] + fn find_oldest_fork_ancestor_simple_case() { + find_oldest_fork_ancestor_test(&[n(0), np(0, 1), np(1, 2), np(0, 3)], h(1)) + } +} diff --git a/beacon_node/network/src/sync/block_lookups/parent_lookup.rs b/beacon_node/network/src/sync/block_lookups/parent_lookup.rs deleted file mode 100644 index 11eb908953f..00000000000 --- a/beacon_node/network/src/sync/block_lookups/parent_lookup.rs +++ /dev/null @@ -1,227 +0,0 @@ -use super::common::LookupType; -use super::single_block_lookup::{LookupRequestError, SingleBlockLookup}; -use super::{DownloadedBlock, PeerId}; -use crate::sync::{manager::SLOT_IMPORT_TOLERANCE, network_context::SyncNetworkContext}; -use beacon_chain::block_verification_types::AsBlock; -use beacon_chain::block_verification_types::RpcBlock; -use beacon_chain::data_availability_checker::{ChildComponents, DataAvailabilityChecker}; -use beacon_chain::BeaconChainTypes; -use std::collections::VecDeque; -use std::sync::Arc; -use store::Hash256; - -/// How many attempts we try to find a parent of a block before we give up trying. -pub(crate) const PARENT_FAIL_TOLERANCE: u8 = 5; -/// The maximum depth we will search for a parent block. In principle we should have sync'd any -/// canonical chain to its head once the peer connects. A chain should not appear where it's depth -/// is further back than the most recent head slot. -pub(crate) const PARENT_DEPTH_TOLERANCE: usize = SLOT_IMPORT_TOLERANCE * 2; - -/// Maintains a sequential list of parents to lookup and the lookup's current state. -pub(crate) struct ParentLookup { - /// The root of the block triggering this parent request. - chain_hash: Hash256, - /// The blocks that have currently been downloaded. - downloaded_blocks: Vec>, - /// Request of the last parent. - pub current_parent_request: SingleBlockLookup, -} - -#[derive(Debug, PartialEq, Eq)] -pub(crate) enum RequestError { - SendFailed(&'static str), - ChainTooLong, - /// We witnessed too many failures trying to complete this parent lookup. - TooManyAttempts { - /// We received more failures trying to process the blocks than downloading them - /// from peers. - cannot_process: bool, - }, - NoPeers, - BadState(String), -} - -impl ParentLookup { - pub fn new( - block_root: Hash256, - parent_root: Hash256, - peer_id: PeerId, - da_checker: Arc>, - cx: &mut SyncNetworkContext, - ) -> Self { - let current_parent_request = SingleBlockLookup::new( - parent_root, - Some(ChildComponents::empty(block_root)), - &[peer_id], - da_checker, - cx.next_id(), - LookupType::Parent, - ); - - Self { - chain_hash: block_root, - downloaded_blocks: vec![], - current_parent_request, - } - } - - pub fn contains_block(&self, block_root: &Hash256) -> bool { - self.downloaded_blocks - .iter() - .any(|(root, _d_block)| root == block_root) - } - - pub fn is_for_block(&self, block_root: Hash256) -> bool { - self.current_parent_request.is_for_block(block_root) - } - - /// Attempts to request the next unknown parent. If the request fails, it should be removed. - pub fn request_parent(&mut self, cx: &mut SyncNetworkContext) -> Result<(), RequestError> { - // check to make sure this request hasn't failed - if self.downloaded_blocks.len() + 1 >= PARENT_DEPTH_TOLERANCE { - return Err(RequestError::ChainTooLong); - } - - self.current_parent_request - .request_block_and_blobs(cx) - .map_err(Into::into) - } - - pub fn check_peer_disconnected(&mut self, peer_id: &PeerId) -> Result<(), ()> { - self.current_parent_request - .block_request_state - .state - .check_peer_disconnected(peer_id) - .and_then(|()| { - self.current_parent_request - .blob_request_state - .state - .check_peer_disconnected(peer_id) - }) - } - - pub fn add_unknown_parent_block(&mut self, block: RpcBlock) { - let next_parent = block.parent_root(); - // Cache the block. - let current_root = self.current_parent_request.block_root(); - self.downloaded_blocks.push((current_root, block)); - - // Update the parent request. - self.current_parent_request - .update_requested_parent_block(next_parent) - } - - pub fn block_processing_peer(&self) -> Result { - self.current_parent_request - .block_request_state - .state - .processing_peer() - } - - pub fn blob_processing_peer(&self) -> Result { - self.current_parent_request - .blob_request_state - .state - .processing_peer() - } - - /// Consumes the parent request and destructures it into it's parts. - #[allow(clippy::type_complexity)] - pub fn parts_for_processing( - self, - ) -> ( - Hash256, - VecDeque>, - Vec, - SingleBlockLookup, - ) { - let ParentLookup { - chain_hash, - downloaded_blocks, - current_parent_request, - } = self; - let block_count = downloaded_blocks.len(); - let mut blocks = VecDeque::with_capacity(block_count); - let mut hashes = Vec::with_capacity(block_count); - for (hash, block) in downloaded_blocks.into_iter() { - blocks.push_back(block); - hashes.push(hash); - } - (chain_hash, blocks, hashes, current_parent_request) - } - - /// Get the parent lookup's chain hash. - pub fn chain_hash(&self) -> Hash256 { - self.chain_hash - } - - pub fn processing_failed(&mut self) { - self.current_parent_request - .block_request_state - .state - .on_processing_failure(); - self.current_parent_request - .blob_request_state - .state - .on_processing_failure(); - if let Some(components) = self.current_parent_request.child_components.as_mut() { - components.downloaded_block = None; - components.downloaded_blobs = <_>::default(); - } - } - - pub fn add_peer(&mut self, peer: PeerId) { - self.current_parent_request.add_peer(peer) - } - - /// Adds a list of peers to the parent request. - pub fn add_peers(&mut self, peers: &[PeerId]) { - self.current_parent_request.add_peers(peers) - } - - pub fn all_used_peers(&self) -> impl Iterator + '_ { - self.current_parent_request.all_used_peers() - } -} - -impl From for RequestError { - fn from(e: LookupRequestError) -> Self { - use LookupRequestError as E; - match e { - E::TooManyAttempts { cannot_process } => { - RequestError::TooManyAttempts { cannot_process } - } - E::NoPeers => RequestError::NoPeers, - E::SendFailed(msg) => RequestError::SendFailed(msg), - E::BadState(msg) => RequestError::BadState(msg), - } - } -} - -impl slog::KV for ParentLookup { - fn serialize( - &self, - record: &slog::Record, - serializer: &mut dyn slog::Serializer, - ) -> slog::Result { - serializer.emit_arguments("chain_hash", &format_args!("{}", self.chain_hash))?; - slog::Value::serialize(&self.current_parent_request, record, "parent", serializer)?; - serializer.emit_usize("downloaded_blocks", self.downloaded_blocks.len())?; - slog::Result::Ok(()) - } -} - -impl RequestError { - pub fn as_static(&self) -> &'static str { - match self { - RequestError::SendFailed(e) => e, - RequestError::ChainTooLong => "chain_too_long", - RequestError::TooManyAttempts { cannot_process } if *cannot_process => { - "too_many_processing_attempts" - } - RequestError::TooManyAttempts { cannot_process: _ } => "too_many_downloading_attempts", - RequestError::NoPeers => "no_peers", - RequestError::BadState(..) => "bad_state", - } - } -} diff --git a/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs b/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs index 077af7c3d19..b642ec8e5b2 100644 --- a/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs +++ b/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs @@ -1,25 +1,28 @@ -use super::common::LookupType; -use super::PeerId; +use super::common::{AwaitingParent, BlockIsProcessed}; +use super::{BlockComponent, PeerId}; use crate::sync::block_lookups::common::RequestState; use crate::sync::block_lookups::Id; use crate::sync::network_context::SyncNetworkContext; -use beacon_chain::block_verification_types::RpcBlock; -use beacon_chain::data_availability_checker::ChildComponents; -use beacon_chain::data_availability_checker::{ - AvailabilityCheckError, DataAvailabilityChecker, MissingBlobs, -}; use beacon_chain::BeaconChainTypes; use itertools::Itertools; -use lighthouse_network::PeerAction; use rand::seq::IteratorRandom; -use slog::{debug, Logger}; use std::collections::HashSet; use std::fmt::Debug; use std::sync::Arc; +use std::time::Duration; use store::Hash256; use strum::IntoStaticStr; use types::blob_sidecar::FixedBlobSidecarList; -use types::EthSpec; +use types::{EthSpec, SignedBeaconBlock}; + +// Dedicated enum for LookupResult to force its usage +#[must_use = "LookupResult must be handled with on_lookup_result"] +pub enum LookupResult { + /// Lookup completed successfully + Completed, + /// Lookup is expecting some future event from the network + Pending, +} #[derive(Debug, PartialEq, Eq, IntoStaticStr)] pub enum LookupRequestError { @@ -28,45 +31,78 @@ pub enum LookupRequestError { /// The failed attempts were primarily due to processing failures. cannot_process: bool, }, + /// No peers left to serve this lookup NoPeers, + /// Error sending event to network or beacon processor SendFailed(&'static str), + /// Inconsistent lookup request state BadState(String), + /// Lookup failed for some other reason and should be dropped + Failed, + /// Attempted to retrieve a not known lookup id + UnknownLookup, } pub struct SingleBlockLookup { pub id: Id, - pub lookup_type: LookupType, - pub block_request_state: BlockRequestState, + pub block_request_state: BlockRequestState, pub blob_request_state: BlobRequestState, - pub da_checker: Arc>, - /// Only necessary for requests triggered by an `UnknownBlockParent` or `UnknownBlockParent` - /// because any blocks or blobs without parents won't hit the data availability cache. - pub child_components: Option>, + block_root: Hash256, + awaiting_parent: Option, } impl SingleBlockLookup { pub fn new( requested_block_root: Hash256, - child_components: Option>, peers: &[PeerId], - da_checker: Arc>, id: Id, - lookup_type: LookupType, + awaiting_parent: Option, ) -> Self { - let is_deneb = da_checker.is_deneb(); Self { id, - lookup_type, block_request_state: BlockRequestState::new(requested_block_root, peers), - blob_request_state: BlobRequestState::new(requested_block_root, peers, is_deneb), - da_checker, - child_components, + blob_request_state: BlobRequestState::new(requested_block_root, peers), + block_root: requested_block_root, + awaiting_parent, } } /// Get the block root that is being requested. pub fn block_root(&self) -> Hash256 { - self.block_request_state.requested_block_root + self.block_root + } + + pub fn awaiting_parent(&self) -> Option { + self.awaiting_parent + } + + /// Mark this lookup as awaiting a parent lookup from being processed. Meanwhile don't send + /// components for processing. + pub fn set_awaiting_parent(&mut self, parent_root: Hash256) { + self.awaiting_parent = Some(parent_root) + } + + /// Mark this lookup as no longer awaiting a parent lookup. Components can be sent for + /// processing. + pub fn resolve_awaiting_parent(&mut self) { + self.awaiting_parent = None; + } + + /// Maybe insert a verified response into this lookup. Returns true if imported + pub fn add_child_components(&mut self, block_component: BlockComponent) -> bool { + match block_component { + BlockComponent::Block(block) => self + .block_request_state + .state + .insert_verified_response(block), + BlockComponent::Blob(_) => { + // For now ignore single blobs, as the blob request state assumes all blobs are + // attributed to the same peer = the peer serving the remaining blobs. Ignoring this + // block component has a minor effect, causing the node to re-request this blob + // once the parent chain is successfully resolved + false + } + } } /// Check the block root matches the requested block root. @@ -74,16 +110,6 @@ impl SingleBlockLookup { self.block_root() == block_root } - /// Update the requested block, this should only be used in a chain of parent lookups to request - /// the next parent. - pub fn update_requested_parent_block(&mut self, block_root: Hash256) { - self.block_request_state.requested_block_root = block_root; - self.blob_request_state.block_root = block_root; - self.block_request_state.state.state = State::AwaitingDownload; - self.blob_request_state.state.state = State::AwaitingDownload; - self.child_components = Some(ChildComponents::empty(block_root)); - } - /// Get all unique used peers across block and blob requests. pub fn all_used_peers(&self) -> impl Iterator + '_ { self.block_request_state @@ -93,87 +119,57 @@ impl SingleBlockLookup { .unique() } - /// Send the necessary requests for blocks and/or blobs. This will check whether we have - /// downloaded the block and/or blobs already and will not send requests if so. It will also - /// inspect the request state or blocks and blobs to ensure we are not already processing or - /// downloading the block and/or blobs. - pub fn request_block_and_blobs( - &mut self, - cx: &mut SyncNetworkContext, - ) -> Result<(), LookupRequestError> { - let block_already_downloaded = self.block_already_downloaded(); - let blobs_already_downloaded = self.blobs_already_downloaded(); - - if !block_already_downloaded { - self.block_request_state - .build_request_and_send(self.id, self.lookup_type, cx)?; - } - if !blobs_already_downloaded { - self.blob_request_state - .build_request_and_send(self.id, self.lookup_type, cx)?; - } - Ok(()) - } - - /// Returns a `CachedChild`, which is a wrapper around a `RpcBlock` that is either: - /// - /// 1. `NotRequired`: there is no child caching required for this lookup. - /// 2. `DownloadIncomplete`: Child caching is required, but all components are not yet downloaded. - /// 3. `Ok`: The child is required and we have downloaded it. - /// 4. `Err`: The child is required, but has failed consistency checks. - pub fn get_cached_child_block(&self) -> CachedChild { - if let Some(components) = self.child_components.as_ref() { - let Some(block) = components.downloaded_block.as_ref() else { - return CachedChild::DownloadIncomplete; - }; - - if !self.missing_blob_ids().is_empty() { - return CachedChild::DownloadIncomplete; - } - - match RpcBlock::new_from_fixed( - self.block_request_state.requested_block_root, - block.clone(), - components.downloaded_blobs.clone(), - ) { - Ok(rpc_block) => CachedChild::Ok(rpc_block), - Err(e) => CachedChild::Err(e), - } - } else { - CachedChild::NotRequired - } + /// Get all unique available peers across block and blob requests. + pub fn all_available_peers(&self) -> impl Iterator + '_ { + self.block_request_state + .state + .get_available_peers() + .chain(self.blob_request_state.state.get_available_peers()) + .unique() } - /// Accepts a verified response, and adds it to the child components if required. This method - /// returns a `CachedChild` which provides a completed block + blob response if all components have been - /// received, or information about whether the child is required and if it has been downloaded. - pub fn add_response>( + /// Makes progress on all requests of this lookup. Any error is not recoverable and must result + /// in dropping the lookup. May mark the lookup as completed. + pub fn continue_requests( &mut self, - verified_response: R::VerifiedResponseType, - ) -> CachedChild { - if let Some(child_components) = self.child_components.as_mut() { - R::add_to_child_components(verified_response, child_components); - self.get_cached_child_block() + cx: &mut SyncNetworkContext, + ) -> Result { + // TODO: Check what's necessary to download, specially for blobs + self.continue_request::>(cx)?; + self.continue_request::>(cx)?; + + // If all components of this lookup are already processed, there will be no future events + // that can make progress so it must be dropped. Consider the lookup completed. + // This case can happen if we receive the components from gossip during a retry. + if self.block_request_state.state.is_processed() + && self.blob_request_state.state.is_processed() + { + Ok(LookupResult::Completed) } else { - CachedChild::NotRequired + Ok(LookupResult::Pending) } } - /// Add a child component to the lookup request. Merges with any existing child components. - pub fn add_child_components(&mut self, components: ChildComponents) { - if let Some(ref mut existing_components) = self.child_components { - let ChildComponents { - block_root: _, - downloaded_block, - downloaded_blobs, - } = components; - if let Some(block) = downloaded_block { - existing_components.merge_block(block); - } - existing_components.merge_blobs(downloaded_blobs); - } else { - self.child_components = Some(components); - } + /// Wrapper around `RequestState::continue_request` to inject lookup data + pub fn continue_request>( + &mut self, + cx: &mut SyncNetworkContext, + ) -> Result<(), LookupRequestError> { + let id = self.id; + let awaiting_parent = self.awaiting_parent.is_some(); + let downloaded_block_expected_blobs = self + .block_request_state + .state + .peek_downloaded_data() + .map(|block| block.num_expected_blobs()); + let block_is_processed = self.block_request_state.state.is_processed(); + R::request_state_mut(self).continue_request( + id, + AwaitingParent(awaiting_parent), + downloaded_block_expected_blobs, + BlockIsProcessed(block_is_processed), + cx, + ) } /// Add all given peers to both block and blob request states. @@ -189,12 +185,6 @@ impl SingleBlockLookup { } } - /// Returns true if the block has already been downloaded. - pub fn both_components_downloaded(&self) -> bool { - self.block_request_state.state.is_downloaded() - && self.blob_request_state.state.is_downloaded() - } - /// Returns true if the block has already been downloaded. pub fn both_components_processed(&self) -> bool { self.block_request_state.state.is_processed() @@ -204,136 +194,43 @@ impl SingleBlockLookup { /// Checks both the block and blob request states to see if the peer is disconnected. /// /// Returns true if the lookup should be dropped. - pub fn should_drop_lookup_on_disconnected_peer( - &mut self, - peer_id: &PeerId, - cx: &mut SyncNetworkContext, - log: &Logger, - ) -> bool { - let block_root = self.block_root(); - let block_peer_disconnected = self - .block_request_state - .state - .check_peer_disconnected(peer_id) - .is_err(); - let blob_peer_disconnected = self - .blob_request_state - .state - .check_peer_disconnected(peer_id) - .is_err(); - - if block_peer_disconnected || blob_peer_disconnected { - if let Err(e) = self.request_block_and_blobs(cx) { - debug!(log, "Single lookup failed on peer disconnection"; "block_root" => ?block_root, "error" => ?e); - return true; - } - } - false - } + pub fn should_drop_lookup_on_disconnected_peer(&mut self, peer_id: &PeerId) -> bool { + self.block_request_state.state.remove_peer(peer_id); + self.blob_request_state.state.remove_peer(peer_id); - /// Returns `true` if the block has already been downloaded. - pub(crate) fn block_already_downloaded(&self) -> bool { - if let Some(components) = self.child_components.as_ref() { - components.downloaded_block.is_some() - } else { - self.da_checker.has_block(&self.block_root()) - } - } - - /// Updates the `requested_ids` field of the `BlockRequestState` with the most recent picture - /// of which blobs still need to be requested. Returns `true` if there are no more blobs to - /// request. - pub(crate) fn blobs_already_downloaded(&mut self) -> bool { - if matches!(self.blob_request_state.state.state, State::AwaitingDownload) { - self.update_blobs_request(); - } - self.blob_request_state.requested_ids.is_empty() - } - - /// Updates this request with the most recent picture of which blobs still need to be requested. - pub fn update_blobs_request(&mut self) { - self.blob_request_state.requested_ids = self.missing_blob_ids(); - } - - /// If `child_components` is `Some`, we know block components won't hit the data - /// availability cache, so we don't check its processing cache unless `child_components` - /// is `None`. - pub(crate) fn missing_blob_ids(&self) -> MissingBlobs { - let block_root = self.block_root(); - if let Some(components) = self.child_components.as_ref() { - self.da_checker.get_missing_blob_ids( - block_root, - components.downloaded_block.as_ref().map(|b| b.as_ref()), - &components.downloaded_blobs, - ) - } else { - self.da_checker.get_missing_blob_ids_with(block_root) + if self.all_available_peers().count() == 0 { + return true; } - } - - /// Penalizes a blob peer if it should have blobs but didn't return them to us. - pub fn penalize_blob_peer(&mut self, cx: &SyncNetworkContext) { - if let Ok(blob_peer) = self.blob_request_state.state.processing_peer() { - cx.report_peer( - blob_peer, - PeerAction::MidToleranceError, - "single_blob_failure", - ); - } - } - /// This failure occurs on download, so register a failure downloading, penalize the peer - /// and clear the blob cache. - pub fn handle_consistency_failure(&mut self, cx: &SyncNetworkContext) { - self.penalize_blob_peer(cx); - if let Some(cached_child) = self.child_components.as_mut() { - cached_child.clear_blobs(); - } - self.blob_request_state.state.on_download_failure() - } - - /// This failure occurs after processing, so register a failure processing, penalize the peer - /// and clear the blob cache. - pub fn handle_availability_check_failure(&mut self, cx: &SyncNetworkContext) { - self.penalize_blob_peer(cx); - if let Some(cached_child) = self.child_components.as_mut() { - cached_child.clear_blobs(); - } - self.blob_request_state.state.on_processing_failure() + // Note: if the peer disconnected happens to have an on-going request associated with this + // lookup we will receive an RPCError and the lookup will fail. No need to manually retry + // now. + false } } /// The state of the blob request component of a `SingleBlockLookup`. pub struct BlobRequestState { - /// The latest picture of which blobs still need to be requested. This includes information - /// from both block/blobs downloaded in the network layer and any blocks/blobs that exist in - /// the data availability checker. - pub requested_ids: MissingBlobs, pub block_root: Hash256, - /// Where we store blobs until we receive the stream terminator. - pub blob_download_queue: FixedBlobSidecarList, - pub state: SingleLookupRequestState, + pub state: SingleLookupRequestState>, } impl BlobRequestState { - pub fn new(block_root: Hash256, peer_source: &[PeerId], is_deneb: bool) -> Self { - let default_ids = MissingBlobs::new_without_block(block_root, is_deneb); + pub fn new(block_root: Hash256, peer_source: &[PeerId]) -> Self { Self { block_root, - requested_ids: default_ids, - blob_download_queue: <_>::default(), state: SingleLookupRequestState::new(peer_source), } } } /// The state of the block request component of a `SingleBlockLookup`. -pub struct BlockRequestState { +pub struct BlockRequestState { pub requested_block_root: Hash256, - pub state: SingleLookupRequestState, + pub state: SingleLookupRequestState>>, } -impl BlockRequestState { +impl BlockRequestState { pub fn new(block_root: Hash256, peers: &[PeerId]) -> Self { Self { requested_block_root: block_root, @@ -342,36 +239,28 @@ impl BlockRequestState { } } -/// This is the status of cached components for a lookup if they are required. It provides information -/// about whether we should send a responses immediately for processing, whether we require more -/// responses, or whether all cached components have been received and the reconstructed block -/// should be sent for processing. -pub enum CachedChild { - /// All child components have been received, this is the reconstructed block, including all. - /// It has been checked for consistency between blobs and block, but no consensus checks have - /// been performed and no kzg verification has been performed. - Ok(RpcBlock), - /// All child components have not yet been received. - DownloadIncomplete, - /// Child components should not be cached, send this directly for processing. - NotRequired, - /// There was an error during consistency checks between block and blobs. - Err(AvailabilityCheckError), +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct DownloadResult { + pub value: T, + pub block_root: Hash256, + pub seen_timestamp: Duration, + pub peer_id: PeerId, } #[derive(Debug, PartialEq, Eq)] -pub enum State { +pub enum State { AwaitingDownload, - Downloading { peer_id: PeerId }, - Processing { peer_id: PeerId }, - Processed { peer_id: PeerId }, + Downloading, + AwaitingProcess(DownloadResult), + Processing(DownloadResult), + Processed(Option), } /// Object representing the state of a single block or blob lookup request. #[derive(PartialEq, Eq, Debug)] -pub struct SingleLookupRequestState { +pub struct SingleLookupRequestState { /// State of this request. - state: State, + state: State, /// Peers that should have this block or blob. available_peers: HashSet, /// Peers from which we have requested this block. @@ -380,15 +269,9 @@ pub struct SingleLookupRequestState { failed_processing: u8, /// How many times have we attempted to download this block or blob. failed_downloading: u8, - /// Should be incremented everytime this request is retried. The purpose of this is to - /// differentiate retries of the same block/blob request within a lookup. We currently penalize - /// peers and retry requests prior to receiving the stream terminator. This means responses - /// from a prior request may arrive after a new request has been sent, this counter allows - /// us to differentiate these two responses. - req_counter: u32, } -impl SingleLookupRequestState { +impl SingleLookupRequestState { pub fn new(peers: &[PeerId]) -> Self { let mut available_peers = HashSet::default(); for peer in peers.iter().copied() { @@ -401,74 +284,174 @@ impl SingleLookupRequestState { used_peers: HashSet::default(), failed_processing: 0, failed_downloading: 0, - req_counter: 0, } } - pub fn is_current_req_counter(&self, req_counter: u32) -> bool { - self.req_counter == req_counter - } - pub fn is_awaiting_download(&self) -> bool { - matches!(self.state, State::AwaitingDownload) - } - - pub fn is_downloaded(&self) -> bool { match self.state { - State::AwaitingDownload => false, - State::Downloading { .. } => false, - State::Processing { .. } => true, - State::Processed { .. } => true, + State::AwaitingDownload => true, + State::Downloading { .. } + | State::AwaitingProcess { .. } + | State::Processing { .. } + | State::Processed { .. } => false, } } pub fn is_processed(&self) -> bool { match self.state { - State::AwaitingDownload => false, - State::Downloading { .. } => false, - State::Processing { .. } => false, + State::AwaitingDownload + | State::Downloading { .. } + | State::AwaitingProcess { .. } + | State::Processing { .. } => false, State::Processed { .. } => true, } } - pub fn on_download_start(&mut self, peer_id: PeerId) -> u32 { - self.state = State::Downloading { peer_id }; - self.req_counter += 1; - self.req_counter + pub fn peek_downloaded_data(&self) -> Option<&T> { + match &self.state { + State::AwaitingDownload => None, + State::Downloading { .. } => None, + State::AwaitingProcess(result) => Some(&result.value), + State::Processing(result) => Some(&result.value), + State::Processed { .. } => None, + } + } + + /// Switch to `AwaitingProcessing` if the request is in `AwaitingDownload` state, otherwise + /// ignore. + pub fn insert_verified_response(&mut self, result: DownloadResult) -> bool { + if let State::AwaitingDownload = &self.state { + self.state = State::AwaitingProcess(result); + true + } else { + false + } + } + + /// Switch to `Downloading` if the request is in `AwaitingDownload` state, otherwise returns None. + pub fn on_download_start(&mut self) -> Result<(), LookupRequestError> { + match &self.state { + State::AwaitingDownload => { + self.state = State::Downloading; + Ok(()) + } + other => Err(LookupRequestError::BadState(format!( + "Bad state on_download_start expected AwaitingDownload got {other}" + ))), + } } /// Registers a failure in downloading a block. This might be a peer disconnection or a wrong /// block. - pub fn on_download_failure(&mut self) { - self.failed_downloading = self.failed_downloading.saturating_add(1); - self.state = State::AwaitingDownload; + pub fn on_download_failure(&mut self) -> Result<(), LookupRequestError> { + match &self.state { + State::Downloading => { + self.failed_downloading = self.failed_downloading.saturating_add(1); + self.state = State::AwaitingDownload; + Ok(()) + } + other => Err(LookupRequestError::BadState(format!( + "Bad state on_download_failure expected Downloading got {other}" + ))), + } + } + + pub fn on_download_success( + &mut self, + result: DownloadResult, + ) -> Result<(), LookupRequestError> { + match &self.state { + State::Downloading => { + self.state = State::AwaitingProcess(result); + Ok(()) + } + other => Err(LookupRequestError::BadState(format!( + "Bad state on_download_success expected Downloading got {other}" + ))), + } + } + + /// Switch to `Processing` if the request is in `AwaitingProcess` state, otherwise returns None. + pub fn maybe_start_processing(&mut self) -> Option> { + // For 2 lines replace state with placeholder to gain ownership of `result` + match &self.state { + State::AwaitingProcess(result) => { + let result = result.clone(); + self.state = State::Processing(result.clone()); + Some(result) + } + _ => None, + } } - pub fn on_download_success(&mut self) -> Result<(), String> { + /// Revert into `AwaitingProcessing`, if the payload if not invalid and can be submitted for + /// processing latter. + pub fn revert_to_awaiting_processing(&mut self) -> Result<(), LookupRequestError> { match &self.state { - State::Downloading { peer_id } => { - self.state = State::Processing { peer_id: *peer_id }; + State::Processing(result) => { + self.state = State::AwaitingProcess(result.clone()); Ok(()) } - other => Err(format!( - "request bad state, expected downloading got {other}" - )), + other => Err(LookupRequestError::BadState(format!( + "Bad state on revert_to_awaiting_processing expected Processing got {other}" + ))), } } /// Registers a failure in processing a block. - pub fn on_processing_failure(&mut self) { - self.failed_processing = self.failed_processing.saturating_add(1); - self.state = State::AwaitingDownload; + pub fn on_processing_failure(&mut self) -> Result { + match &self.state { + State::Processing(result) => { + let peer_id = result.peer_id; + self.failed_processing = self.failed_processing.saturating_add(1); + self.state = State::AwaitingDownload; + Ok(peer_id) + } + other => Err(LookupRequestError::BadState(format!( + "Bad state on_processing_failure expected Processing got {other}" + ))), + } + } + + pub fn on_processing_success(&mut self) -> Result { + match &self.state { + State::Processing(result) => { + let peer_id = result.peer_id; + self.state = State::Processed(Some(peer_id)); + Ok(peer_id) + } + other => Err(LookupRequestError::BadState(format!( + "Bad state on_processing_success expected Processing got {other}" + ))), + } } - pub fn on_processing_success(&mut self) -> Result<(), String> { + pub fn on_post_process_validation_failure( + &mut self, + ) -> Result, LookupRequestError> { match &self.state { - State::Processing { peer_id } => { - self.state = State::Processed { peer_id: *peer_id }; + State::Processed(peer_id) => { + let peer_id = *peer_id; + self.failed_processing = self.failed_processing.saturating_add(1); + self.state = State::AwaitingDownload; + Ok(peer_id) + } + other => Err(LookupRequestError::BadState(format!( + "Bad state on_post_process_validation_failure expected Processed got {other}" + ))), + } + } + + /// Mark a request as complete without any download or processing + pub fn on_completed_request(&mut self) -> Result<(), LookupRequestError> { + match &self.state { + State::AwaitingDownload => { + self.state = State::Processed(None); Ok(()) } - other => Err(format!("not in processing state: {}", other).to_string()), + other => Err(LookupRequestError::BadState(format!( + "Bad state on_completed_request expected AwaitingDownload got {other}" + ))), } } @@ -487,31 +470,18 @@ impl SingleLookupRequestState { } /// If a peer disconnects, this request could be failed. If so, an error is returned - pub fn check_peer_disconnected(&mut self, dc_peer_id: &PeerId) -> Result<(), ()> { - self.available_peers.remove(dc_peer_id); - if let State::Downloading { peer_id } = &self.state { - if peer_id == dc_peer_id { - // Peer disconnected before providing a block - self.on_download_failure(); - return Err(()); - } - } - Ok(()) - } - - /// Returns the id peer we downloaded from if we have downloaded a verified block, otherwise - /// returns an error. - pub fn processing_peer(&self) -> Result { - match &self.state { - State::Processing { peer_id } | State::Processed { peer_id } => Ok(*peer_id), - other => Err(format!("not in processing state: {}", other).to_string()), - } + pub fn remove_peer(&mut self, disconnected_peer_id: &PeerId) { + self.available_peers.remove(disconnected_peer_id); } pub fn get_used_peers(&self) -> impl Iterator { self.used_peers.iter() } + pub fn get_available_peers(&self) -> impl Iterator { + self.available_peers.iter() + } + /// Selects a random peer from available peers if any, inserts it in used peers and returns it. pub fn use_rand_available_peer(&mut self) -> Option { let peer_id = self @@ -524,63 +494,12 @@ impl SingleLookupRequestState { } } -impl slog::Value for SingleBlockLookup { - fn serialize( - &self, - _record: &slog::Record, - key: slog::Key, - serializer: &mut dyn slog::Serializer, - ) -> slog::Result { - serializer.emit_str("request", key)?; - serializer.emit_arguments("lookup_type", &format_args!("{:?}", self.lookup_type))?; - serializer.emit_arguments("hash", &format_args!("{}", self.block_root()))?; - serializer.emit_arguments( - "blob_ids", - &format_args!("{:?}", self.blob_request_state.requested_ids.indices()), - )?; - serializer.emit_arguments( - "block_request_state.state", - &format_args!("{:?}", self.block_request_state.state), - )?; - serializer.emit_arguments( - "blob_request_state.state", - &format_args!("{:?}", self.blob_request_state.state), - )?; - slog::Result::Ok(()) - } -} - -impl slog::Value for SingleLookupRequestState { - fn serialize( - &self, - record: &slog::Record, - key: slog::Key, - serializer: &mut dyn slog::Serializer, - ) -> slog::Result { - serializer.emit_str("request_state", key)?; - match &self.state { - State::AwaitingDownload => { - "awaiting_download".serialize(record, "state", serializer)? - } - State::Downloading { peer_id } => { - serializer.emit_arguments("downloading_peer", &format_args!("{}", peer_id))? - } - State::Processing { peer_id } => { - serializer.emit_arguments("processing_peer", &format_args!("{}", peer_id))? - } - State::Processed { .. } => "processed".serialize(record, "state", serializer)?, - } - serializer.emit_u8("failed_downloads", self.failed_downloading)?; - serializer.emit_u8("failed_processing", self.failed_processing)?; - slog::Result::Ok(()) - } -} - -impl std::fmt::Display for State { +impl std::fmt::Display for State { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { State::AwaitingDownload => write!(f, "AwaitingDownload"), State::Downloading { .. } => write!(f, "Downloading"), + State::AwaitingProcess { .. } => write!(f, "AwaitingProcessing"), State::Processing { .. } => write!(f, "Processing"), State::Processed { .. } => write!(f, "Processed"), } diff --git a/beacon_node/network/src/sync/block_lookups/tests.rs b/beacon_node/network/src/sync/block_lookups/tests.rs index 8e3b35ee5d3..302a0489c3b 100644 --- a/beacon_node/network/src/sync/block_lookups/tests.rs +++ b/beacon_node/network/src/sync/block_lookups/tests.rs @@ -1,14 +1,17 @@ use crate::network_beacon_processor::NetworkBeaconProcessor; use crate::service::RequestId; -use crate::sync::manager::{RequestId as SyncRequestId, SingleLookupReqId, SyncManager}; +use crate::sync::manager::{ + BlockProcessType, RequestId as SyncRequestId, SingleLookupReqId, SyncManager, +}; use crate::sync::SyncMessage; use crate::NetworkMessage; use std::sync::Arc; use super::*; -use crate::sync::block_lookups::common::ResponseType; +use crate::sync::block_lookups::common::{ResponseType, PARENT_DEPTH_TOLERANCE}; +use beacon_chain::block_verification_types::RpcBlock; use beacon_chain::builder::Witness; use beacon_chain::eth1_chain::CachingEth1Backend; use beacon_chain::test_utils::{ @@ -24,7 +27,7 @@ use store::MemoryStore; use tokio::sync::mpsc; use types::{ test_utils::{SeedableRng, XorShiftRng}, - BlobSidecar, ForkName, MinimalEthSpec as E, SignedBeaconBlock, + BlobSidecar, ForkName, MinimalEthSpec as E, SignedBeaconBlock, Slot, }; type T = Witness, E, MemoryStore, MemoryStore>; @@ -72,6 +75,7 @@ struct TestRig { } const D: Duration = Duration::new(0, 0); +const PARENT_FAIL_TOLERANCE: u8 = SINGLE_BLOCK_LOOKUP_MAX_ATTEMPTS; impl TestRig { fn test_setup() -> Self { @@ -194,11 +198,15 @@ impl TestRig { self.sync_manager.handle_message(sync_message); } + fn active_single_lookups(&self) -> Vec<(Id, Hash256, Option)> { + self.sync_manager.active_single_lookups() + } + fn active_single_lookups_count(&self) -> usize { self.sync_manager.active_single_lookups().len() } - fn active_parent_lookups(&self) -> Vec { + fn active_parent_lookups(&self) -> Vec> { self.sync_manager.active_parent_lookups() } @@ -206,22 +214,74 @@ impl TestRig { self.sync_manager.active_parent_lookups().len() } - fn failed_chains_contains(&mut self, chain_hash: &Hash256) -> bool { - self.sync_manager.failed_chains_contains(chain_hash) + fn assert_single_lookups_count(&self, count: usize) { + assert_eq!( + self.active_single_lookups_count(), + count, + "Unexpected count of single lookups. Current lookups: {:?}", + self.active_single_lookups() + ); } - #[track_caller] - fn assert_parent_lookups_consistency(&self) { - let hashes = self.active_parent_lookups(); - let expected = hashes.len(); + fn assert_parent_lookups_count(&self, count: usize) { assert_eq!( - expected, - hashes - .into_iter() - .collect::>() - .len(), - "duplicated chain hashes in parent queue" - ) + self.active_parent_lookups_count(), + count, + "Unexpected count of parent lookups. Parent lookups: {:?}. Current lookups: {:?}", + self.active_parent_lookups(), + self.active_single_lookups() + ); + } + + fn assert_lookup_is_active(&self, block_root: Hash256) { + let lookups = self.sync_manager.active_single_lookups(); + if !lookups.iter().any(|l| l.1 == block_root) { + panic!("Expected lookup {block_root} to be the only active: {lookups:?}"); + } + } + + fn insert_failed_chain(&mut self, block_root: Hash256) { + self.sync_manager.insert_failed_chain(block_root); + } + + fn assert_not_failed_chain(&mut self, chain_hash: Hash256) { + let failed_chains = self.sync_manager.get_failed_chains(); + if failed_chains.contains(&chain_hash) { + panic!("failed chains contain {chain_hash:?}: {failed_chains:?}"); + } + } + + fn failed_chains_contains(&mut self, chain_hash: &Hash256) -> bool { + self.sync_manager.get_failed_chains().contains(chain_hash) + } + + fn find_single_lookup_for(&self, block_root: Hash256) -> Id { + self.active_single_lookups() + .iter() + .find(|(_, b, _)| b == &block_root) + .unwrap_or_else(|| panic!("no single block lookup found for {block_root}")) + .0 + } + + fn expect_no_active_single_lookups(&self) { + assert!( + self.active_single_lookups().is_empty(), + "expect no single block lookups: {:?}", + self.active_single_lookups() + ); + } + + fn expect_no_active_lookups(&self) { + self.expect_no_active_single_lookups(); + } + + fn expect_lookups(&self, expected_block_roots: &[Hash256]) { + let block_roots = self + .active_single_lookups() + .iter() + .map(|(_, b, _)| *b) + .collect::>(); + assert_eq!(&block_roots, expected_block_roots); } fn new_connected_peer(&mut self) -> PeerId { @@ -233,27 +293,43 @@ impl TestRig { peer_id } - fn parent_chain_processed(&mut self, chain_hash: Hash256, result: BatchProcessResult) { - self.send_sync_message(SyncMessage::BatchProcessed { - sync_type: ChainSegmentProcessId::ParentLookup(chain_hash), - result, - }) + fn parent_chain_processed_success( + &mut self, + chain_hash: Hash256, + blocks: &[Arc>], + ) { + // Send import events for all pending parent blocks + for _ in blocks { + self.parent_block_processed_imported(chain_hash); + } + // Send final import event for the block that triggered the lookup + self.single_block_component_processed_imported(chain_hash); + } + + /// Locate a parent lookup chain with tip hash `chain_hash` + fn find_oldest_parent_lookup(&self, chain_hash: Hash256) -> Hash256 { + let parent_chain = self + .active_parent_lookups() + .into_iter() + .find(|chain| chain.first() == Some(&chain_hash)) + .unwrap_or_else(|| { + panic!( + "No parent chain with chain_hash {chain_hash:?}: Parent lookups {:?} Single lookups {:?}", + self.active_parent_lookups(), + self.active_single_lookups(), + ) + }); + *parent_chain.last().unwrap() } - fn parent_chain_processed_success(&mut self, chain_hash: Hash256) { - self.parent_chain_processed( - chain_hash, - BatchProcessResult::Success { - was_non_empty: true, - }, - ) + fn parent_block_processed(&mut self, chain_hash: Hash256, result: BlockProcessingResult) { + let id = self.find_single_lookup_for(self.find_oldest_parent_lookup(chain_hash)); + self.single_block_component_processed(id, result); } - fn parent_block_processed(&mut self, chain_hash: Hash256, result: BlockProcessingResult) { - self.send_sync_message(SyncMessage::BlockComponentProcessed { - process_type: BlockProcessType::ParentLookup { chain_hash }, - result, - }); + fn parent_blob_processed(&mut self, chain_hash: Hash256, result: BlockProcessingResult) { + let id = self.find_single_lookup_for(self.find_oldest_parent_lookup(chain_hash)); + self.single_blob_component_processed(id, result); } fn parent_block_processed_imported(&mut self, chain_hash: Hash256) { @@ -263,35 +339,24 @@ impl TestRig { ); } - fn single_block_component_processed( - &mut self, - id: SingleLookupReqId, - result: BlockProcessingResult, - ) { + fn single_block_component_processed(&mut self, id: Id, result: BlockProcessingResult) { self.send_sync_message(SyncMessage::BlockComponentProcessed { - process_type: BlockProcessType::SingleBlock { id: id.id }, + process_type: BlockProcessType::SingleBlock { id }, result, }) } - fn single_block_component_processed_imported( - &mut self, - id: SingleLookupReqId, - block_root: Hash256, - ) { + fn single_block_component_processed_imported(&mut self, block_root: Hash256) { + let id = self.find_single_lookup_for(block_root); self.single_block_component_processed( id, BlockProcessingResult::Ok(AvailabilityProcessingStatus::Imported(block_root)), ) } - fn single_blob_component_processed( - &mut self, - id: SingleLookupReqId, - result: BlockProcessingResult, - ) { + fn single_blob_component_processed(&mut self, id: Id, result: BlockProcessingResult) { self.send_sync_message(SyncMessage::BlockComponentProcessed { - process_type: BlockProcessType::SingleBlob { id: id.id }, + process_type: BlockProcessType::SingleBlob { id }, result, }) } @@ -302,6 +367,7 @@ impl TestRig { peer_id: PeerId, beacon_block: Option>>, ) { + self.log("parent_lookup_block_response"); self.send_sync_message(SyncMessage::RpcBlock { request_id: SyncRequestId::SingleBlock { id }, peer_id, @@ -316,6 +382,7 @@ impl TestRig { peer_id: PeerId, beacon_block: Option>>, ) { + self.log("single_lookup_block_response"); self.send_sync_message(SyncMessage::RpcBlock { request_id: SyncRequestId::SingleBlock { id }, peer_id, @@ -330,6 +397,10 @@ impl TestRig { peer_id: PeerId, blob_sidecar: Option>>, ) { + self.log(&format!( + "parent_lookup_blob_response {:?}", + blob_sidecar.as_ref().map(|b| b.index) + )); self.send_sync_message(SyncMessage::RpcBlob { request_id: SyncRequestId::SingleBlob { id }, peer_id, @@ -416,11 +487,7 @@ impl TestRig { peer_id: _, request: Request::BlocksByRoot(request), request_id: RequestId::Sync(SyncRequestId::SingleBlock { id }), - } if id.lookup_type == LookupType::Current - && request.block_roots().to_vec().contains(&for_block) => - { - Some(*id) - } + } if request.block_roots().to_vec().contains(&for_block) => Some(*id), _ => None, }) .unwrap_or_else(|e| panic!("Expected block request for {for_block:?}: {e}")) @@ -433,12 +500,11 @@ impl TestRig { peer_id: _, request: Request::BlobsByRoot(request), request_id: RequestId::Sync(SyncRequestId::SingleBlob { id }), - } if id.lookup_type == LookupType::Current - && request - .blob_ids - .to_vec() - .iter() - .any(|r| r.block_root == for_block) => + } if request + .blob_ids + .to_vec() + .iter() + .any(|r| r.block_root == for_block) => { Some(*id) } @@ -454,11 +520,7 @@ impl TestRig { peer_id: _, request: Request::BlocksByRoot(request), request_id: RequestId::Sync(SyncRequestId::SingleBlock { id }), - } if id.lookup_type == LookupType::Parent - && request.block_roots().to_vec().contains(&for_block) => - { - Some(*id) - } + } if request.block_roots().to_vec().contains(&for_block) => Some(*id), _ => None, }) .unwrap_or_else(|e| panic!("Expected block parent request for {for_block:?}: {e}")) @@ -471,12 +533,11 @@ impl TestRig { peer_id: _, request: Request::BlobsByRoot(request), request_id: RequestId::Sync(SyncRequestId::SingleBlob { id }), - } if id.lookup_type == LookupType::Parent - && request - .blob_ids - .to_vec() - .iter() - .all(|r| r.block_root == for_block) => + } if request + .blob_ids + .to_vec() + .iter() + .all(|r| r.block_root == for_block) => { Some(*id) } @@ -544,9 +605,13 @@ impl TestRig { fn expect_parent_chain_process(&mut self) { match self.beacon_processor_rx.try_recv() { Ok(work) => { - assert_eq!(work.work_type(), beacon_processor::CHAIN_SEGMENT); + // Parent chain sends blocks one by one + assert_eq!(work.work_type(), beacon_processor::RPC_BLOCK); } - other => panic!("Expected chain segment process, found {:?}", other), + other => panic!( + "Expected rpc_block from chain segment process, found {:?}", + other + ), } } @@ -560,24 +625,37 @@ impl TestRig { #[track_caller] fn expect_empty_beacon_processor(&mut self) { + match self.beacon_processor_rx.try_recv() { + Err(mpsc::error::TryRecvError::Empty) => {} // ok + Ok(event) => panic!("expected empty beacon processor: {:?}", event), + other => panic!("unexpected err {:?}", other), + } + } + + #[track_caller] + pub fn expect_penalty(&mut self, peer_id: PeerId, expect_penalty_msg: &'static str) { + let penalty_msg = self + .pop_received_network_event(|ev| match ev { + NetworkMessage::ReportPeer { + peer_id: p_id, msg, .. + } if p_id == &peer_id => Some(msg.to_owned()), + _ => None, + }) + .unwrap_or_else(|_| { + panic!( + "Expected '{expect_penalty_msg}' penalty for peer {peer_id}: {:#?}", + self.network_rx_queue + ) + }); assert_eq!( - self.beacon_processor_rx.try_recv().expect_err("must err"), - mpsc::error::TryRecvError::Empty + penalty_msg, expect_penalty_msg, + "Unexpected penalty msg for {peer_id}" ); } - #[track_caller] - pub fn expect_penalty(&mut self, peer_id: PeerId) { - self.pop_received_network_event(|ev| match ev { - NetworkMessage::ReportPeer { peer_id: p_id, .. } if p_id == &peer_id => Some(()), - _ => None, - }) - .unwrap_or_else(|_| { - panic!( - "Expected peer penalty for {peer_id}: {:#?}", - self.network_rx_queue - ) - }) + pub fn expect_single_penalty(&mut self, peer_id: PeerId, expect_penalty_msg: &'static str) { + self.expect_penalty(peer_id, expect_penalty_msg); + self.expect_no_penalty_for(peer_id); } pub fn block_with_parent_and_blobs( @@ -595,19 +673,46 @@ impl TestRig { pub fn rand_blockchain(&mut self, depth: usize) -> Vec>> { let mut blocks = Vec::>>::with_capacity(depth); - while blocks.len() < depth { + for slot in 0..depth { let parent = blocks .last() .map(|b| b.canonical_root()) .unwrap_or_else(Hash256::random); let mut block = self.rand_block(); *block.message_mut().parent_root_mut() = parent; + *block.message_mut().slot_mut() = slot.into(); blocks.push(block.into()); } + self.log(&format!( + "Blockchain dump {:#?}", + blocks + .iter() + .map(|b| format!( + "block {} {} parent {}", + b.slot(), + b.canonical_root(), + b.parent_root() + )) + .collect::>() + )); blocks } } +#[test] +fn stable_rng() { + let mut rng = XorShiftRng::from_seed([42; 16]); + let (block, _) = generate_rand_block_and_blobs::(ForkName::Base, NumBlobs::None, &mut rng); + assert_eq!( + block.canonical_root(), + Hash256::from_slice( + &hex::decode("adfd2e9e7a7976e8ccaed6eaf0257ed36a5b476732fee63ff44966602fd099ec") + .unwrap() + ), + "rng produces a consistent value" + ); +} + #[test] fn test_single_block_lookup_happy_path() { let mut rig = TestRig::test_setup(); @@ -630,9 +735,9 @@ fn test_single_block_lookup_happy_path() { // Send the stream termination. Peer should have not been penalized, and the request removed // after processing. rig.single_lookup_block_response(id, peer_id, None); - rig.single_block_component_processed_imported(id, block_root); + rig.single_block_component_processed_imported(block_root); rig.expect_empty_network(); - assert_eq!(rig.active_single_lookups_count(), 0); + rig.expect_no_active_lookups(); } #[test] @@ -648,7 +753,7 @@ fn test_single_block_lookup_empty_response() { // The peer does not have the block. It should be penalized. rig.single_lookup_block_response(id, peer_id, None); - rig.expect_penalty(peer_id); + rig.expect_penalty(peer_id, "NoResponseReturned"); rig.expect_block_lookup_request(block_hash); // it should be retried } @@ -667,7 +772,7 @@ fn test_single_block_lookup_wrong_response() { // Peer sends something else. It should be penalized. let bad_block = rig.rand_block(); rig.single_lookup_block_response(id, peer_id, Some(bad_block.into())); - rig.expect_penalty(peer_id); + rig.expect_penalty(peer_id, "UnrequestedBlockRoot"); rig.expect_block_lookup_request(block_hash); // should be retried // Send the stream termination. This should not produce an additional penalty. @@ -717,10 +822,10 @@ fn test_single_block_lookup_becomes_parent_request() { // Send the stream termination. Peer should have not been penalized, and the request moved to a // parent request after processing. rig.single_block_component_processed( - id, + id.lookup_id, BlockError::ParentUnknown(RpcBlock::new_without_blobs(None, block)).into(), ); - assert_eq!(rig.active_single_lookups_count(), 1); + assert_eq!(rig.active_single_lookups_count(), 2); // 2 = current + parent rig.expect_parent_request_block_and_blobs(parent_root); rig.expect_empty_network(); assert_eq!(rig.active_parent_lookups_count(), 1); @@ -748,8 +853,8 @@ fn test_parent_lookup_happy_path() { BlockError::BlockIsAlreadyKnown(block_root).into(), ); rig.expect_parent_chain_process(); - rig.parent_chain_processed_success(block_root); - assert_eq!(rig.active_parent_lookups_count(), 0); + rig.parent_chain_processed_success(block_root, &[]); + rig.expect_no_active_lookups(); } #[test] @@ -766,7 +871,7 @@ fn test_parent_lookup_wrong_response() { // Peer sends the wrong block, peer should be penalized and the block re-requested. let bad_block = rig.rand_block(); rig.parent_lookup_block_response(id1, peer_id, Some(bad_block.into())); - rig.expect_penalty(peer_id); + rig.expect_penalty(peer_id, "UnrequestedBlockRoot"); let id2 = rig.expect_block_parent_request(parent_root); // Send the stream termination for the first request. This should not produce extra penalties. @@ -780,8 +885,8 @@ fn test_parent_lookup_wrong_response() { // Processing succeeds, now the rest of the chain should be sent for processing. rig.parent_block_processed_imported(block_root); rig.expect_parent_chain_process(); - rig.parent_chain_processed_success(block_root); - assert_eq!(rig.active_parent_lookups_count(), 0); + rig.parent_chain_processed_success(block_root, &[]); + rig.expect_no_active_lookups(); } #[test] @@ -797,7 +902,7 @@ fn test_parent_lookup_empty_response() { // Peer sends an empty response, peer should be penalized and the block re-requested. rig.parent_lookup_block_response(id1, peer_id, None); - rig.expect_penalty(peer_id); + rig.expect_penalty(peer_id, "NoResponseReturned"); let id2 = rig.expect_block_parent_request(parent_root); // Send the right block this time. @@ -806,9 +911,9 @@ fn test_parent_lookup_empty_response() { // Processing succeeds, now the rest of the chain should be sent for processing. rig.parent_block_processed_imported(block_root); - rig.expect_parent_chain_process(); - rig.parent_chain_processed_success(block_root); - assert_eq!(rig.active_parent_lookups_count(), 0); + + rig.single_block_component_processed_imported(block_root); + rig.expect_no_active_lookups(); } #[test] @@ -833,8 +938,8 @@ fn test_parent_lookup_rpc_failure() { // Processing succeeds, now the rest of the chain should be sent for processing. rig.parent_block_processed_imported(block_root); rig.expect_parent_chain_process(); - rig.parent_chain_processed_success(block_root); - assert_eq!(rig.active_parent_lookups_count(), 0); + rig.parent_chain_processed_success(block_root, &[]); + rig.expect_no_active_lookups(); } #[test] @@ -847,7 +952,7 @@ fn test_parent_lookup_too_many_attempts() { // Trigger the request rig.trigger_unknown_parent_block(peer_id, block.into()); - for i in 1..=parent_lookup::PARENT_FAIL_TOLERANCE { + for i in 1..=PARENT_FAIL_TOLERANCE { let id = rig.expect_block_parent_request(parent_root); // Blobs are only requested in the first iteration as this test only retries blocks if rig.after_deneb() && i == 1 { @@ -872,11 +977,11 @@ fn test_parent_lookup_too_many_attempts() { // I'm unsure if this is how it should behave? // rig.parent_lookup_block_response(id, peer_id, None); - rig.expect_penalty(peer_id); + rig.expect_penalty(peer_id, "UnrequestedBlockRoot"); } } - assert_eq!(rig.active_parent_lookups_count(), 0); + rig.expect_no_active_lookups(); } #[test] @@ -888,7 +993,7 @@ fn test_parent_lookup_too_many_download_attempts_no_blacklist() { // Trigger the request rig.trigger_unknown_parent_block(peer_id, block.into()); - for i in 1..=parent_lookup::PARENT_FAIL_TOLERANCE { + for i in 1..=PARENT_FAIL_TOLERANCE { assert!(!rig.failed_chains_contains(&block_root)); let id = rig.expect_block_parent_request(parent_root); // Blobs are only requested in the first iteration as this test only retries blocks @@ -902,18 +1007,18 @@ fn test_parent_lookup_too_many_download_attempts_no_blacklist() { // Send a bad block this time. It should be tried again. let bad_block = rig.rand_block(); rig.parent_lookup_block_response(id, peer_id, Some(bad_block.into())); - rig.expect_penalty(peer_id); + rig.expect_penalty(peer_id, "UnrequestedBlockRoot"); } } - assert_eq!(rig.active_parent_lookups_count(), 0); assert!(!rig.failed_chains_contains(&block_root)); assert!(!rig.failed_chains_contains(&parent.canonical_root())); + rig.expect_no_active_lookups(); } #[test] fn test_parent_lookup_too_many_processing_attempts_must_blacklist() { - const PROCESSING_FAILURES: u8 = parent_lookup::PARENT_FAIL_TOLERANCE / 2 + 1; + const PROCESSING_FAILURES: u8 = PARENT_FAIL_TOLERANCE / 2 + 1; let mut rig = TestRig::test_setup(); let (parent, block, parent_root, block_root) = rig.rand_block_and_parent(); let peer_id = rig.new_connected_peer(); @@ -922,7 +1027,7 @@ fn test_parent_lookup_too_many_processing_attempts_must_blacklist() { rig.trigger_unknown_parent_block(peer_id, block.into()); rig.log("Fail downloading the block"); - for i in 0..(parent_lookup::PARENT_FAIL_TOLERANCE - PROCESSING_FAILURES) { + for i in 0..(PARENT_FAIL_TOLERANCE - PROCESSING_FAILURES) { let id = rig.expect_block_parent_request(parent_root); // Blobs are only requested in the first iteration as this test only retries blocks if rig.after_deneb() && i == 0 { @@ -933,28 +1038,25 @@ fn test_parent_lookup_too_many_processing_attempts_must_blacklist() { } rig.log("Now fail processing a block in the parent request"); - for i in 0..PROCESSING_FAILURES { + for _ in 0..PROCESSING_FAILURES { let id = rig.expect_block_parent_request(parent_root); - // Blobs are only requested in the first iteration as this test only retries blocks - if rig.after_deneb() && i != 0 { - let _ = rig.expect_blob_parent_request(parent_root); - } - assert!(!rig.failed_chains_contains(&block_root)); + // Blobs are only requested in the previous first iteration as this test only retries blocks + rig.assert_not_failed_chain(block_root); // send the right parent but fail processing rig.parent_lookup_block_response(id, peer_id, Some(parent.clone().into())); rig.parent_block_processed(block_root, BlockError::InvalidSignature.into()); rig.parent_lookup_block_response(id, peer_id, None); - rig.expect_penalty(peer_id); + rig.expect_penalty(peer_id, "lookup_block_processing_failure"); } - assert!(rig.failed_chains_contains(&block_root)); - assert_eq!(rig.active_parent_lookups_count(), 0); + rig.assert_not_failed_chain(block_root); + rig.expect_no_active_lookups(); } #[test] fn test_parent_lookup_too_deep() { let mut rig = TestRig::test_setup(); - let mut blocks = rig.rand_blockchain(parent_lookup::PARENT_DEPTH_TOLERANCE); + let mut blocks = rig.rand_blockchain(PARENT_DEPTH_TOLERANCE); let peer_id = rig.new_connected_peer(); let trigger_block = blocks.pop().unwrap(); @@ -976,19 +1078,59 @@ fn test_parent_lookup_too_deep() { ) } - rig.expect_penalty(peer_id); + rig.expect_penalty(peer_id, "chain_too_long"); assert!(rig.failed_chains_contains(&chain_hash)); } #[test] -fn test_parent_lookup_disconnection() { +fn test_parent_lookup_disconnection_no_peers_left() { let mut rig = TestRig::test_setup(); let peer_id = rig.new_connected_peer(); let trigger_block = rig.rand_block(); rig.trigger_unknown_parent_block(peer_id, trigger_block.into()); rig.peer_disconnected(peer_id); - assert_eq!(rig.active_parent_lookups_count(), 0); + rig.expect_no_active_lookups(); +} + +#[test] +fn test_parent_lookup_disconnection_peer_left() { + let mut rig = TestRig::test_setup(); + let peer_ids = (0..2).map(|_| rig.new_connected_peer()).collect::>(); + let trigger_block = rig.rand_block(); + // lookup should have two peers associated with the same block + for peer_id in peer_ids.iter() { + rig.trigger_unknown_parent_block(*peer_id, trigger_block.clone().into()); + } + // Disconnect the first peer only, which is the one handling the request + rig.peer_disconnected(*peer_ids.first().unwrap()); + rig.assert_parent_lookups_count(1); +} + +#[test] +fn test_skip_creating_failed_parent_lookup() { + let mut rig = TestRig::test_setup(); + let (_, block, parent_root, _) = rig.rand_block_and_parent(); + let peer_id = rig.new_connected_peer(); + rig.insert_failed_chain(parent_root); + rig.trigger_unknown_parent_block(peer_id, block.into()); + // Expect single penalty for peer, despite dropping two lookups + rig.expect_single_penalty(peer_id, "failed_chain"); + // Both current and parent lookup should be rejected + rig.expect_no_active_lookups(); +} + +#[test] +fn test_skip_creating_failed_current_lookup() { + let mut rig = TestRig::test_setup(); + let (_, block, parent_root, block_root) = rig.rand_block_and_parent(); + let peer_id = rig.new_connected_peer(); + rig.insert_failed_chain(block_root); + rig.trigger_unknown_parent_block(peer_id, block.into()); + // Expect single penalty for peer + rig.expect_single_penalty(peer_id, "failed_chain"); + // Only the current lookup should be rejected + rig.expect_lookups(&[parent_root]); } #[test] @@ -1015,9 +1157,9 @@ fn test_single_block_lookup_ignored_response() { // after processing. rig.single_lookup_block_response(id, peer_id, None); // Send an Ignored response, the request should be dropped - rig.single_block_component_processed(id, BlockProcessingResult::Ignored); + rig.single_block_component_processed(id.lookup_id, BlockProcessingResult::Ignored); rig.expect_empty_network(); - assert_eq!(rig.active_single_lookups_count(), 0); + rig.expect_no_active_lookups(); } #[test] @@ -1028,8 +1170,10 @@ fn test_parent_lookup_ignored_response() { let peer_id = rig.new_connected_peer(); // Trigger the request - rig.trigger_unknown_parent_block(peer_id, block.into()); + rig.trigger_unknown_parent_block(peer_id, block.clone().into()); let id = rig.expect_parent_request_block_and_blobs(parent_root); + // Note: single block lookup for current `block` does not trigger any request because it does + // not have blobs, and the block is already cached // Peer sends the right block, it should be sent for processing. Peer should not be penalized. rig.parent_lookup_block_response(id, peer_id, Some(parent.into())); @@ -1039,7 +1183,7 @@ fn test_parent_lookup_ignored_response() { // Return an Ignored result. The request should be dropped rig.parent_block_processed(block_root, BlockProcessingResult::Ignored); rig.expect_empty_network(); - assert_eq!(rig.active_parent_lookups_count(), 0); + rig.expect_no_active_lookups(); } /// This is a regression test. @@ -1056,7 +1200,7 @@ fn test_same_chain_race_condition() { let chain_hash = trigger_block.canonical_root(); rig.trigger_unknown_parent_block(peer_id, trigger_block.clone()); - for (i, block) in blocks.into_iter().rev().enumerate() { + for (i, block) in blocks.clone().into_iter().rev().enumerate() { let id = rig.expect_parent_request_block_and_blobs(block.canonical_root()); // the block rig.parent_lookup_block_response(id, peer_id, Some(block.clone())); @@ -1066,36 +1210,40 @@ fn test_same_chain_race_condition() { rig.expect_block_process(ResponseType::Block); // the processing result if i + 2 == depth { - // one block was removed + rig.log(&format!("Block {i} was removed and is already known")); rig.parent_block_processed( chain_hash, BlockError::BlockIsAlreadyKnown(block.canonical_root()).into(), ) } else { + rig.log(&format!("Block {i} ParentUnknown")); rig.parent_block_processed( chain_hash, BlockError::ParentUnknown(RpcBlock::new_without_blobs(None, block)).into(), ) } - rig.assert_parent_lookups_consistency(); } - // Processing succeeds, now the rest of the chain should be sent for processing. - rig.expect_parent_chain_process(); - // Try to get this block again while the chain is being processed. We should not request it again. let peer_id = rig.new_connected_peer(); - rig.trigger_unknown_parent_block(peer_id, trigger_block); - rig.assert_parent_lookups_consistency(); + rig.trigger_unknown_parent_block(peer_id, trigger_block.clone()); + rig.expect_empty_network(); - rig.parent_chain_processed_success(chain_hash); - assert_eq!(rig.active_parent_lookups_count(), 0); + // Processing succeeds, now the rest of the chain should be sent for processing. + for block in blocks.iter().skip(1).chain(&[trigger_block]) { + rig.expect_parent_chain_process(); + rig.single_block_component_processed_imported(block.canonical_root()); + } + rig.expect_no_active_lookups(); } mod deneb_only { use super::*; - use beacon_chain::data_availability_checker::AvailabilityCheckError; + use beacon_chain::{ + block_verification_types::RpcBlock, data_availability_checker::AvailabilityCheckError, + }; use ssz_types::VariableList; + use std::collections::VecDeque; struct DenebTester { rig: TestRig, @@ -1232,6 +1380,11 @@ mod deneb_only { }) } + fn log(self, msg: &str) -> Self { + self.rig.log(msg); + self + } + fn parent_block_response(mut self) -> Self { self.rig.expect_empty_network(); let block = self.parent_block.pop_front().unwrap().clone(); @@ -1242,7 +1395,7 @@ mod deneb_only { Some(block), ); - assert_eq!(self.rig.active_parent_lookups_count(), 1); + self.rig.assert_parent_lookups_count(1); self } @@ -1286,18 +1439,22 @@ mod deneb_only { self.rig.expect_empty_network(); // The request should still be active. - assert_eq!(self.rig.active_single_lookups_count(), 1); + self.rig + .assert_lookup_is_active(self.block.canonical_root()); self } fn blobs_response(mut self) -> Self { + self.rig + .log(&format!("blobs response {}", self.blobs.len())); for blob in &self.blobs { self.rig.single_lookup_blob_response( self.blob_req_id.expect("blob request id"), self.peer_id, Some(blob.clone()), ); - assert_eq!(self.rig.active_single_lookups_count(), 1); + self.rig + .assert_lookup_is_active(self.block.canonical_root()); } self.rig.single_lookup_blob_response( self.blob_req_id.expect("blob request id"), @@ -1356,29 +1513,68 @@ mod deneb_only { self } + fn block_missing_components(mut self) -> Self { + self.rig.single_block_component_processed( + self.block_req_id.expect("block request id").lookup_id, + BlockProcessingResult::Ok(AvailabilityProcessingStatus::MissingComponents( + self.block.slot(), + self.block_root, + )), + ); + self.rig.expect_empty_network(); + self.rig.assert_single_lookups_count(1); + self + } + + fn blob_imported(mut self) -> Self { + self.rig.single_blob_component_processed( + self.blob_req_id.expect("blob request id").lookup_id, + BlockProcessingResult::Ok(AvailabilityProcessingStatus::Imported(self.block_root)), + ); + self.rig.expect_empty_network(); + self.rig.assert_single_lookups_count(0); + self + } + fn block_imported(mut self) -> Self { // Missing blobs should be the request is not removed, the outstanding blobs request should // mean we do not send a new request. self.rig.single_block_component_processed( - self.block_req_id.expect("block request id"), + self.block_req_id + .or(self.blob_req_id) + .expect("block request id") + .lookup_id, BlockProcessingResult::Ok(AvailabilityProcessingStatus::Imported(self.block_root)), ); self.rig.expect_empty_network(); - assert_eq!(self.rig.active_single_lookups_count(), 0); + self.rig.assert_single_lookups_count(0); self } fn parent_block_imported(mut self) -> Self { + self.rig.log("parent_block_imported"); self.rig.parent_block_processed( self.block_root, BlockProcessingResult::Ok(AvailabilityProcessingStatus::Imported(self.block_root)), ); self.rig.expect_empty_network(); - assert_eq!(self.rig.active_parent_lookups_count(), 0); + self.rig.assert_parent_lookups_count(0); + self + } + + fn parent_blob_imported(mut self) -> Self { + self.rig.log("parent_blob_imported"); + self.rig.parent_blob_processed( + self.block_root, + BlockProcessingResult::Ok(AvailabilityProcessingStatus::Imported(self.block_root)), + ); + self.rig.expect_empty_network(); + self.rig.assert_parent_lookups_count(0); self } fn parent_block_unknown_parent(mut self) -> Self { + self.rig.log("parent_block_unknown_parent"); let block = self.unknown_parent_block.take().unwrap(); // Now this block is the one we expect requests from self.block = block.clone(); @@ -1396,6 +1592,26 @@ mod deneb_only { self } + fn parent_block_missing_components(mut self) -> Self { + let block = self.unknown_parent_block.clone().unwrap(); + self.rig.parent_block_processed( + self.block_root, + BlockProcessingResult::Ok(AvailabilityProcessingStatus::MissingComponents( + block.slot(), + block.canonical_root(), + )), + ); + self.rig.parent_blob_processed( + self.block_root, + BlockProcessingResult::Ok(AvailabilityProcessingStatus::MissingComponents( + block.slot(), + block.canonical_root(), + )), + ); + assert_eq!(self.rig.active_parent_lookups_count(), 1); + self + } + fn invalid_parent_processed(mut self) -> Self { self.rig.parent_block_processed( self.block_root, @@ -1407,50 +1623,51 @@ mod deneb_only { fn invalid_block_processed(mut self) -> Self { self.rig.single_block_component_processed( - self.block_req_id.expect("block request id"), + self.block_req_id.expect("block request id").lookup_id, BlockProcessingResult::Err(BlockError::ProposalSignatureInvalid), ); - assert_eq!(self.rig.active_single_lookups_count(), 1); + self.rig.assert_single_lookups_count(1); self } fn invalid_blob_processed(mut self) -> Self { - self.rig.single_block_component_processed( - self.blob_req_id.expect("blob request id"), + self.rig.log("invalid_blob_processed"); + self.rig.single_blob_component_processed( + self.blob_req_id.expect("blob request id").lookup_id, BlockProcessingResult::Err(BlockError::AvailabilityCheck( AvailabilityCheckError::KzgVerificationFailed, )), ); - assert_eq!(self.rig.active_single_lookups_count(), 1); + self.rig.assert_single_lookups_count(1); self } fn missing_components_from_block_request(mut self) -> Self { self.rig.single_block_component_processed( - self.block_req_id.expect("block request id"), + self.block_req_id.expect("block request id").lookup_id, BlockProcessingResult::Ok(AvailabilityProcessingStatus::MissingComponents( self.slot, self.block_root, )), ); - assert_eq!(self.rig.active_single_lookups_count(), 1); + self.rig.assert_single_lookups_count(1); self } fn missing_components_from_blob_request(mut self) -> Self { self.rig.single_blob_component_processed( - self.blob_req_id.expect("blob request id"), + self.blob_req_id.expect("blob request id").lookup_id, BlockProcessingResult::Ok(AvailabilityProcessingStatus::MissingComponents( self.slot, self.block_root, )), ); - assert_eq!(self.rig.active_single_lookups_count(), 1); + self.rig.assert_single_lookups_count(1); self } - fn expect_penalty(mut self) -> Self { - self.rig.expect_penalty(self.peer_id); + fn expect_penalty(mut self, expect_penalty_msg: &'static str) -> Self { + self.rig.expect_penalty(self.peer_id, expect_penalty_msg); self } fn expect_no_penalty(mut self) -> Self { @@ -1514,6 +1731,10 @@ mod deneb_only { self.rig.expect_block_process(ResponseType::Block); self } + fn expect_no_active_lookups(self) -> Self { + self.rig.expect_no_active_lookups(); + self + } fn search_parent_dup(mut self) -> Self { self.rig .trigger_unknown_parent_block(self.peer_id, self.block.clone()); @@ -1530,8 +1751,9 @@ mod deneb_only { tester .block_response_triggering_process() .blobs_response() + .block_missing_components() // blobs not yet imported .blobs_response_was_valid() - .block_imported(); + .blob_imported(); // now blobs resolve as imported } #[test] @@ -1541,10 +1763,11 @@ mod deneb_only { }; tester - .blobs_response() - .blobs_response_was_valid() + .blobs_response() // hold blobs for processing .block_response_triggering_process() - .block_imported(); + .block_missing_components() // blobs not yet imported + .blobs_response_was_valid() + .blob_imported(); // now blobs resolve as imported } #[test] @@ -1555,7 +1778,7 @@ mod deneb_only { tester .empty_block_response() - .expect_penalty() + .expect_penalty("NoResponseReturned") .expect_block_request() .expect_no_blobs_request() .empty_blobs_response() @@ -1578,7 +1801,7 @@ mod deneb_only { .missing_components_from_block_request() .empty_blobs_response() .missing_components_from_blob_request() - .expect_penalty() + .expect_penalty("sent_incomplete_blobs") .expect_blobs_request() .expect_no_block_request(); } @@ -1591,11 +1814,10 @@ mod deneb_only { tester .blobs_response() - .blobs_response_was_valid() .expect_no_penalty_and_no_requests() - .missing_components_from_blob_request() + // blobs not sent for processing until the block is processed .empty_block_response() - .expect_penalty() + .expect_penalty("NoResponseReturned") .expect_block_request() .expect_no_blobs_request(); } @@ -1609,11 +1831,11 @@ mod deneb_only { tester .block_response_triggering_process() .invalid_block_processed() - .expect_penalty() + .expect_penalty("lookup_block_processing_failure") .expect_block_request() .expect_no_blobs_request() .blobs_response() - .missing_components_from_blob_request() + // blobs not sent for processing until the block is processed .expect_no_penalty_and_no_requests(); } @@ -1628,7 +1850,7 @@ mod deneb_only { .missing_components_from_block_request() .blobs_response() .invalid_blob_processed() - .expect_penalty() + .expect_penalty("lookup_blobs_processing_failure") .expect_blobs_request() .expect_no_block_request(); } @@ -1645,7 +1867,7 @@ mod deneb_only { .invalidate_blobs_too_few() .blobs_response() .missing_components_from_blob_request() - .expect_penalty() + .expect_penalty("sent_incomplete_blobs") .expect_blobs_request() .expect_no_block_request(); } @@ -1660,7 +1882,7 @@ mod deneb_only { .block_response_triggering_process() .invalidate_blobs_too_many() .blobs_response() - .expect_penalty() + .expect_penalty("DuplicateData") .expect_blobs_request() .expect_no_block_request(); } @@ -1673,8 +1895,7 @@ mod deneb_only { tester .invalidate_blobs_too_few() - .blobs_response() - .blobs_response_was_valid() + .blobs_response() // blobs are not sent until the block is processed .expect_no_penalty_and_no_requests() .block_response_triggering_process(); } @@ -1688,7 +1909,7 @@ mod deneb_only { tester .invalidate_blobs_too_many() .blobs_response() - .expect_penalty() + .expect_penalty("DuplicateData") .expect_blobs_request() .expect_no_block_request() .block_response_triggering_process(); @@ -1729,9 +1950,8 @@ mod deneb_only { .parent_blob_response() .expect_block_process() .invalid_parent_processed() - .expect_penalty() + .expect_penalty("lookup_block_processing_failure") .expect_parent_block_request() - .expect_parent_blobs_request() .expect_empty_beacon_processor(); } @@ -1780,7 +2000,7 @@ mod deneb_only { tester .empty_parent_block_response() - .expect_penalty() + .expect_penalty("NoResponseReturned") .expect_parent_block_request() .expect_no_blobs_request() .parent_blob_response() @@ -1802,15 +2022,20 @@ mod deneb_only { tester .blobs_response() + .log(" Return empty blobs for parent, block errors with missing components, downscore") .empty_parent_blobs_response() .expect_no_penalty_and_no_requests() .parent_block_response() - .expect_penalty() + .parent_block_missing_components() + .expect_penalty("sent_incomplete_blobs") + .log("Re-request parent blobs, succeed and import parent") .expect_parent_blobs_request() .parent_blob_response() .expect_block_process() - .parent_block_imported() - .expect_parent_chain_process(); + .parent_blob_imported() + .log("resolve original block trigger blobs request and import") + .block_imported() + .expect_no_active_lookups(); } #[test] @@ -1848,9 +2073,9 @@ mod deneb_only { .parent_blob_response() .expect_block_process() .invalid_parent_processed() - .expect_penalty() + .expect_penalty("lookup_block_processing_failure") .expect_parent_block_request() - .expect_parent_blobs_request() + // blobs are not sent until block is processed .expect_empty_beacon_processor(); } @@ -1868,7 +2093,10 @@ mod deneb_only { .expect_block_process() .parent_block_imported() .block_response() - .expect_parent_chain_process(); + .blobs_response() + .expect_parent_chain_process() + .block_imported() + .expect_no_active_lookups(); } #[test] @@ -1886,7 +2114,10 @@ mod deneb_only { .parent_blob_response() .expect_block_process() .parent_block_imported() - .expect_parent_chain_process(); + .blobs_response() + .expect_parent_chain_process() + .block_imported() + .expect_no_active_lookups(); } #[test] @@ -1899,7 +2130,7 @@ mod deneb_only { tester .empty_parent_block_response() - .expect_penalty() + .expect_penalty("NoResponseReturned") .expect_parent_block_request() .expect_no_blobs_request() .parent_blob_response() @@ -1907,8 +2138,10 @@ mod deneb_only { .parent_block_response() .expect_block_process() .parent_block_imported() + .blobs_response() .block_response() - .expect_parent_chain_process(); + .block_imported() + .expect_no_active_lookups(); } #[test] @@ -1921,15 +2154,21 @@ mod deneb_only { tester .block_response() + .log(" Return empty blobs for parent, block errors with missing components, downscore") .empty_parent_blobs_response() .expect_no_penalty_and_no_requests() .parent_block_response() - .expect_penalty() + .parent_block_missing_components() + .expect_penalty("sent_incomplete_blobs") + .log("Re-request parent blobs, succeed and import parent") .expect_parent_blobs_request() .parent_blob_response() .expect_block_process() - .parent_block_imported() - .expect_parent_chain_process(); + .parent_blob_imported() + .log("resolve original block trigger blobs request and import") + .blobs_response() + .block_imported() + .expect_no_active_lookups(); } #[test] diff --git a/beacon_node/network/src/sync/block_sidecar_coupling.rs b/beacon_node/network/src/sync/block_sidecar_coupling.rs index 6a3b568c1c4..d159733cbc7 100644 --- a/beacon_node/network/src/sync/block_sidecar_coupling.rs +++ b/beacon_node/network/src/sync/block_sidecar_coupling.rs @@ -130,4 +130,30 @@ mod tests { assert!(info.is_finished()); info.into_responses().unwrap(); } + + #[test] + fn empty_blobs_into_responses() { + let mut info = BlocksAndBlobsRequestInfo::::new(ByRangeRequestType::BlocksAndBlobs); + let mut rng = XorShiftRng::from_seed([42; 16]); + let blocks = (0..4) + .map(|_| { + // Always generate some blobs. + generate_rand_block_and_blobs::(ForkName::Deneb, NumBlobs::Number(3), &mut rng).0 + }) + .collect::>(); + + // Send blocks and complete terminate response + for block in blocks { + info.add_block_response(Some(block.into())); + } + info.add_block_response(None); + // Expect no blobs returned + info.add_sidecar_response(None); + + // Assert response is finished and RpcBlocks can be constructed, even if blobs weren't returned. + // This makes sure we don't expect blobs here when they have expired. Checking this logic should + // be hendled elsewhere. + assert!(info.is_finished()); + info.into_responses().unwrap(); + } } diff --git a/beacon_node/network/src/sync/manager.rs b/beacon_node/network/src/sync/manager.rs index 9c17c6a1512..0836d97c49f 100644 --- a/beacon_node/network/src/sync/manager.rs +++ b/beacon_node/network/src/sync/manager.rs @@ -34,7 +34,6 @@ //! search for the block and subsequently search for parents if needed. use super::backfill_sync::{BackFillSync, ProcessResult, SyncStart}; -use super::block_lookups::common::LookupType; use super::block_lookups::BlockLookups; use super::network_context::{BlockOrBlob, RangeRequestId, RpcEvent, SyncNetworkContext}; use super::peer_sync_info::{remote_sync_type, PeerSyncType}; @@ -42,11 +41,13 @@ use super::range_sync::{RangeSync, RangeSyncType, EPOCHS_PER_BATCH}; use crate::network_beacon_processor::{ChainSegmentProcessId, NetworkBeaconProcessor}; use crate::service::NetworkMessage; use crate::status::ToStatusMessage; -use crate::sync::block_lookups::{BlobRequestState, BlockRequestState}; +use crate::sync::block_lookups::{ + BlobRequestState, BlockComponent, BlockRequestState, DownloadResult, +}; use crate::sync::block_sidecar_coupling::BlocksAndBlobsRequestInfo; use beacon_chain::block_verification_types::AsBlock; use beacon_chain::block_verification_types::RpcBlock; -use beacon_chain::data_availability_checker::ChildComponents; +use beacon_chain::validator_monitor::timestamp_now; use beacon_chain::{ AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes, BlockError, EngineState, }; @@ -56,12 +57,10 @@ use lighthouse_network::types::{NetworkGlobals, SyncState}; use lighthouse_network::SyncInfo; use lighthouse_network::{PeerAction, PeerId}; use slog::{crit, debug, error, info, trace, warn, Logger}; -use std::ops::IndexMut; use std::ops::Sub; use std::sync::Arc; use std::time::Duration; use tokio::sync::mpsc; -use types::blob_sidecar::FixedBlobSidecarList; use types::{BlobSidecar, EthSpec, Hash256, SignedBeaconBlock, Slot}; /// The number of slots ahead of us that is allowed before requesting a long-range (batch) Sync @@ -77,9 +76,8 @@ pub type Id = u32; #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] pub struct SingleLookupReqId { - pub id: Id, - pub req_counter: Id, - pub lookup_type: LookupType, + pub lookup_id: Id, + pub req_id: Id, } /// Id of rpc requests sent by sync to the network. @@ -153,7 +151,14 @@ pub enum SyncMessage { pub enum BlockProcessType { SingleBlock { id: Id }, SingleBlob { id: Id }, - ParentLookup { chain_hash: Hash256 }, +} + +impl BlockProcessType { + pub fn id(&self) -> Id { + match self { + BlockProcessType::SingleBlock { id } | BlockProcessType::SingleBlob { id } => *id, + } + } } #[derive(Debug)] @@ -254,27 +259,33 @@ impl SyncManager { ), range_sync: RangeSync::new(beacon_chain.clone(), log.clone()), backfill_sync: BackFillSync::new(beacon_chain.clone(), network_globals, log.clone()), - block_lookups: BlockLookups::new( - beacon_chain.data_availability_checker.clone(), - log.clone(), - ), + block_lookups: BlockLookups::new(log.clone()), log: log.clone(), } } #[cfg(test)] - pub(crate) fn active_single_lookups(&self) -> Vec { + pub(crate) fn active_single_lookups(&self) -> Vec<(Id, Hash256, Option)> { self.block_lookups.active_single_lookups() } #[cfg(test)] - pub(crate) fn active_parent_lookups(&self) -> Vec { - self.block_lookups.active_parent_lookups() + pub(crate) fn active_parent_lookups(&self) -> Vec> { + self.block_lookups + .active_parent_lookups() + .iter() + .map(|c| c.chain.clone()) + .collect() + } + + #[cfg(test)] + pub(crate) fn get_failed_chains(&mut self) -> Vec { + self.block_lookups.get_failed_chains() } #[cfg(test)] - pub(crate) fn failed_chains_contains(&mut self, chain_hash: &Hash256) -> bool { - self.block_lookups.failed_chains_contains(chain_hash) + pub(crate) fn insert_failed_chain(&mut self, block_root: Hash256) { + self.block_lookups.insert_failed_chain(block_root); } fn network_globals(&self) -> &NetworkGlobals { @@ -353,8 +364,7 @@ impl SyncManager { fn peer_disconnect(&mut self, peer_id: &PeerId) { self.range_sync.peer_disconnect(&mut self.network, peer_id); - self.block_lookups - .peer_disconnected(peer_id, &mut self.network); + self.block_lookups.peer_disconnected(peer_id); // Regardless of the outcome, we update the sync status. let _ = self .backfill_sync @@ -578,31 +588,33 @@ impl SyncManager { block_root, parent_root, block_slot, - block.into(), + BlockComponent::Block(DownloadResult { + value: block.block_cloned(), + block_root, + seen_timestamp: timestamp_now(), + peer_id, + }), ); } SyncMessage::UnknownParentBlob(peer_id, blob) => { let blob_slot = blob.slot(); let block_root = blob.block_root(); let parent_root = blob.block_parent_root(); - let blob_index = blob.index; - if blob_index >= T::EthSpec::max_blobs_per_block() as u64 { - warn!(self.log, "Peer sent blob with invalid index"; "index" => blob_index, "peer_id" => %peer_id); - return; - } - let mut blobs = FixedBlobSidecarList::default(); - *blobs.index_mut(blob_index as usize) = Some(blob); debug!(self.log, "Received unknown parent blob message"; "block_root" => %block_root, "parent_root" => %parent_root); self.handle_unknown_parent( peer_id, block_root, parent_root, blob_slot, - ChildComponents::new(block_root, None, Some(blobs)), + BlockComponent::Blob(DownloadResult { + value: blob, + block_root, + seen_timestamp: timestamp_now(), + peer_id, + }), ); } SyncMessage::UnknownBlockHashFromAttestation(peer_id, block_root) => { - debug!(self.log, "Received unknown block hash message"; "block_root" => %block_root); self.handle_unknown_block_root(peer_id, block_root); } SyncMessage::Disconnect(peer_id) => { @@ -617,25 +629,9 @@ impl SyncManager { SyncMessage::BlockComponentProcessed { process_type, result, - } => match process_type { - BlockProcessType::SingleBlock { id } => self - .block_lookups - .single_block_component_processed::( - id, - result, - &mut self.network, - ), - BlockProcessType::SingleBlob { id } => self - .block_lookups - .single_block_component_processed::>( - id, - result, - &mut self.network, - ), - BlockProcessType::ParentLookup { chain_hash } => self - .block_lookups - .parent_block_processed(chain_hash, result, &mut self.network), - }, + } => self + .block_lookups + .on_processing_result(process_type, result, &mut self.network), SyncMessage::BatchProcessed { sync_type, result } => match sync_type { ChainSegmentProcessId::RangeBatchId(chain_id, epoch) => { self.range_sync.handle_block_process_result( @@ -661,9 +657,6 @@ impl SyncManager { } } } - ChainSegmentProcessId::ParentLookup(chain_hash) => self - .block_lookups - .parent_chain_processed(chain_hash, result, &mut self.network), }, } } @@ -674,23 +667,16 @@ impl SyncManager { block_root: Hash256, parent_root: Hash256, slot: Slot, - child_components: ChildComponents, + block_component: BlockComponent, ) { match self.should_search_for_block(Some(slot), &peer_id) { Ok(_) => { - self.block_lookups.search_parent( - slot, + self.block_lookups.search_child_and_parent( block_root, - parent_root, + block_component, peer_id, &mut self.network, ); - self.block_lookups.search_child_block( - block_root, - child_components, - &[peer_id], - &mut self.network, - ); } Err(reason) => { debug!(self.log, "Ignoring unknown parent request"; "block_root" => %block_root, "parent_root" => %parent_root, "reason" => reason); @@ -702,7 +688,7 @@ impl SyncManager { match self.should_search_for_block(None, &peer_id) { Ok(_) => { self.block_lookups - .search_block(block_root, &[peer_id], &mut self.network); + .search_unknown_block(block_root, &[peer_id], &mut self.network); } Err(reason) => { debug!(self.log, "Ignoring unknown block request"; "block_root" => %block_root, "reason" => reason); @@ -774,11 +760,6 @@ impl SyncManager { let dropped_single_blocks_requests = self.block_lookups.drop_single_block_requests(); - // - Parent lookups: - // Disabled while in this state. We drop current requests and don't search for new - // blocks. - let dropped_parent_chain_requests = self.block_lookups.drop_parent_chain_requests(); - // - Range: // We still send found peers to range so that it can keep track of potential chains // with respect to our current peers. Range will stop processing batches in the @@ -787,10 +768,9 @@ impl SyncManager { // - Backfill: Not affected by ee states, nothing to do. // Some logs. - if dropped_single_blocks_requests > 0 || dropped_parent_chain_requests > 0 { + if dropped_single_blocks_requests > 0 { debug!(self.log, "Execution engine not online. Dropping active requests."; "dropped_single_blocks_requests" => dropped_single_blocks_requests, - "dropped_parent_chain_requests" => dropped_parent_chain_requests, ); } } @@ -829,46 +809,13 @@ impl SyncManager { block: RpcEvent>>, ) { if let Some(resp) = self.network.on_single_block_response(id, block) { - match resp { - Ok((block, seen_timestamp)) => match id.lookup_type { - LookupType::Current => self - .block_lookups - .single_lookup_response::( - id, - peer_id, - block, - seen_timestamp, - &mut self.network, - ), - LookupType::Parent => self - .block_lookups - .parent_lookup_response::( - id, - peer_id, - block, - seen_timestamp, - &mut self.network, - ), - }, - Err(error) => match id.lookup_type { - LookupType::Current => self - .block_lookups - .single_block_lookup_failed::( - id, - &peer_id, - &mut self.network, - error, - ), - LookupType::Parent => self - .block_lookups - .parent_lookup_failed::( - id, - &peer_id, - &mut self.network, - error, - ), - }, - } + self.block_lookups + .on_download_response::>( + id.lookup_id, + peer_id, + resp, + &mut self.network, + ) } } @@ -904,47 +851,13 @@ impl SyncManager { blob: RpcEvent>>, ) { if let Some(resp) = self.network.on_single_blob_response(id, blob) { - match resp { - Ok((blobs, seen_timestamp)) => match id.lookup_type { - LookupType::Current => self - .block_lookups - .single_lookup_response::>( - id, - peer_id, - blobs, - seen_timestamp, - &mut self.network, - ), - LookupType::Parent => self - .block_lookups - .parent_lookup_response::>( - id, - peer_id, - blobs, - seen_timestamp, - &mut self.network, - ), - }, - - Err(error) => match id.lookup_type { - LookupType::Current => self - .block_lookups - .single_block_lookup_failed::>( - id, - &peer_id, - &mut self.network, - error, - ), - LookupType::Parent => self - .block_lookups - .parent_lookup_failed::>( - id, - &peer_id, - &mut self.network, - error, - ), - }, - } + self.block_lookups + .on_download_response::>( + id.lookup_id, + peer_id, + resp, + &mut self.network, + ) } } diff --git a/beacon_node/network/src/sync/network_context.rs b/beacon_node/network/src/sync/network_context.rs index fc91270c1dc..88495a5b350 100644 --- a/beacon_node/network/src/sync/network_context.rs +++ b/beacon_node/network/src/sync/network_context.rs @@ -4,11 +4,12 @@ use self::requests::{ActiveBlobsByRootRequest, ActiveBlocksByRootRequest}; pub use self::requests::{BlobsByRootSingleBlockRequest, BlocksByRootSingleRequest}; use super::block_sidecar_coupling::BlocksAndBlobsRequestInfo; -use super::manager::{Id, RequestId as SyncRequestId}; +use super::manager::{BlockProcessType, Id, RequestId as SyncRequestId}; use super::range_sync::{BatchId, ByRangeRequestType, ChainId}; use crate::network_beacon_processor::NetworkBeaconProcessor; use crate::service::{NetworkMessage, RequestId}; use crate::status::ToStatusMessage; +use crate::sync::block_lookups::SingleLookupId; use crate::sync::manager::SingleLookupReqId; use beacon_chain::block_verification_types::RpcBlock; use beacon_chain::validator_monitor::timestamp_now; @@ -18,13 +19,13 @@ use lighthouse_network::rpc::methods::BlobsByRangeRequest; use lighthouse_network::rpc::{BlocksByRangeRequest, GoodbyeReason, RPCError}; use lighthouse_network::{Client, NetworkGlobals, PeerAction, PeerId, ReportSource, Request}; pub use requests::LookupVerifyError; -use slog::{debug, trace, warn}; +use slog::{debug, error, trace, warn}; use std::collections::hash_map::Entry; use std::sync::Arc; use std::time::Duration; use tokio::sync::mpsc; use types::blob_sidecar::FixedBlobSidecarList; -use types::{BlobSidecar, EthSpec, SignedBeaconBlock}; +use types::{BlobSidecar, EthSpec, Hash256, SignedBeaconBlock}; mod requests; @@ -52,7 +53,7 @@ pub enum RpcEvent { RPCError(RPCError), } -pub type RpcProcessingResult = Option>; +pub type RpcProcessingResult = Result<(T, Duration), LookupFailure>; pub enum LookupFailure { RpcError(RPCError), @@ -295,21 +296,41 @@ impl SyncNetworkContext { } } + /// Request block of `block_root` if necessary by checking: + /// - If the da_checker has a pending block from gossip or a previous request + /// + /// Returns false if no request was made, because the block is already imported pub fn block_lookup_request( &mut self, - id: SingleLookupReqId, + lookup_id: SingleLookupId, peer_id: PeerId, - request: BlocksByRootSingleRequest, - ) -> Result<(), &'static str> { + block_root: Hash256, + ) -> Result { + if self + .chain + .reqresp_pre_import_cache + .read() + .contains_key(&block_root) + { + return Ok(false); + } + + let id = SingleLookupReqId { + lookup_id, + req_id: self.next_id(), + }; + debug!( self.log, "Sending BlocksByRoot Request"; "method" => "BlocksByRoot", - "block_root" => ?request.0, + "block_root" => ?block_root, "peer" => %peer_id, "id" => ?id ); + let request = BlocksByRootSingleRequest(block_root); + self.send_network_msg(NetworkMessage::SendRequest { peer_id, request: Request::BlocksByRoot(request.into_request(&self.chain.spec)), @@ -319,25 +340,76 @@ impl SyncNetworkContext { self.blocks_by_root_requests .insert(id, ActiveBlocksByRootRequest::new(request)); - Ok(()) + Ok(true) } + /// Request necessary blobs for `block_root`. Requests only the necessary blobs by checking: + /// - If we have a downloaded but not yet processed block + /// - If the da_checker has a pending block + /// - If the da_checker has pending blobs from gossip + /// + /// Returns false if no request was made, because we don't need to import (more) blobs. pub fn blob_lookup_request( &mut self, - id: SingleLookupReqId, + lookup_id: SingleLookupId, peer_id: PeerId, - request: BlobsByRootSingleBlockRequest, - ) -> Result<(), &'static str> { + block_root: Hash256, + downloaded_block_expected_blobs: Option, + ) -> Result { + let expected_blobs = downloaded_block_expected_blobs + .or_else(|| { + self.chain + .data_availability_checker + .num_expected_blobs(&block_root) + }) + .unwrap_or_else(|| { + // If we don't about the block being requested, attempt to fetch all blobs + if self + .chain + .data_availability_checker + .da_check_required_for_current_epoch() + { + T::EthSpec::max_blobs_per_block() + } else { + 0 + } + }); + + let imported_blob_indexes = self + .chain + .data_availability_checker + .imported_blob_indexes(&block_root) + .unwrap_or_default(); + // Include only the blob indexes not yet imported (received through gossip) + let indices = (0..expected_blobs as u64) + .filter(|index| !imported_blob_indexes.contains(index)) + .collect::>(); + + if indices.is_empty() { + // No blobs required, do not issue any request + return Ok(false); + } + + let id = SingleLookupReqId { + lookup_id, + req_id: self.next_id(), + }; + debug!( self.log, "Sending BlobsByRoot Request"; "method" => "BlobsByRoot", - "block_root" => ?request.block_root, - "blob_indices" => ?request.indices, + "block_root" => ?block_root, + "blob_indices" => ?indices, "peer" => %peer_id, "id" => ?id ); + let request = BlobsByRootSingleBlockRequest { + block_root, + indices, + }; + self.send_network_msg(NetworkMessage::SendRequest { peer_id, request: Request::BlobsByRoot(request.clone().into_request(&self.chain.spec)), @@ -347,7 +419,7 @@ impl SyncNetworkContext { self.blobs_by_root_requests .insert(id, ActiveBlobsByRootRequest::new(request)); - Ok(()) + Ok(true) } pub fn is_execution_engine_online(&self) -> bool { @@ -458,7 +530,7 @@ impl SyncNetworkContext { &mut self, request_id: SingleLookupReqId, block: RpcEvent>>, - ) -> RpcProcessingResult>> { + ) -> Option>>> { let Entry::Occupied(mut request) = self.blocks_by_root_requests.entry(request_id) else { return None; }; @@ -489,7 +561,7 @@ impl SyncNetworkContext { &mut self, request_id: SingleLookupReqId, blob: RpcEvent>>, - ) -> RpcProcessingResult> { + ) -> Option>> { let Entry::Occupied(mut request) = self.blobs_by_root_requests.entry(request_id) else { return None; }; @@ -520,6 +592,69 @@ impl SyncNetworkContext { } }) } + + pub fn send_block_for_processing( + &self, + block_root: Hash256, + block: RpcBlock, + duration: Duration, + process_type: BlockProcessType, + ) -> Result<(), &'static str> { + match self.beacon_processor_if_enabled() { + Some(beacon_processor) => { + debug!(self.log, "Sending block for processing"; "block" => ?block_root, "process" => ?process_type); + if let Err(e) = beacon_processor.send_rpc_beacon_block( + block_root, + block, + duration, + process_type, + ) { + error!( + self.log, + "Failed to send sync block to processor"; + "error" => ?e + ); + Err("beacon processor send failure") + } else { + Ok(()) + } + } + None => { + trace!(self.log, "Dropping block ready for processing. Beacon processor not available"; "block" => %block_root); + Err("beacon processor unavailable") + } + } + } + + pub fn send_blobs_for_processing( + &self, + block_root: Hash256, + blobs: FixedBlobSidecarList, + duration: Duration, + process_type: BlockProcessType, + ) -> Result<(), &'static str> { + match self.beacon_processor_if_enabled() { + Some(beacon_processor) => { + debug!(self.log, "Sending blobs for processing"; "block" => ?block_root, "process_type" => ?process_type); + if let Err(e) = + beacon_processor.send_rpc_blobs(block_root, blobs, duration, process_type) + { + error!( + self.log, + "Failed to send sync blobs to processor"; + "error" => ?e + ); + Err("beacon processor send failure") + } else { + Ok(()) + } + } + None => { + trace!(self.log, "Dropping blobs ready for processing. Beacon processor not available"; "block_root" => %block_root); + Err("beacon processor unavailable") + } + } + } } fn to_fixed_blob_sidecar_list( diff --git a/beacon_node/src/cli.rs b/beacon_node/src/cli.rs index 818cdbd460f..81c2196b754 100644 --- a/beacon_node/src/cli.rs +++ b/beacon_node/src/cli.rs @@ -624,7 +624,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { Arg::with_name("state-cache-size") .long("state-cache-size") .value_name("STATE_CACHE_SIZE") - .help("Specifies the size of the snapshot cache [default: 3]") + .help("Specifies the size of the state cache [default: 128]") .takes_value(true) ) /* diff --git a/beacon_node/store/src/impls/execution_payload.rs b/beacon_node/store/src/impls/execution_payload.rs index a874031ca27..14fc10ad6de 100644 --- a/beacon_node/store/src/impls/execution_payload.rs +++ b/beacon_node/store/src/impls/execution_payload.rs @@ -1,8 +1,8 @@ use crate::{DBColumn, Error, StoreItem}; use ssz::{Decode, Encode}; use types::{ - BlobSidecarList, EthSpec, ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, - ExecutionPayloadElectra, ExecutionPayloadMerge, + BlobSidecarList, EthSpec, ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, + ExecutionPayloadDeneb, ExecutionPayloadElectra, }; macro_rules! impl_store_item { @@ -22,7 +22,7 @@ macro_rules! impl_store_item { } }; } -impl_store_item!(ExecutionPayloadMerge); +impl_store_item!(ExecutionPayloadBellatrix); impl_store_item!(ExecutionPayloadCapella); impl_store_item!(ExecutionPayloadDeneb); impl_store_item!(ExecutionPayloadElectra); @@ -51,7 +51,8 @@ impl StoreItem for ExecutionPayload { ExecutionPayloadCapella::from_ssz_bytes(bytes) .map(Self::Capella) .or_else(|_| { - ExecutionPayloadMerge::from_ssz_bytes(bytes).map(Self::Merge) + ExecutionPayloadBellatrix::from_ssz_bytes(bytes) + .map(Self::Bellatrix) }) }) }) diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index 25438fc7e0a..e56d0580ac2 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -14,7 +14,7 @@ use types::*; /// /// Utilises lazy-loading from separate storage for its vector fields. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb, Electra), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), variant_attributes(derive(Debug, PartialEq, Clone, Encode, Decode)) )] #[derive(Debug, PartialEq, Clone, Encode)] @@ -66,9 +66,9 @@ where pub current_epoch_attestations: List, E::MaxPendingAttestations>, // Participation (Altair and later) - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] pub previous_epoch_participation: List, - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] pub current_epoch_participation: List, // Finality @@ -78,21 +78,21 @@ where pub finalized_checkpoint: Checkpoint, // Inactivity - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] pub inactivity_scores: List, // Light-client sync committees - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] pub current_sync_committee: Arc>, - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] pub next_sync_committee: Arc>, // Execution #[superstruct( - only(Merge), - partial_getter(rename = "latest_execution_payload_header_merge") + only(Bellatrix), + partial_getter(rename = "latest_execution_payload_header_bellatrix") )] - pub latest_execution_payload_header: ExecutionPayloadHeaderMerge, + pub latest_execution_payload_header: ExecutionPayloadHeaderBellatrix, #[superstruct( only(Capella), partial_getter(rename = "latest_execution_payload_header_capella") @@ -118,6 +118,29 @@ where #[ssz(skip_serializing, skip_deserializing)] #[superstruct(only(Capella, Deneb, Electra))] pub historical_summaries: Option>, + + // Electra + #[superstruct(only(Electra))] + pub deposit_receipts_start_index: u64, + #[superstruct(only(Electra))] + pub deposit_balance_to_consume: u64, + #[superstruct(only(Electra))] + pub exit_balance_to_consume: u64, + #[superstruct(only(Electra))] + pub earliest_exit_epoch: Epoch, + #[superstruct(only(Electra))] + pub consolidation_balance_to_consume: u64, + #[superstruct(only(Electra))] + pub earliest_consolidation_epoch: Epoch, + + // TODO(electra) should these be optional? + #[superstruct(only(Electra))] + pub pending_balance_deposits: List, + #[superstruct(only(Electra))] + pub pending_partial_withdrawals: + List, + #[superstruct(only(Electra))] + pub pending_consolidations: List, } /// Implement the conversion function from BeaconState -> PartialBeaconState. @@ -199,11 +222,11 @@ impl PartialBeaconState { ], [] ), - BeaconState::Merge(s) => impl_from_state_forgetful!( + BeaconState::Bellatrix(s) => impl_from_state_forgetful!( s, outer, - Merge, - PartialBeaconStateMerge, + Bellatrix, + PartialBeaconStateBellatrix, [ previous_epoch_participation, current_epoch_participation, @@ -261,7 +284,16 @@ impl PartialBeaconState { inactivity_scores, latest_execution_payload_header, next_withdrawal_index, - next_withdrawal_validator_index + next_withdrawal_validator_index, + deposit_receipts_start_index, + deposit_balance_to_consume, + exit_balance_to_consume, + earliest_exit_epoch, + consolidation_balance_to_consume, + earliest_consolidation_epoch, + pending_balance_deposits, + pending_partial_withdrawals, + pending_consolidations ], [historical_summaries] ), @@ -467,10 +499,10 @@ impl TryInto> for PartialBeaconState { ], [] ), - PartialBeaconState::Merge(inner) => impl_try_into_beacon_state!( + PartialBeaconState::Bellatrix(inner) => impl_try_into_beacon_state!( inner, - Merge, - BeaconStateMerge, + Bellatrix, + BeaconStateBellatrix, [ previous_epoch_participation, current_epoch_participation, @@ -525,7 +557,16 @@ impl TryInto> for PartialBeaconState { inactivity_scores, latest_execution_payload_header, next_withdrawal_index, - next_withdrawal_validator_index + next_withdrawal_validator_index, + deposit_receipts_start_index, + deposit_balance_to_consume, + exit_balance_to_consume, + earliest_exit_epoch, + consolidation_balance_to_consume, + earliest_consolidation_epoch, + pending_balance_deposits, + pending_partial_withdrawals, + pending_consolidations ], [historical_summaries] ), diff --git a/book/src/help_bn.md b/book/src/help_bn.md index e437925a0e8..efdc7114b79 100644 --- a/book/src/help_bn.md +++ b/book/src/help_bn.md @@ -438,7 +438,7 @@ OPTIONS: Specifies how often a freezer DB restore point should be stored. Cannot be changed after initialization. [default: 8192 (mainnet) or 64 (minimal)] --state-cache-size - Specifies the size of the snapshot cache [default: 3] + Specifies the size of the state cache [default: 128] --suggested-fee-recipient Emergency fallback fee recipient for use in case the validator client does not have one configured. You diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index feff1d391a9..b15246e7fdb 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -1023,7 +1023,7 @@ impl ForkVersionDeserialize for SsePayloadAttributes { fork_name: ForkName, ) -> Result { match fork_name { - ForkName::Merge => serde_json::from_value(value) + ForkName::Bellatrix => serde_json::from_value(value) .map(Self::V1) .map_err(serde::de::Error::custom), ForkName::Capella => serde_json::from_value(value) @@ -1080,6 +1080,8 @@ pub enum EventKind { #[cfg(feature = "lighthouse")] BlockReward(BlockReward), PayloadAttributes(VersionedSsePayloadAttributes), + ProposerSlashing(Box), + AttesterSlashing(Box>), } impl EventKind { @@ -1099,6 +1101,8 @@ impl EventKind { EventKind::LightClientOptimisticUpdate(_) => "light_client_optimistic_update", #[cfg(feature = "lighthouse")] EventKind::BlockReward(_) => "block_reward", + EventKind::ProposerSlashing(_) => "proposer_slashing", + EventKind::AttesterSlashing(_) => "attester_slashing", } } @@ -1179,6 +1183,16 @@ impl EventKind { "block_reward" => Ok(EventKind::BlockReward(serde_json::from_str(data).map_err( |e| ServerError::InvalidServerSentEvent(format!("Block Reward: {:?}", e)), )?)), + "attester_slashing" => Ok(EventKind::AttesterSlashing( + serde_json::from_str(data).map_err(|e| { + ServerError::InvalidServerSentEvent(format!("Attester Slashing: {:?}", e)) + })?, + )), + "proposer_slashing" => Ok(EventKind::ProposerSlashing( + serde_json::from_str(data).map_err(|e| { + ServerError::InvalidServerSentEvent(format!("Proposer Slashing: {:?}", e)) + })?, + )), _ => Err(ServerError::InvalidServerSentEvent( "Could not parse event tag".to_string(), )), @@ -1210,6 +1224,8 @@ pub enum EventTopic { LightClientOptimisticUpdate, #[cfg(feature = "lighthouse")] BlockReward, + AttesterSlashing, + ProposerSlashing, } impl FromStr for EventTopic { @@ -1231,6 +1247,8 @@ impl FromStr for EventTopic { "light_client_optimistic_update" => Ok(EventTopic::LightClientOptimisticUpdate), #[cfg(feature = "lighthouse")] "block_reward" => Ok(EventTopic::BlockReward), + "attester_slashing" => Ok(EventTopic::AttesterSlashing), + "proposer_slashing" => Ok(EventTopic::ProposerSlashing), _ => Err("event topic cannot be parsed.".to_string()), } } @@ -1253,6 +1271,8 @@ impl fmt::Display for EventTopic { EventTopic::LightClientOptimisticUpdate => write!(f, "light_client_optimistic_update"), #[cfg(feature = "lighthouse")] EventTopic::BlockReward => write!(f, "block_reward"), + EventTopic::AttesterSlashing => write!(f, "attester_slashing"), + EventTopic::ProposerSlashing => write!(f, "proposer_slashing"), } } } @@ -1598,7 +1618,7 @@ impl FullBlockContents { fork_name: ForkName, ) -> Result { match fork_name { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { BeaconBlock::from_ssz_bytes_for_fork(bytes, fork_name) .map(|block| FullBlockContents::Block(block)) } @@ -1658,7 +1678,7 @@ impl ForkVersionDeserialize for FullBlockContents { fork_name: ForkName, ) -> Result { match fork_name { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { Ok(FullBlockContents::Block( BeaconBlock::deserialize_by_fork::<'de, D>(value, fork_name)?, )) @@ -1758,7 +1778,7 @@ impl PublishBlockRequest { /// SSZ decode with fork variant determined by `fork_name`. pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result { match fork_name { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { SignedBeaconBlock::from_ssz_bytes_for_fork(bytes, fork_name) .map(|block| PublishBlockRequest::Block(Arc::new(block))) } @@ -1844,7 +1864,7 @@ impl TryFrom>> for PublishBlockRequest { match *block { SignedBeaconBlock::Base(_) | SignedBeaconBlock::Altair(_) - | SignedBeaconBlock::Merge(_) + | SignedBeaconBlock::Bellatrix(_) | SignedBeaconBlock::Capella(_) => Ok(PublishBlockRequest::Block(block)), SignedBeaconBlock::Deneb(_) | SignedBeaconBlock::Electra(_) => Err( "post-Deneb block contents cannot be fully constructed from just the signed block", @@ -1953,7 +1973,7 @@ impl ForkVersionDeserialize for FullPayloadContents { fork_name: ForkName, ) -> Result { match fork_name { - ForkName::Merge | ForkName::Capella => serde_json::from_value(value) + ForkName::Bellatrix | ForkName::Capella => serde_json::from_value(value) .map(Self::Payload) .map_err(serde::de::Error::custom), ForkName::Deneb | ForkName::Electra => serde_json::from_value(value) diff --git a/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml b/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml index 27fb81a5139..50a5fcc3a50 100644 --- a/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml @@ -33,7 +33,7 @@ GENESIS_DELAY: 6000 # Altair ALTAIR_FORK_VERSION: 0x01000064 ALTAIR_FORK_EPOCH: 512 -# Merge +# Bellatrix BELLATRIX_FORK_VERSION: 0x02000064 BELLATRIX_FORK_EPOCH: 385536 # Capella diff --git a/common/eth2_network_config/built_in_network_configs/holesky/config.yaml b/common/eth2_network_config/built_in_network_configs/holesky/config.yaml index bd384cfe497..6a399b957d2 100644 --- a/common/eth2_network_config/built_in_network_configs/holesky/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/holesky/config.yaml @@ -22,7 +22,7 @@ GENESIS_DELAY: 300 # Altair ALTAIR_FORK_VERSION: 0x02017000 ALTAIR_FORK_EPOCH: 0 -# Merge +# Bellatrix BELLATRIX_FORK_VERSION: 0x03017000 BELLATRIX_FORK_EPOCH: 0 TERMINAL_TOTAL_DIFFICULTY: 0 diff --git a/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml b/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml index 2df40798c11..72a48679118 100644 --- a/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml @@ -21,7 +21,7 @@ GENESIS_DELAY: 86400 ALTAIR_FORK_VERSION: 0x90000070 ALTAIR_FORK_EPOCH: 50 -# Merge +# Bellatrix BELLATRIX_FORK_VERSION: 0x90000071 BELLATRIX_FORK_EPOCH: 100 TERMINAL_TOTAL_DIFFICULTY: 17000000000000000 diff --git a/common/logging/src/lib.rs b/common/logging/src/lib.rs index 3a5a5209b02..b0e1da00e97 100644 --- a/common/logging/src/lib.rs +++ b/common/logging/src/lib.rs @@ -256,14 +256,14 @@ pub fn create_tracing_layer(base_tracing_log_path: PathBuf) { return; }; - let (libp2p_non_blocking_writer, libp2p_guard) = NonBlocking::new(libp2p_writer); - let (discv5_non_blocking_writer, discv5_guard) = NonBlocking::new(discv5_writer); + let (libp2p_non_blocking_writer, _libp2p_guard) = NonBlocking::new(libp2p_writer); + let (discv5_non_blocking_writer, _discv5_guard) = NonBlocking::new(discv5_writer); let custom_layer = LoggingLayer { libp2p_non_blocking_writer, - libp2p_guard, + _libp2p_guard, discv5_non_blocking_writer, - discv5_guard, + _discv5_guard, }; if let Err(e) = tracing_subscriber::fmt() diff --git a/common/logging/src/tracing_logging_layer.rs b/common/logging/src/tracing_logging_layer.rs index aabb6ddd0c9..a9ddae828ae 100644 --- a/common/logging/src/tracing_logging_layer.rs +++ b/common/logging/src/tracing_logging_layer.rs @@ -7,9 +7,9 @@ use tracing_subscriber::Layer; pub struct LoggingLayer { pub libp2p_non_blocking_writer: NonBlocking, - pub libp2p_guard: WorkerGuard, + pub _libp2p_guard: WorkerGuard, pub discv5_non_blocking_writer: NonBlocking, - pub discv5_guard: WorkerGuard, + pub _discv5_guard: WorkerGuard, } impl Layer for LoggingLayer diff --git a/common/lru_cache/src/time.rs b/common/lru_cache/src/time.rs index 0b2fd835687..890bf47eb44 100644 --- a/common/lru_cache/src/time.rs +++ b/common/lru_cache/src/time.rs @@ -166,6 +166,12 @@ where self.map.contains(key) } + /// List known keys + pub fn keys(&mut self) -> impl Iterator { + self.update(); + self.map.iter() + } + /// Shrink the mappings to fit the current size. pub fn shrink_to_fit(&mut self) { self.map.shrink_to_fit(); diff --git a/common/slot_clock/src/lib.rs b/common/slot_clock/src/lib.rs index 6bf74645000..4f54b2ee76b 100644 --- a/common/slot_clock/src/lib.rs +++ b/common/slot_clock/src/lib.rs @@ -11,7 +11,7 @@ pub use crate::manual_slot_clock::ManualSlotClock as TestingSlotClock; pub use crate::manual_slot_clock::ManualSlotClock; pub use crate::system_time_slot_clock::SystemTimeSlotClock; pub use metrics::scrape_for_metrics; -use types::consts::merge::INTERVALS_PER_SLOT; +use types::consts::bellatrix::INTERVALS_PER_SLOT; pub use types::Slot; /// A clock that reports the current slot. diff --git a/consensus/fork_choice/src/fork_choice.rs b/consensus/fork_choice/src/fork_choice.rs index 147fdb01d02..c55219a6761 100644 --- a/consensus/fork_choice/src/fork_choice.rs +++ b/consensus/fork_choice/src/fork_choice.rs @@ -13,7 +13,7 @@ use std::collections::BTreeSet; use std::marker::PhantomData; use std::time::Duration; use types::{ - consts::merge::INTERVALS_PER_SLOT, AbstractExecPayload, AttestationShufflingId, + consts::bellatrix::INTERVALS_PER_SLOT, AbstractExecPayload, AttestationShufflingId, AttesterSlashingRef, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, Checkpoint, Epoch, EthSpec, ExecPayload, ExecutionBlockHash, Hash256, IndexedAttestationRef, RelativeEpoch, SignedBeaconBlock, Slot, @@ -751,7 +751,7 @@ where BeaconBlockRef::Electra(_) | BeaconBlockRef::Deneb(_) | BeaconBlockRef::Capella(_) - | BeaconBlockRef::Merge(_) + | BeaconBlockRef::Bellatrix(_) | BeaconBlockRef::Altair(_) => { // NOTE: Processing justification & finalization requires the progressive // balances cache, but we cannot initialize it here as we only have an diff --git a/consensus/state_processing/src/common/get_attestation_participation.rs b/consensus/state_processing/src/common/get_attestation_participation.rs index d27a00c3826..fc09dad1f4e 100644 --- a/consensus/state_processing/src/common/get_attestation_participation.rs +++ b/consensus/state_processing/src/common/get_attestation_participation.rs @@ -47,7 +47,7 @@ pub fn get_attestation_participation_flag_indices( match state { &BeaconState::Base(_) | &BeaconState::Altair(_) - | &BeaconState::Merge(_) + | &BeaconState::Bellatrix(_) | &BeaconState::Capella(_) => { if is_matching_target && inclusion_delay <= E::slots_per_epoch() { participation_flag_indices.push(TIMELY_TARGET_FLAG_INDEX); diff --git a/consensus/state_processing/src/common/slash_validator.rs b/consensus/state_processing/src/common/slash_validator.rs index 16b4e74ece9..520b58a8af3 100644 --- a/consensus/state_processing/src/common/slash_validator.rs +++ b/consensus/state_processing/src/common/slash_validator.rs @@ -58,7 +58,7 @@ pub fn slash_validator( let proposer_reward = match state { BeaconState::Base(_) => whistleblower_reward.safe_div(spec.proposer_reward_quotient)?, BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => whistleblower_reward diff --git a/consensus/state_processing/src/genesis.rs b/consensus/state_processing/src/genesis.rs index 5ab9a99a3b1..a84f359389c 100644 --- a/consensus/state_processing/src/genesis.rs +++ b/consensus/state_processing/src/genesis.rs @@ -63,7 +63,7 @@ pub fn initialize_beacon_state_from_eth1( .bellatrix_fork_epoch .map_or(false, |fork_epoch| fork_epoch == E::genesis_epoch()) { - // this will set state.latest_execution_payload_header = ExecutionPayloadHeaderMerge::default() + // this will set state.latest_execution_payload_header = ExecutionPayloadHeaderBellatrix::default() upgrade_to_bellatrix(&mut state, spec)?; // Remove intermediate Altair fork from `state.fork`. @@ -71,8 +71,8 @@ pub fn initialize_beacon_state_from_eth1( // Override latest execution payload header. // See https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/bellatrix/beacon-chain.md#testing - if let Some(ExecutionPayloadHeader::Merge(ref header)) = execution_payload_header { - *state.latest_execution_payload_header_merge_mut()? = header.clone(); + if let Some(ExecutionPayloadHeader::Bellatrix(ref header)) = execution_payload_header { + *state.latest_execution_payload_header_bellatrix_mut()? = header.clone(); } } diff --git a/consensus/state_processing/src/metrics.rs b/consensus/state_processing/src/metrics.rs index e163f3b76b8..ac5c0f659cd 100644 --- a/consensus/state_processing/src/metrics.rs +++ b/consensus/state_processing/src/metrics.rs @@ -17,6 +17,10 @@ lazy_static! { "beacon_participation_prev_epoch_source_attesting_gwei_total", "Total effective balance (gwei) of validators who attested to the source in the previous epoch" ); + pub static ref PARTICIPATION_CURRENT_EPOCH_TOTAL_ACTIVE_GWEI_TOTAL: Result = try_create_int_gauge( + "beacon_participation_current_epoch_active_gwei_total", + "Total effective balance (gwei) of validators who are active in the current epoch" + ); /* * Processing metrics */ diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index a8447b7714a..2efa1218829 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -418,9 +418,9 @@ pub fn process_execution_payload>( partially_verify_execution_payload::(state, state.slot(), body, spec)?; let payload = body.execution_payload()?; match state.latest_execution_payload_header_mut()? { - ExecutionPayloadHeaderRefMut::Merge(header_mut) => { + ExecutionPayloadHeaderRefMut::Bellatrix(header_mut) => { match payload.to_execution_payload_header() { - ExecutionPayloadHeader::Merge(header) => *header_mut = header, + ExecutionPayloadHeader::Bellatrix(header) => *header_mut = header, _ => return Err(BlockProcessingError::IncorrectStateType), } } @@ -449,14 +449,14 @@ pub fn process_execution_payload>( /// These functions will definitely be called before the merge. Their entire purpose is to check if /// the merge has happened or if we're on the transition block. Thus we don't want to propagate -/// errors from the `BeaconState` being an earlier variant than `BeaconStateMerge` as we'd have to +/// errors from the `BeaconState` being an earlier variant than `BeaconStateBellatrix` as we'd have to /// repeatedly write code to treat these errors as false. /// https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#is_merge_transition_complete pub fn is_merge_transition_complete(state: &BeaconState) -> bool { match state { // We must check defaultness against the payload header with 0x0 roots, as that's what's meant // by `ExecutionPayloadHeader()` in the spec. - BeaconState::Merge(_) => state + BeaconState::Bellatrix(_) => state .latest_execution_payload_header() .map(|header| !header.is_default_with_zero_roots()) .unwrap_or(false), @@ -557,7 +557,7 @@ pub fn process_withdrawals>( spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { match state { - BeaconState::Merge(_) => Ok(()), + BeaconState::Bellatrix(_) => Ok(()), BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => { let expected_withdrawals = get_expected_withdrawals(state, spec)?; let expected_root = expected_withdrawals.tree_hash_root(); diff --git a/consensus/state_processing/src/per_block_processing/process_operations.rs b/consensus/state_processing/src/per_block_processing/process_operations.rs index fdccf5af205..9b4e9d71394 100644 --- a/consensus/state_processing/src/per_block_processing/process_operations.rs +++ b/consensus/state_processing/src/per_block_processing/process_operations.rs @@ -286,7 +286,7 @@ pub fn process_attestations>( )?; } BeaconBlockBodyRef::Altair(_) - | BeaconBlockBodyRef::Merge(_) + | BeaconBlockBodyRef::Bellatrix(_) | BeaconBlockBodyRef::Capella(_) | BeaconBlockBodyRef::Deneb(_) | BeaconBlockBodyRef::Electra(_) => { diff --git a/consensus/state_processing/src/per_block_processing/signature_sets.rs b/consensus/state_processing/src/per_block_processing/signature_sets.rs index f19714dc193..9885daaab0a 100644 --- a/consensus/state_processing/src/per_block_processing/signature_sets.rs +++ b/consensus/state_processing/src/per_block_processing/signature_sets.rs @@ -390,7 +390,7 @@ where let domain = match state { BeaconState::Base(_) | BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) => spec.get_domain( exit.epoch, Domain::VoluntaryExit, diff --git a/consensus/state_processing/src/per_block_processing/verify_attestation.rs b/consensus/state_processing/src/per_block_processing/verify_attestation.rs index df9bbc855c1..6bfb5d7cfe7 100644 --- a/consensus/state_processing/src/per_block_processing/verify_attestation.rs +++ b/consensus/state_processing/src/per_block_processing/verify_attestation.rs @@ -35,7 +35,7 @@ pub fn verify_attestation_for_block_inclusion<'ctxt, E: EthSpec>( match state { BeaconState::Base(_) | BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) => { verify!( state.slot() <= data.slot.safe_add(E::slots_per_epoch())?, diff --git a/consensus/state_processing/src/per_epoch_processing.rs b/consensus/state_processing/src/per_epoch_processing.rs index b51aa23f370..55e8853f3f8 100644 --- a/consensus/state_processing/src/per_epoch_processing.rs +++ b/consensus/state_processing/src/per_epoch_processing.rs @@ -44,7 +44,7 @@ pub fn process_epoch( match state { BeaconState::Base(_) => base::process_epoch(state, spec), BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => altair::process_epoch(state, spec), diff --git a/consensus/state_processing/src/per_epoch_processing/epoch_processing_summary.rs b/consensus/state_processing/src/per_epoch_processing/epoch_processing_summary.rs index 6f48050e161..952ab3f6498 100644 --- a/consensus/state_processing/src/per_epoch_processing/epoch_processing_summary.rs +++ b/consensus/state_processing/src/per_epoch_processing/epoch_processing_summary.rs @@ -100,6 +100,10 @@ impl EpochProcessingSummary { &metrics::PARTICIPATION_PREV_EPOCH_SOURCE_ATTESTING_GWEI_TOTAL, self.previous_epoch_source_attesting_balance()? as i64, ); + metrics::set_gauge( + &metrics::PARTICIPATION_CURRENT_EPOCH_TOTAL_ACTIVE_GWEI_TOTAL, + self.current_epoch_total_active_balance() as i64, + ); Ok(()) } diff --git a/consensus/state_processing/src/per_slot_processing.rs b/consensus/state_processing/src/per_slot_processing.rs index cc28340962a..6554423199f 100644 --- a/consensus/state_processing/src/per_slot_processing.rs +++ b/consensus/state_processing/src/per_slot_processing.rs @@ -54,7 +54,7 @@ pub fn per_slot_processing( if spec.altair_fork_epoch == Some(state.current_epoch()) { upgrade_to_altair(state, spec)?; } - // If the Merge fork epoch is reached, perform an irregular state upgrade. + // If the Bellatrix fork epoch is reached, perform an irregular state upgrade. if spec.bellatrix_fork_epoch == Some(state.current_epoch()) { upgrade_to_bellatrix(state, spec)?; } diff --git a/consensus/state_processing/src/upgrade.rs b/consensus/state_processing/src/upgrade.rs index 98602c66ba3..93cafa73d03 100644 --- a/consensus/state_processing/src/upgrade.rs +++ b/consensus/state_processing/src/upgrade.rs @@ -1,11 +1,11 @@ pub mod altair; +pub mod bellatrix; pub mod capella; pub mod deneb; pub mod electra; -pub mod merge; pub use altair::upgrade_to_altair; +pub use bellatrix::upgrade_to_bellatrix; pub use capella::upgrade_to_capella; pub use deneb::upgrade_to_deneb; pub use electra::upgrade_to_electra; -pub use merge::upgrade_to_bellatrix; diff --git a/consensus/state_processing/src/upgrade/merge.rs b/consensus/state_processing/src/upgrade/bellatrix.rs similarity index 90% rename from consensus/state_processing/src/upgrade/merge.rs rename to consensus/state_processing/src/upgrade/bellatrix.rs index 02705743ceb..f23e571cd12 100644 --- a/consensus/state_processing/src/upgrade/merge.rs +++ b/consensus/state_processing/src/upgrade/bellatrix.rs @@ -1,10 +1,10 @@ use std::mem; use types::{ - BeaconState, BeaconStateError as Error, BeaconStateMerge, ChainSpec, EpochCache, EthSpec, - ExecutionPayloadHeaderMerge, Fork, + BeaconState, BeaconStateBellatrix, BeaconStateError as Error, ChainSpec, EpochCache, EthSpec, + ExecutionPayloadHeaderBellatrix, Fork, }; -/// Transform a `Altair` state into an `Merge` state. +/// Transform a `Altair` state into an `Bellatrix` state. pub fn upgrade_to_bellatrix( pre_state: &mut BeaconState, spec: &ChainSpec, @@ -17,7 +17,7 @@ pub fn upgrade_to_bellatrix( // // Fixed size vectors get cloned because replacing them would require the same size // allocation as cloning. - let post = BeaconState::Merge(BeaconStateMerge { + let post = BeaconState::Bellatrix(BeaconStateBellatrix { // Versioning genesis_time: pre.genesis_time, genesis_validators_root: pre.genesis_validators_root, @@ -57,7 +57,7 @@ pub fn upgrade_to_bellatrix( current_sync_committee: pre.current_sync_committee.clone(), next_sync_committee: pre.next_sync_committee.clone(), // Execution - latest_execution_payload_header: >::default(), + latest_execution_payload_header: >::default(), // Caches total_active_balance: pre.total_active_balance, progressive_balances_cache: mem::take(&mut pre.progressive_balances_cache), diff --git a/consensus/state_processing/src/upgrade/capella.rs b/consensus/state_processing/src/upgrade/capella.rs index 51e29d10f3c..ae0dbde7678 100644 --- a/consensus/state_processing/src/upgrade/capella.rs +++ b/consensus/state_processing/src/upgrade/capella.rs @@ -4,13 +4,13 @@ use types::{ Fork, List, }; -/// Transform a `Merge` state into an `Capella` state. +/// Transform a `Bellatrix` state into an `Capella` state. pub fn upgrade_to_capella( pre_state: &mut BeaconState, spec: &ChainSpec, ) -> Result<(), Error> { let epoch = pre_state.current_epoch(); - let pre = pre_state.as_merge_mut()?; + let pre = pre_state.as_bellatrix_mut()?; // Where possible, use something like `mem::take` to move fields from behind the &mut // reference. For other fields that don't have a good default value, use `clone`. diff --git a/consensus/state_processing/src/upgrade/electra.rs b/consensus/state_processing/src/upgrade/electra.rs index f64228f050b..1e60bf488db 100644 --- a/consensus/state_processing/src/upgrade/electra.rs +++ b/consensus/state_processing/src/upgrade/electra.rs @@ -1,3 +1,4 @@ +use safe_arith::SafeArith; use std::mem; use types::{ BeaconState, BeaconStateElectra, BeaconStateError as Error, ChainSpec, EpochCache, EthSpec, @@ -10,14 +11,28 @@ pub fn upgrade_to_electra( spec: &ChainSpec, ) -> Result<(), Error> { let epoch = pre_state.current_epoch(); - let pre = pre_state.as_deneb_mut()?; + let earliest_exit_epoch = pre_state + .validators() + .iter() + .filter(|v| v.exit_epoch != spec.far_future_epoch) + .map(|v| v.exit_epoch) + .max() + .unwrap_or(epoch) + .safe_add(1)?; + + // The total active balance cache must be built before the consolidation churn limit + // is calculated. + pre_state.build_total_active_balance_cache(spec)?; + let earliest_consolidation_epoch = spec.compute_activation_exit_epoch(epoch)?; + + let pre = pre_state.as_deneb_mut()?; // Where possible, use something like `mem::take` to move fields from behind the &mut // reference. For other fields that don't have a good default value, use `clone`. // // Fixed size vectors get cloned because replacing them would require the same size // allocation as cloning. - let post = BeaconState::Electra(BeaconStateElectra { + let mut post = BeaconState::Electra(BeaconStateElectra { // Versioning genesis_time: pre.genesis_time, genesis_validators_root: pre.genesis_validators_root, @@ -62,6 +77,16 @@ pub fn upgrade_to_electra( next_withdrawal_index: pre.next_withdrawal_index, next_withdrawal_validator_index: pre.next_withdrawal_validator_index, historical_summaries: pre.historical_summaries.clone(), + // Electra + deposit_receipts_start_index: spec.unset_deposit_receipts_start_index, + deposit_balance_to_consume: 0, + exit_balance_to_consume: 0, + earliest_exit_epoch, + consolidation_balance_to_consume: 0, + earliest_consolidation_epoch, + pending_balance_deposits: Default::default(), + pending_partial_withdrawals: Default::default(), + pending_consolidations: Default::default(), // Caches total_active_balance: pre.total_active_balance, progressive_balances_cache: mem::take(&mut pre.progressive_balances_cache), @@ -71,6 +96,39 @@ pub fn upgrade_to_electra( slashings_cache: mem::take(&mut pre.slashings_cache), epoch_cache: EpochCache::default(), }); + *post.exit_balance_to_consume_mut()? = post.get_activation_exit_churn_limit(spec)?; + *post.consolidation_balance_to_consume_mut()? = post.get_consolidation_churn_limit(spec)?; + + // Add validators that are not yet active to pending balance deposits + let validators = post.validators().clone(); + let mut pre_activation = validators + .iter() + .enumerate() + .filter(|(_, validator)| validator.activation_epoch == spec.far_future_epoch) + .collect::>(); + + // Sort the indices by activation_eligibility_epoch and then by index + pre_activation.sort_by(|(index_a, val_a), (index_b, val_b)| { + if val_a.activation_eligibility_epoch == val_b.activation_eligibility_epoch { + index_a.cmp(index_b) + } else { + val_a + .activation_eligibility_epoch + .cmp(&val_b.activation_eligibility_epoch) + } + }); + + // Process validators to queue entire balance and reset them + for (index, _) in pre_activation { + post.queue_entire_balance_and_reset_validator(index, spec)?; + } + + // Ensure early adopters of compounding credentials go through the activation churn + for (index, validator) in validators.iter().enumerate() { + if validator.has_compounding_withdrawal_credential(spec) { + post.queue_excess_active_balance(index, spec)?; + } + } *pre_state = post; diff --git a/consensus/types/presets/gnosis/electra.yaml b/consensus/types/presets/gnosis/electra.yaml index cafdcbbf8d3..72c626ded2f 100644 --- a/consensus/types/presets/gnosis/electra.yaml +++ b/consensus/types/presets/gnosis/electra.yaml @@ -1,3 +1,45 @@ -# Gnosis preset - Electra +# Mainnet preset - Electra -ELECTRA_PLACEHOLDER: 0 +# Gwei values +# --------------------------------------------------------------- +# 2**5 * 10**9 (= 32,000,000,000) Gwei +MIN_ACTIVATION_BALANCE: 32000000000 +# 2**11 * 10**9 (= 2,048,000,000,000) Gwei +MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000 + +# State list lengths +# --------------------------------------------------------------- +# `uint64(2**27)` (= 134,217,728) +PENDING_BALANCE_DEPOSITS_LIMIT: 134217728 +# `uint64(2**27)` (= 134,217,728) +PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728 +# `uint64(2**18)` (= 262,144) +PENDING_CONSOLIDATIONS_LIMIT: 262144 + +# Reward and penalty quotients +# --------------------------------------------------------------- +# `uint64(2**12)` (= 4,096) +MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: 4096 +# `uint64(2**12)` (= 4,096) +WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: 4096 + +# # Max operations per block +# --------------------------------------------------------------- +# `uint64(2**0)` (= 1) +MAX_ATTESTER_SLASHINGS_ELECTRA: 1 +# `uint64(2**3)` (= 8) +MAX_ATTESTATIONS_ELECTRA: 8 +# `uint64(2**0)` (= 1) +MAX_CONSOLIDATIONS: 1 + +# Execution +# --------------------------------------------------------------- +# 2**13 (= 8192) receipts +MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192 +# 2**4 (= 16) withdrawal requests +MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 16 + +# Withdrawals processing +# --------------------------------------------------------------- +# 2**3 ( = 8) pending withdrawals +MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8 diff --git a/consensus/types/presets/mainnet/electra.yaml b/consensus/types/presets/mainnet/electra.yaml index 64d8b97b631..72c626ded2f 100644 --- a/consensus/types/presets/mainnet/electra.yaml +++ b/consensus/types/presets/mainnet/electra.yaml @@ -1,3 +1,45 @@ # Mainnet preset - Electra -ELECTRA_PLACEHOLDER: 0 +# Gwei values +# --------------------------------------------------------------- +# 2**5 * 10**9 (= 32,000,000,000) Gwei +MIN_ACTIVATION_BALANCE: 32000000000 +# 2**11 * 10**9 (= 2,048,000,000,000) Gwei +MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000 + +# State list lengths +# --------------------------------------------------------------- +# `uint64(2**27)` (= 134,217,728) +PENDING_BALANCE_DEPOSITS_LIMIT: 134217728 +# `uint64(2**27)` (= 134,217,728) +PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728 +# `uint64(2**18)` (= 262,144) +PENDING_CONSOLIDATIONS_LIMIT: 262144 + +# Reward and penalty quotients +# --------------------------------------------------------------- +# `uint64(2**12)` (= 4,096) +MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: 4096 +# `uint64(2**12)` (= 4,096) +WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: 4096 + +# # Max operations per block +# --------------------------------------------------------------- +# `uint64(2**0)` (= 1) +MAX_ATTESTER_SLASHINGS_ELECTRA: 1 +# `uint64(2**3)` (= 8) +MAX_ATTESTATIONS_ELECTRA: 8 +# `uint64(2**0)` (= 1) +MAX_CONSOLIDATIONS: 1 + +# Execution +# --------------------------------------------------------------- +# 2**13 (= 8192) receipts +MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192 +# 2**4 (= 16) withdrawal requests +MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 16 + +# Withdrawals processing +# --------------------------------------------------------------- +# 2**3 ( = 8) pending withdrawals +MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8 diff --git a/consensus/types/presets/minimal/electra.yaml b/consensus/types/presets/minimal/electra.yaml index 3baa7fa8161..11aa5e1f50e 100644 --- a/consensus/types/presets/minimal/electra.yaml +++ b/consensus/types/presets/minimal/electra.yaml @@ -1,3 +1,45 @@ # Minimal preset - Electra -ELECTRA_PLACEHOLDER: 0 +# Gwei values +# --------------------------------------------------------------- +# 2**5 * 10**9 (= 32,000,000,000) Gwei +MIN_ACTIVATION_BALANCE: 32000000000 +# 2**11 * 10**9 (= 2,048,000,000,000) Gwei +MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000 + +# State list lengths +# --------------------------------------------------------------- +# `uint64(2**27)` (= 134,217,728) +PENDING_BALANCE_DEPOSITS_LIMIT: 134217728 +# [customized] `uint64(2**6)` (= 64) +PENDING_PARTIAL_WITHDRAWALS_LIMIT: 64 +# [customized] `uint64(2**6)` (= 64) +PENDING_CONSOLIDATIONS_LIMIT: 64 + +# Reward and penalty quotients +# --------------------------------------------------------------- +# `uint64(2**12)` (= 4,096) +MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: 4096 +# `uint64(2**12)` (= 4,096) +WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: 4096 + +# # Max operations per block +# --------------------------------------------------------------- +# `uint64(2**0)` (= 1) +MAX_ATTESTER_SLASHINGS_ELECTRA: 1 +# `uint64(2**3)` (= 8) +MAX_ATTESTATIONS_ELECTRA: 8 +# `uint64(2**0)` (= 1) +MAX_CONSOLIDATIONS: 1 + +# Execution +# --------------------------------------------------------------- +# [customized] +MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4 +# [customized] 2**1 (= 2) withdrawal requests +MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 2 + +# Withdrawals processing +# --------------------------------------------------------------- +# 2**0 ( = 1) pending withdrawals +MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 1 diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/beacon_block.rs index 045cfb0ef5c..ac0525a3d84 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/beacon_block.rs @@ -15,7 +15,7 @@ use self::indexed_attestation::{IndexedAttestationBase, IndexedAttestationElectr /// A block of the `BeaconChain`. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb, Electra), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -66,8 +66,8 @@ pub struct BeaconBlock = FullPayload pub body: BeaconBlockBodyBase, #[superstruct(only(Altair), partial_getter(rename = "body_altair"))] pub body: BeaconBlockBodyAltair, - #[superstruct(only(Merge), partial_getter(rename = "body_merge"))] - pub body: BeaconBlockBodyMerge, + #[superstruct(only(Bellatrix), partial_getter(rename = "body_bellatrix"))] + pub body: BeaconBlockBodyBellatrix, #[superstruct(only(Capella), partial_getter(rename = "body_capella"))] pub body: BeaconBlockBodyCapella, #[superstruct(only(Deneb), partial_getter(rename = "body_deneb"))] @@ -133,7 +133,7 @@ impl> BeaconBlock { .map(BeaconBlock::Electra) .or_else(|_| BeaconBlockDeneb::from_ssz_bytes(bytes).map(BeaconBlock::Deneb)) .or_else(|_| BeaconBlockCapella::from_ssz_bytes(bytes).map(BeaconBlock::Capella)) - .or_else(|_| BeaconBlockMerge::from_ssz_bytes(bytes).map(BeaconBlock::Merge)) + .or_else(|_| BeaconBlockBellatrix::from_ssz_bytes(bytes).map(BeaconBlock::Bellatrix)) .or_else(|_| BeaconBlockAltair::from_ssz_bytes(bytes).map(BeaconBlock::Altair)) .or_else(|_| BeaconBlockBase::from_ssz_bytes(bytes).map(BeaconBlock::Base)) } @@ -224,7 +224,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockRef<'a, E, Payl match self { BeaconBlockRef::Base { .. } => ForkName::Base, BeaconBlockRef::Altair { .. } => ForkName::Altair, - BeaconBlockRef::Merge { .. } => ForkName::Merge, + BeaconBlockRef::Bellatrix { .. } => ForkName::Bellatrix, BeaconBlockRef::Capella { .. } => ForkName::Capella, BeaconBlockRef::Deneb { .. } => ForkName::Deneb, BeaconBlockRef::Electra { .. } => ForkName::Electra, @@ -469,15 +469,15 @@ impl> BeaconBlockAltair } } -impl> EmptyBlock for BeaconBlockMerge { - /// Returns an empty Merge block to be used during genesis. +impl> EmptyBlock for BeaconBlockBellatrix { + /// Returns an empty Bellatrix block to be used during genesis. fn empty(spec: &ChainSpec) -> Self { - BeaconBlockMerge { + BeaconBlockBellatrix { slot: spec.genesis_slot, proposer_index: 0, parent_root: Hash256::zero(), state_root: Hash256::zero(), - body: BeaconBlockBodyMerge { + body: BeaconBlockBodyBellatrix { randao_reveal: Signature::empty(), eth1_data: Eth1Data { deposit_root: Hash256::zero(), @@ -491,7 +491,7 @@ impl> EmptyBlock for BeaconBlockMerg deposits: VariableList::empty(), voluntary_exits: VariableList::empty(), sync_aggregate: SyncAggregate::empty(), - execution_payload: Payload::Merge::default(), + execution_payload: Payload::Bellatrix::default(), }, } } @@ -794,7 +794,7 @@ macro_rules! impl_from { impl_from!(BeaconBlockBase, >, >, |body: BeaconBlockBodyBase<_, _>| body.into()); impl_from!(BeaconBlockAltair, >, >, |body: BeaconBlockBodyAltair<_, _>| body.into()); -impl_from!(BeaconBlockMerge, >, >, |body: BeaconBlockBodyMerge<_, _>| body.into()); +impl_from!(BeaconBlockBellatrix, >, >, |body: BeaconBlockBodyBellatrix<_, _>| body.into()); impl_from!(BeaconBlockCapella, >, >, |body: BeaconBlockBodyCapella<_, _>| body.into()); impl_from!(BeaconBlockDeneb, >, >, |body: BeaconBlockBodyDeneb<_, _>| body.into()); impl_from!(BeaconBlockElectra, >, >, |body: BeaconBlockBodyElectra<_, _>| body.into()); @@ -827,7 +827,7 @@ macro_rules! impl_clone_as_blinded { impl_clone_as_blinded!(BeaconBlockBase, >, >); impl_clone_as_blinded!(BeaconBlockAltair, >, >); -impl_clone_as_blinded!(BeaconBlockMerge, >, >); +impl_clone_as_blinded!(BeaconBlockBellatrix, >, >); impl_clone_as_blinded!(BeaconBlockCapella, >, >); impl_clone_as_blinded!(BeaconBlockDeneb, >, >); impl_clone_as_blinded!(BeaconBlockElectra, >, >); @@ -1115,9 +1115,8 @@ mod tests { .expect("good electra block can be decoded"), good_block ); - // TODO(electra): once the Electra block is changed from Deneb, update this to match - // the other forks. - assert!(BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec).is_ok()); + BeaconBlock::from_ssz_bytes(&bad_block.as_ssz_bytes(), &spec) + .expect_err("bad electra block cannot be decoded"); } } } diff --git a/consensus/types/src/beacon_block_body.rs b/consensus/types/src/beacon_block_body.rs index 9fdffd0736a..fba542450de 100644 --- a/consensus/types/src/beacon_block_body.rs +++ b/consensus/types/src/beacon_block_body.rs @@ -29,7 +29,7 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11; /// /// This *superstruct* abstracts over the hard-fork. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb, Electra), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -64,7 +64,7 @@ pub struct BeaconBlockBody = FullPay pub graffiti: Graffiti, pub proposer_slashings: VariableList, #[superstruct( - only(Base, Altair, Merge, Capella, Deneb), + only(Base, Altair, Bellatrix, Capella, Deneb), partial_getter(rename = "attester_slashings_base") )] pub attester_slashings: VariableList, E::MaxAttesterSlashings>, @@ -72,7 +72,7 @@ pub struct BeaconBlockBody = FullPay pub attester_slashings: VariableList, E::MaxAttesterSlashingsElectra>, #[superstruct( - only(Base, Altair, Merge, Capella, Deneb), + only(Base, Altair, Bellatrix, Capella, Deneb), partial_getter(rename = "attestations_base") )] pub attestations: VariableList, E::MaxAttestations>, @@ -80,14 +80,17 @@ pub struct BeaconBlockBody = FullPay pub attestations: VariableList, E::MaxAttestationsElectra>, pub deposits: VariableList, pub voluntary_exits: VariableList, - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] pub sync_aggregate: SyncAggregate, // 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), partial_getter(rename = "execution_payload_merge"))] + #[superstruct( + only(Bellatrix), + partial_getter(rename = "execution_payload_bellatrix") + )] #[serde(flatten)] - pub execution_payload: Payload::Merge, + pub execution_payload: Payload::Bellatrix, #[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))] #[serde(flatten)] pub execution_payload: Payload::Capella, @@ -122,7 +125,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, pub fn execution_payload(&self) -> Result, Error> { match self { Self::Base(_) | Self::Altair(_) => Err(Error::IncorrectStateVariant), - Self::Merge(body) => Ok(Payload::Ref::from(&body.execution_payload)), + Self::Bellatrix(body) => Ok(Payload::Ref::from(&body.execution_payload)), Self::Capella(body) => Ok(Payload::Ref::from(&body.execution_payload)), Self::Deneb(body) => Ok(Payload::Ref::from(&body.execution_payload)), Self::Electra(body) => Ok(Payload::Ref::from(&body.execution_payload)), @@ -136,7 +139,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, index: usize, ) -> Result, Error> { match self { - Self::Base(_) | Self::Altair(_) | Self::Merge(_) | Self::Capella(_) => { + Self::Base(_) | Self::Altair(_) | Self::Bellatrix(_) | Self::Capella(_) => { Err(Error::IncorrectStateVariant) } Self::Deneb(body) => { @@ -273,7 +276,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, match self { Self::Base(body) => body.attestations.len(), Self::Altair(body) => body.attestations.len(), - Self::Merge(body) => body.attestations.len(), + Self::Bellatrix(body) => body.attestations.len(), Self::Capella(body) => body.attestations.len(), Self::Deneb(body) => body.attestations.len(), Self::Electra(body) => body.attestations.len(), @@ -284,7 +287,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, match self { Self::Base(body) => body.attester_slashings.len(), Self::Altair(body) => body.attester_slashings.len(), - Self::Merge(body) => body.attester_slashings.len(), + Self::Bellatrix(body) => body.attester_slashings.len(), Self::Capella(body) => body.attester_slashings.len(), Self::Deneb(body) => body.attester_slashings.len(), Self::Electra(body) => body.attester_slashings.len(), @@ -295,7 +298,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, match self { Self::Base(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), Self::Altair(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), - Self::Merge(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), + Self::Bellatrix(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), Self::Capella(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), Self::Deneb(body) => Box::new(body.attestations.iter().map(AttestationRef::Base)), Self::Electra(body) => Box::new(body.attestations.iter().map(AttestationRef::Electra)), @@ -314,7 +317,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, .iter() .map(AttesterSlashingRef::Base), ), - Self::Merge(body) => Box::new( + Self::Bellatrix(body) => Box::new( body.attester_slashings .iter() .map(AttesterSlashingRef::Base), @@ -347,7 +350,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRefMut<'a, Self::Altair(body) => { Box::new(body.attestations.iter_mut().map(AttestationRefMut::Base)) } - Self::Merge(body) => { + Self::Bellatrix(body) => { Box::new(body.attestations.iter_mut().map(AttestationRefMut::Base)) } Self::Capella(body) => { @@ -369,7 +372,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, match self { BeaconBlockBodyRef::Base { .. } => ForkName::Base, BeaconBlockBodyRef::Altair { .. } => ForkName::Altair, - BeaconBlockBodyRef::Merge { .. } => ForkName::Merge, + BeaconBlockBodyRef::Bellatrix { .. } => ForkName::Bellatrix, BeaconBlockBodyRef::Capella { .. } => ForkName::Capella, BeaconBlockBodyRef::Deneb { .. } => ForkName::Deneb, BeaconBlockBodyRef::Electra { .. } => ForkName::Electra, @@ -515,14 +518,14 @@ impl From>> } } -impl From>> +impl From>> for ( - BeaconBlockBodyMerge>, - Option>, + BeaconBlockBodyBellatrix>, + Option>, ) { - fn from(body: BeaconBlockBodyMerge>) -> Self { - let BeaconBlockBodyMerge { + fn from(body: BeaconBlockBodyBellatrix>) -> Self { + let BeaconBlockBodyBellatrix { randao_reveal, eth1_data, graffiti, @@ -532,11 +535,11 @@ impl From>> deposits, voluntary_exits, sync_aggregate, - execution_payload: FullPayloadMerge { execution_payload }, + execution_payload: FullPayloadBellatrix { execution_payload }, } = body; ( - BeaconBlockBodyMerge { + BeaconBlockBodyBellatrix { randao_reveal, eth1_data, graffiti, @@ -546,7 +549,7 @@ impl From>> deposits, voluntary_exits, sync_aggregate, - execution_payload: BlindedPayloadMerge { + execution_payload: BlindedPayloadBellatrix { execution_payload_header: From::from(&execution_payload), }, }, @@ -702,9 +705,9 @@ impl BeaconBlockBodyAltair> { } } -impl BeaconBlockBodyMerge> { - pub fn clone_as_blinded(&self) -> BeaconBlockBodyMerge> { - let BeaconBlockBodyMerge { +impl BeaconBlockBodyBellatrix> { + pub fn clone_as_blinded(&self) -> BeaconBlockBodyBellatrix> { + let BeaconBlockBodyBellatrix { randao_reveal, eth1_data, graffiti, @@ -714,10 +717,10 @@ impl BeaconBlockBodyMerge> { deposits, voluntary_exits, sync_aggregate, - execution_payload: FullPayloadMerge { execution_payload }, + execution_payload: FullPayloadBellatrix { execution_payload }, } = self; - BeaconBlockBodyMerge { + BeaconBlockBodyBellatrix { randao_reveal: randao_reveal.clone(), eth1_data: eth1_data.clone(), graffiti: *graffiti, @@ -727,7 +730,7 @@ impl BeaconBlockBodyMerge> { deposits: deposits.clone(), voluntary_exits: voluntary_exits.clone(), sync_aggregate: sync_aggregate.clone(), - execution_payload: BlindedPayloadMerge { + execution_payload: BlindedPayloadBellatrix { execution_payload_header: execution_payload.into(), }, } @@ -874,7 +877,7 @@ impl BeaconBlockBody { let attestations_root = match self { BeaconBlockBody::Base(_) | BeaconBlockBody::Altair(_) - | BeaconBlockBody::Merge(_) + | BeaconBlockBody::Bellatrix(_) | BeaconBlockBody::Capella(_) | BeaconBlockBody::Deneb(_) => self.attestations_base()?.tree_hash_root(), BeaconBlockBody::Electra(_) => self.attestations_electra()?.tree_hash_root(), @@ -883,7 +886,7 @@ impl BeaconBlockBody { let attester_slashings_root = match self { BeaconBlockBody::Base(_) | BeaconBlockBody::Altair(_) - | BeaconBlockBody::Merge(_) + | BeaconBlockBody::Bellatrix(_) | BeaconBlockBody::Capella(_) | BeaconBlockBody::Deneb(_) => self.attester_slashings_base()?.tree_hash_root(), BeaconBlockBody::Electra(_) => self.attester_slashings_electra()?.tree_hash_root(), diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index 5da81f6a752..577f282a556 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -206,7 +206,7 @@ impl From for Hash256 { /// The state of the `BeaconChain` at some slot. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb, Electra), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), variant_attributes( derive( Derivative, @@ -254,14 +254,14 @@ impl From for Hash256 { )), num_fields(all()), )), - Merge(metastruct( + Bellatrix(metastruct( mappings( map_beacon_state_bellatrix_fields(), map_beacon_state_bellatrix_tree_list_fields(mutable, fallible, groups(tree_lists)), map_beacon_state_bellatrix_tree_list_fields_immutable(groups(tree_lists)), ), bimappings(bimap_beacon_state_bellatrix_tree_list_fields( - other_type = "BeaconStateMerge", + other_type = "BeaconStateBellatrix", self_mutable, fallible, groups(tree_lists) @@ -392,10 +392,10 @@ where pub current_epoch_attestations: List, E::MaxPendingAttestations>, // Participation (Altair and later) - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] #[test_random(default)] pub previous_epoch_participation: List, - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] #[test_random(default)] pub current_epoch_participation: List, @@ -415,25 +415,25 @@ where // Inactivity #[serde(with = "ssz_types::serde_utils::quoted_u64_var_list")] - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] #[test_random(default)] pub inactivity_scores: List, // Light-client sync committees - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] #[metastruct(exclude_from(tree_lists))] pub current_sync_committee: Arc>, - #[superstruct(only(Altair, Merge, Capella, Deneb, Electra))] + #[superstruct(only(Altair, Bellatrix, Capella, Deneb, Electra))] #[metastruct(exclude_from(tree_lists))] pub next_sync_committee: Arc>, // Execution #[superstruct( - only(Merge), - partial_getter(rename = "latest_execution_payload_header_merge") + only(Bellatrix), + partial_getter(rename = "latest_execution_payload_header_bellatrix") )] #[metastruct(exclude_from(tree_lists))] - pub latest_execution_payload_header: ExecutionPayloadHeaderMerge, + pub latest_execution_payload_header: ExecutionPayloadHeaderBellatrix, #[superstruct( only(Capella), partial_getter(rename = "latest_execution_payload_header_capella") @@ -467,6 +467,40 @@ where #[test_random(default)] pub historical_summaries: List, + // Electra + #[superstruct(only(Electra), partial_getter(copy))] + #[metastruct(exclude_from(tree_lists))] + #[serde(with = "serde_utils::quoted_u64")] + pub deposit_receipts_start_index: u64, + #[superstruct(only(Electra), partial_getter(copy))] + #[metastruct(exclude_from(tree_lists))] + #[serde(with = "serde_utils::quoted_u64")] + pub deposit_balance_to_consume: u64, + #[superstruct(only(Electra), partial_getter(copy))] + #[metastruct(exclude_from(tree_lists))] + #[serde(with = "serde_utils::quoted_u64")] + pub exit_balance_to_consume: u64, + #[superstruct(only(Electra), partial_getter(copy))] + #[metastruct(exclude_from(tree_lists))] + pub earliest_exit_epoch: Epoch, + #[superstruct(only(Electra), partial_getter(copy))] + #[metastruct(exclude_from(tree_lists))] + #[serde(with = "serde_utils::quoted_u64")] + pub consolidation_balance_to_consume: u64, + #[superstruct(only(Electra), partial_getter(copy))] + #[metastruct(exclude_from(tree_lists))] + pub earliest_consolidation_epoch: Epoch, + #[test_random(default)] + #[superstruct(only(Electra))] + pub pending_balance_deposits: List, + #[test_random(default)] + #[superstruct(only(Electra))] + pub pending_partial_withdrawals: + List, + #[test_random(default)] + #[superstruct(only(Electra))] + pub pending_consolidations: List, + // Caching (not in the spec) #[serde(skip_serializing, skip_deserializing)] #[ssz(skip_serializing, skip_deserializing)] @@ -601,7 +635,7 @@ impl BeaconState { match self { BeaconState::Base { .. } => ForkName::Base, BeaconState::Altair { .. } => ForkName::Altair, - BeaconState::Merge { .. } => ForkName::Merge, + BeaconState::Bellatrix { .. } => ForkName::Bellatrix, BeaconState::Capella { .. } => ForkName::Capella, BeaconState::Deneb { .. } => ForkName::Deneb, BeaconState::Electra { .. } => ForkName::Electra, @@ -884,7 +918,7 @@ impl BeaconState { pub fn latest_execution_payload_header(&self) -> Result, Error> { match self { BeaconState::Base(_) | BeaconState::Altair(_) => Err(Error::IncorrectStateVariant), - BeaconState::Merge(state) => Ok(ExecutionPayloadHeaderRef::Merge( + BeaconState::Bellatrix(state) => Ok(ExecutionPayloadHeaderRef::Bellatrix( &state.latest_execution_payload_header, )), BeaconState::Capella(state) => Ok(ExecutionPayloadHeaderRef::Capella( @@ -904,7 +938,7 @@ impl BeaconState { ) -> Result, Error> { match self { BeaconState::Base(_) | BeaconState::Altair(_) => Err(Error::IncorrectStateVariant), - BeaconState::Merge(state) => Ok(ExecutionPayloadHeaderRefMut::Merge( + BeaconState::Bellatrix(state) => Ok(ExecutionPayloadHeaderRefMut::Bellatrix( &mut state.latest_execution_payload_header, )), BeaconState::Capella(state) => Ok(ExecutionPayloadHeaderRefMut::Capella( @@ -1390,7 +1424,7 @@ impl BeaconState { &mut state.exit_cache, &mut state.epoch_cache, )), - BeaconState::Merge(state) => Ok(( + BeaconState::Bellatrix(state) => Ok(( &mut state.validators, &mut state.balances, &state.previous_epoch_participation, @@ -1555,7 +1589,7 @@ impl BeaconState { Ok(match self { BeaconState::Base(_) | BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) => self.get_validator_churn_limit(spec)?, BeaconState::Deneb(_) | BeaconState::Electra(_) => std::cmp::min( spec.max_per_epoch_activation_churn_limit, @@ -1674,7 +1708,7 @@ impl BeaconState { match self { BeaconState::Base(_) => Err(BeaconStateError::IncorrectStateVariant), BeaconState::Altair(state) => Ok(&mut state.current_epoch_participation), - BeaconState::Merge(state) => Ok(&mut state.current_epoch_participation), + BeaconState::Bellatrix(state) => Ok(&mut state.current_epoch_participation), BeaconState::Capella(state) => Ok(&mut state.current_epoch_participation), BeaconState::Deneb(state) => Ok(&mut state.current_epoch_participation), BeaconState::Electra(state) => Ok(&mut state.current_epoch_participation), @@ -1683,7 +1717,7 @@ impl BeaconState { match self { BeaconState::Base(_) => Err(BeaconStateError::IncorrectStateVariant), BeaconState::Altair(state) => Ok(&mut state.previous_epoch_participation), - BeaconState::Merge(state) => Ok(&mut state.previous_epoch_participation), + BeaconState::Bellatrix(state) => Ok(&mut state.previous_epoch_participation), BeaconState::Capella(state) => Ok(&mut state.previous_epoch_participation), BeaconState::Deneb(state) => Ok(&mut state.previous_epoch_participation), BeaconState::Electra(state) => Ok(&mut state.previous_epoch_participation), @@ -1924,7 +1958,7 @@ impl BeaconState { any_pending_mutations |= self_field.has_pending_updates(); }); } - Self::Merge(self_inner) => { + Self::Bellatrix(self_inner) => { map_beacon_state_bellatrix_tree_list_fields_immutable!( self_inner, |_, self_field| { @@ -2031,6 +2065,210 @@ impl BeaconState { self.epoch_cache().get_base_reward(validator_index) } + // ******* Electra accessors ******* + + /// Return the churn limit for the current epoch. + pub fn get_balance_churn_limit(&self, spec: &ChainSpec) -> Result { + let total_active_balance = self.get_total_active_balance()?; + let churn = std::cmp::max( + spec.min_per_epoch_churn_limit_electra, + total_active_balance.safe_div(spec.churn_limit_quotient)?, + ); + + Ok(churn.safe_sub(churn.safe_rem(spec.effective_balance_increment)?)?) + } + + /// Return the churn limit for the current epoch dedicated to activations and exits. + pub fn get_activation_exit_churn_limit(&self, spec: &ChainSpec) -> Result { + Ok(std::cmp::min( + spec.max_per_epoch_activation_exit_churn_limit, + self.get_balance_churn_limit(spec)?, + )) + } + + pub fn get_consolidation_churn_limit(&self, spec: &ChainSpec) -> Result { + self.get_balance_churn_limit(spec)? + .safe_sub(self.get_activation_exit_churn_limit(spec)?) + .map_err(Into::into) + } + + /// Get active balance for the given `validator_index`. + pub fn get_active_balance( + &self, + validator_index: usize, + spec: &ChainSpec, + ) -> Result { + let max_effective_balance = self + .validators() + .get(validator_index) + .map(|validator| validator.get_validator_max_effective_balance(spec)) + .ok_or(Error::UnknownValidator(validator_index))?; + Ok(std::cmp::min( + *self + .balances() + .get(validator_index) + .ok_or(Error::UnknownValidator(validator_index))?, + max_effective_balance, + )) + } + + pub fn get_pending_balance_to_withdraw(&self, validator_index: usize) -> Result { + let mut pending_balance = 0; + for withdrawal in self + .pending_partial_withdrawals()? + .iter() + .filter(|withdrawal| withdrawal.index as usize == validator_index) + { + pending_balance.safe_add_assign(withdrawal.amount)?; + } + Ok(pending_balance) + } + + // ******* Electra mutators ******* + + pub fn queue_excess_active_balance( + &mut self, + validator_index: usize, + spec: &ChainSpec, + ) -> Result<(), Error> { + let balance = self + .balances_mut() + .get_mut(validator_index) + .ok_or(Error::UnknownValidator(validator_index))?; + if *balance > spec.min_activation_balance { + let excess_balance = balance.safe_sub(spec.min_activation_balance)?; + *balance = spec.min_activation_balance; + self.pending_balance_deposits_mut()? + .push(PendingBalanceDeposit { + index: validator_index as u64, + amount: excess_balance, + })?; + } + Ok(()) + } + + pub fn queue_entire_balance_and_reset_validator( + &mut self, + validator_index: usize, + spec: &ChainSpec, + ) -> Result<(), Error> { + let balance = self + .balances_mut() + .get_mut(validator_index) + .ok_or(Error::UnknownValidator(validator_index))?; + let balance_copy = *balance; + *balance = 0_u64; + + let validator = self + .validators_mut() + .get_mut(validator_index) + .ok_or(Error::UnknownValidator(validator_index))?; + validator.effective_balance = 0; + validator.activation_eligibility_epoch = spec.far_future_epoch; + + self.pending_balance_deposits_mut()? + .push(PendingBalanceDeposit { + index: validator_index as u64, + amount: balance_copy, + }) + .map_err(Into::into) + } + + /// Change the withdrawal prefix of the given `validator_index` to the compounding withdrawal validator prefix. + pub fn switch_to_compounding_validator( + &mut self, + validator_index: usize, + spec: &ChainSpec, + ) -> Result<(), Error> { + let validator = self + .validators_mut() + .get_mut(validator_index) + .ok_or(Error::UnknownValidator(validator_index))?; + if validator.has_eth1_withdrawal_credential(spec) { + validator.withdrawal_credentials.as_fixed_bytes_mut()[0] = + spec.compounding_withdrawal_prefix_byte; + self.queue_excess_active_balance(validator_index, spec)?; + } + Ok(()) + } + + pub fn compute_exit_epoch_and_update_churn( + &mut self, + exit_balance: u64, + spec: &ChainSpec, + ) -> Result { + let mut earliest_exit_epoch = std::cmp::max( + self.earliest_exit_epoch()?, + self.compute_activation_exit_epoch(self.current_epoch(), spec)?, + ); + + let per_epoch_churn = self.get_activation_exit_churn_limit(spec)?; + // New epoch for exits + let mut exit_balance_to_consume = if self.earliest_exit_epoch()? < earliest_exit_epoch { + per_epoch_churn + } else { + self.exit_balance_to_consume()? + }; + + // Exit doesn't fit in the current earliest epoch + if exit_balance > exit_balance_to_consume { + let balance_to_process = exit_balance.safe_sub(exit_balance_to_consume)?; + let additional_epochs = balance_to_process + .safe_sub(1)? + .safe_div(per_epoch_churn)? + .safe_add(1)?; + earliest_exit_epoch.safe_add_assign(additional_epochs)?; + exit_balance_to_consume + .safe_add_assign(additional_epochs.safe_mul(per_epoch_churn)?)?; + } + let state = self.as_electra_mut()?; + // Consume the balance and update state variables + state.exit_balance_to_consume = exit_balance_to_consume.safe_sub(exit_balance)?; + state.earliest_exit_epoch = earliest_exit_epoch; + + Ok(state.earliest_exit_epoch) + } + + pub fn compute_consolidation_epoch_and_update_churn( + &mut self, + consolidation_balance: u64, + spec: &ChainSpec, + ) -> Result { + let mut earliest_consolidation_epoch = std::cmp::max( + self.earliest_consolidation_epoch()?, + self.compute_activation_exit_epoch(self.current_epoch(), spec)?, + ); + + let per_epoch_consolidation_churn = self.get_consolidation_churn_limit(spec)?; + + // New epoch for consolidations + let mut consolidation_balance_to_consume = + if self.earliest_consolidation_epoch()? < earliest_consolidation_epoch { + per_epoch_consolidation_churn + } else { + self.consolidation_balance_to_consume()? + }; + // Consolidation doesn't fit in the current earliest epoch + if consolidation_balance > consolidation_balance_to_consume { + let balance_to_process = + consolidation_balance.safe_sub(consolidation_balance_to_consume)?; + let additional_epochs = balance_to_process + .safe_sub(1)? + .safe_div(per_epoch_consolidation_churn)? + .safe_add(1)?; + earliest_consolidation_epoch.safe_add_assign(additional_epochs)?; + consolidation_balance_to_consume + .safe_add_assign(additional_epochs.safe_mul(per_epoch_consolidation_churn)?)?; + } + // Consume the balance and update state variables + let state = self.as_electra_mut()?; + state.consolidation_balance_to_consume = + consolidation_balance_to_consume.safe_sub(consolidation_balance)?; + state.earliest_consolidation_epoch = earliest_consolidation_epoch; + + Ok(state.earliest_consolidation_epoch) + } + #[allow(clippy::arithmetic_side_effects)] pub fn rebase_on(&mut self, base: &Self, spec: &ChainSpec) -> Result<(), Error> { // Required for macros (which use type-hints internally). @@ -2052,14 +2290,14 @@ impl BeaconState { ); } (Self::Altair(_), _) => (), - (Self::Merge(self_inner), Self::Merge(base_inner)) => { + (Self::Bellatrix(self_inner), Self::Bellatrix(base_inner)) => { bimap_beacon_state_bellatrix_tree_list_fields!( self_inner, base_inner, |_, self_field, base_field| { self_field.rebase_on(base_field) } ); } - (Self::Merge(_), _) => (), + (Self::Bellatrix(_), _) => (), (Self::Capella(self_inner), Self::Capella(base_inner)) => { bimap_beacon_state_capella_tree_list_fields!( self_inner, @@ -2147,10 +2385,17 @@ impl BeaconState { /// The number of fields of the `BeaconState` rounded up to the nearest power of two. /// /// This is relevant to tree-hashing of the `BeaconState`. - /// - /// We assume this value is stable across forks. This assumption is checked in the - /// `check_num_fields_pow2` test. - pub const NUM_FIELDS_POW2: usize = BeaconStateMerge::::NUM_FIELDS.next_power_of_two(); + pub fn num_fields_pow2(&self) -> usize { + let fork_name = self.fork_name_unchecked(); + match fork_name { + ForkName::Base => BeaconStateBase::::NUM_FIELDS.next_power_of_two(), + ForkName::Altair => BeaconStateAltair::::NUM_FIELDS.next_power_of_two(), + ForkName::Bellatrix => BeaconStateBellatrix::::NUM_FIELDS.next_power_of_two(), + ForkName::Capella => BeaconStateCapella::::NUM_FIELDS.next_power_of_two(), + ForkName::Deneb => BeaconStateDeneb::::NUM_FIELDS.next_power_of_two(), + ForkName::Electra => BeaconStateElectra::::NUM_FIELDS.next_power_of_two(), + } + } /// Specialised deserialisation method that uses the `ChainSpec` as context. #[allow(clippy::arithmetic_side_effects)] @@ -2185,7 +2430,7 @@ impl BeaconState { Self::Altair(inner) => { map_beacon_state_altair_tree_list_fields!(inner, |_, x| { x.apply_updates() }) } - Self::Merge(inner) => { + Self::Bellatrix(inner) => { map_beacon_state_bellatrix_tree_list_fields!(inner, |_, x| { x.apply_updates() }) } Self::Capella(inner) => { @@ -2211,7 +2456,7 @@ impl BeaconState { // in the `BeaconState`: // https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate generalized_index - .checked_sub(Self::NUM_FIELDS_POW2) + .checked_sub(self.num_fields_pow2()) .ok_or(Error::IndexNotSupported(generalized_index))? } light_client_update::FINALIZED_ROOT_INDEX => { @@ -2221,7 +2466,7 @@ impl BeaconState { // Subtract off the internal nodes. Result should be 105/2 - 32 = 20 which matches // position of `finalized_checkpoint` in `BeaconState`. finalized_checkpoint_generalized_index - .checked_sub(Self::NUM_FIELDS_POW2) + .checked_sub(self.num_fields_pow2()) .ok_or(Error::IndexNotSupported(generalized_index))? } _ => return Err(Error::IndexNotSupported(generalized_index)), @@ -2241,7 +2486,7 @@ impl BeaconState { leaves.push(field.tree_hash_root()); }); } - BeaconState::Merge(state) => { + BeaconState::Bellatrix(state) => { map_beacon_state_bellatrix_fields!(state, |_, field| { leaves.push(field.tree_hash_root()); }); @@ -2331,7 +2576,7 @@ impl CompareFields for BeaconState { match (self, other) { (BeaconState::Base(x), BeaconState::Base(y)) => x.compare_fields(y), (BeaconState::Altair(x), BeaconState::Altair(y)) => x.compare_fields(y), - (BeaconState::Merge(x), BeaconState::Merge(y)) => x.compare_fields(y), + (BeaconState::Bellatrix(x), BeaconState::Bellatrix(y)) => x.compare_fields(y), (BeaconState::Capella(x), BeaconState::Capella(y)) => x.compare_fields(y), (BeaconState::Deneb(x), BeaconState::Deneb(y)) => x.compare_fields(y), (BeaconState::Electra(x), BeaconState::Electra(y)) => x.compare_fields(y), diff --git a/consensus/types/src/beacon_state/progressive_balances_cache.rs b/consensus/types/src/beacon_state/progressive_balances_cache.rs index 523c94cf57e..fd5e51313f7 100644 --- a/consensus/types/src/beacon_state/progressive_balances_cache.rs +++ b/consensus/types/src/beacon_state/progressive_balances_cache.rs @@ -288,7 +288,7 @@ pub fn is_progressive_balances_enabled(state: &BeaconState) -> bo match state { BeaconState::Base(_) => false, BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => true, diff --git a/consensus/types/src/beacon_state/tests.rs b/consensus/types/src/beacon_state/tests.rs index 012c063afef..38a76e44c50 100644 --- a/consensus/types/src/beacon_state/tests.rs +++ b/consensus/types/src/beacon_state/tests.rs @@ -1,10 +1,10 @@ #![cfg(test)] -use crate::{test_utils::*, ForkName}; +use crate::test_utils::*; use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType}; use beacon_chain::types::{ - test_utils::TestRandom, BeaconState, BeaconStateAltair, BeaconStateBase, BeaconStateCapella, - BeaconStateDeneb, BeaconStateElectra, BeaconStateError, BeaconStateMerge, ChainSpec, Domain, - Epoch, EthSpec, Hash256, Keypair, MainnetEthSpec, MinimalEthSpec, RelativeEpoch, Slot, Vector, + test_utils::TestRandom, BeaconState, BeaconStateAltair, BeaconStateBase, BeaconStateError, + ChainSpec, Domain, Epoch, EthSpec, Hash256, Keypair, MainnetEthSpec, MinimalEthSpec, + RelativeEpoch, Slot, Vector, }; use ssz::Encode; use std::ops::Mul; @@ -403,24 +403,3 @@ fn decode_base_and_altair() { .expect_err("bad altair state cannot be decoded"); } } - -#[test] -fn check_num_fields_pow2() { - use metastruct::NumFields; - pub type E = MainnetEthSpec; - - for fork_name in ForkName::list_all() { - let num_fields = match fork_name { - ForkName::Base => BeaconStateBase::::NUM_FIELDS, - ForkName::Altair => BeaconStateAltair::::NUM_FIELDS, - ForkName::Merge => BeaconStateMerge::::NUM_FIELDS, - ForkName::Capella => BeaconStateCapella::::NUM_FIELDS, - ForkName::Deneb => BeaconStateDeneb::::NUM_FIELDS, - ForkName::Electra => BeaconStateElectra::::NUM_FIELDS, - }; - assert_eq!( - num_fields.next_power_of_two(), - BeaconState::::NUM_FIELDS_POW2 - ); - } -} diff --git a/consensus/types/src/builder_bid.rs b/consensus/types/src/builder_bid.rs index 121d3f84277..9885f78474f 100644 --- a/consensus/types/src/builder_bid.rs +++ b/consensus/types/src/builder_bid.rs @@ -1,7 +1,7 @@ use crate::beacon_block_body::KzgCommitments; use crate::{ - ChainSpec, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, - ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, + ChainSpec, EthSpec, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, + ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, ForkName, ForkVersionDeserialize, SignedRoot, Uint256, }; use bls::PublicKeyBytes; @@ -11,7 +11,7 @@ use superstruct::superstruct; use tree_hash_derive::TreeHash; #[superstruct( - variants(Merge, Capella, Deneb, Electra), + variants(Bellatrix, Capella, Deneb, Electra), variant_attributes( derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone), serde(bound = "E: EthSpec", deny_unknown_fields) @@ -23,8 +23,8 @@ use tree_hash_derive::TreeHash; #[serde(bound = "E: EthSpec", deny_unknown_fields, untagged)] #[tree_hash(enum_behaviour = "transparent")] pub struct BuilderBid { - #[superstruct(only(Merge), partial_getter(rename = "header_merge"))] - pub header: ExecutionPayloadHeaderMerge, + #[superstruct(only(Bellatrix), partial_getter(rename = "header_bellatrix"))] + pub header: ExecutionPayloadHeaderBellatrix, #[superstruct(only(Capella), partial_getter(rename = "header_capella"))] pub header: ExecutionPayloadHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "header_deneb"))] @@ -79,7 +79,9 @@ impl ForkVersionDeserialize for BuilderBid { |e| serde::de::Error::custom(format!("BuilderBid failed to deserialize: {:?}", e)); Ok(match fork_name { - ForkName::Merge => Self::Merge(serde_json::from_value(value).map_err(convert_err)?), + ForkName::Bellatrix => { + Self::Bellatrix(serde_json::from_value(value).map_err(convert_err)?) + } ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?), ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?), ForkName::Electra => Self::Electra(serde_json::from_value(value).map_err(convert_err)?), diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index e4f27d6873c..b0346a14ef8 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -147,13 +147,13 @@ pub struct ChainSpec { pub altair_fork_epoch: Option, /* - * Merge hard fork params + * Bellatrix hard fork params */ pub inactivity_penalty_quotient_bellatrix: u64, pub min_slashing_penalty_quotient_bellatrix: u64, pub proportional_slashing_multiplier_bellatrix: u64, pub bellatrix_fork_version: [u8; 4], - /// The Merge fork epoch is optional, with `None` representing "Merge never happens". + /// The Bellatrix fork epoch is optional, with `None` representing "Bellatrix never happens". pub bellatrix_fork_epoch: Option, pub terminal_total_difficulty: Uint256, pub terminal_block_hash: ExecutionBlockHash, @@ -310,7 +310,7 @@ impl ChainSpec { _ => match self.capella_fork_epoch { Some(fork_epoch) if epoch >= fork_epoch => ForkName::Capella, _ => match self.bellatrix_fork_epoch { - Some(fork_epoch) if epoch >= fork_epoch => ForkName::Merge, + Some(fork_epoch) if epoch >= fork_epoch => ForkName::Bellatrix, _ => match self.altair_fork_epoch { Some(fork_epoch) if epoch >= fork_epoch => ForkName::Altair, _ => ForkName::Base, @@ -326,7 +326,7 @@ impl ChainSpec { match fork_name { ForkName::Base => self.genesis_fork_version, ForkName::Altair => self.altair_fork_version, - ForkName::Merge => self.bellatrix_fork_version, + ForkName::Bellatrix => self.bellatrix_fork_version, ForkName::Capella => self.capella_fork_version, ForkName::Deneb => self.deneb_fork_version, ForkName::Electra => self.electra_fork_version, @@ -338,7 +338,7 @@ impl ChainSpec { match fork_name { ForkName::Base => Some(Epoch::new(0)), ForkName::Altair => self.altair_fork_epoch, - ForkName::Merge => self.bellatrix_fork_epoch, + ForkName::Bellatrix => self.bellatrix_fork_epoch, ForkName::Capella => self.capella_fork_epoch, ForkName::Deneb => self.deneb_fork_epoch, ForkName::Electra => self.electra_fork_epoch, @@ -346,7 +346,7 @@ impl ChainSpec { } pub fn inactivity_penalty_quotient_for_fork(&self, fork_name: ForkName) -> u64 { - if fork_name >= ForkName::Merge { + if fork_name >= ForkName::Bellatrix { self.inactivity_penalty_quotient_bellatrix } else if fork_name >= ForkName::Altair { self.inactivity_penalty_quotient_altair @@ -361,7 +361,7 @@ impl ChainSpec { state: &BeaconState, ) -> u64 { let fork_name = state.fork_name_unchecked(); - if fork_name >= ForkName::Merge { + if fork_name >= ForkName::Bellatrix { self.proportional_slashing_multiplier_bellatrix } else if fork_name >= ForkName::Altair { self.proportional_slashing_multiplier_altair @@ -378,7 +378,7 @@ impl ChainSpec { let fork_name = state.fork_name_unchecked(); if fork_name >= ForkName::Electra { self.min_slashing_penalty_quotient_electra - } else if fork_name >= ForkName::Merge { + } else if fork_name >= ForkName::Bellatrix { self.min_slashing_penalty_quotient_bellatrix } else if fork_name >= ForkName::Altair { self.min_slashing_penalty_quotient_altair @@ -693,7 +693,7 @@ impl ChainSpec { altair_fork_epoch: Some(Epoch::new(74240)), /* - * Merge hard fork params + * Bellatrix hard fork params */ inactivity_penalty_quotient_bellatrix: u64::checked_pow(2, 24) .expect("pow does not overflow"), @@ -830,7 +830,7 @@ impl ChainSpec { epochs_per_sync_committee_period: Epoch::new(8), altair_fork_version: [0x01, 0x00, 0x00, 0x01], altair_fork_epoch: None, - // Merge + // Bellatrix bellatrix_fork_version: [0x02, 0x00, 0x00, 0x01], bellatrix_fork_epoch: None, terminal_total_difficulty: Uint256::MAX @@ -850,6 +850,8 @@ impl ChainSpec { // Electra electra_fork_version: [0x05, 0x00, 0x00, 0x01], electra_fork_epoch: None, + max_pending_partials_per_withdrawals_sweep: u64::checked_pow(2, 0) + .expect("pow does not overflow"), // Other network_id: 2, // lighthouse testnet network id deposit_chain_id: 5, @@ -991,7 +993,7 @@ impl ChainSpec { altair_fork_epoch: Some(Epoch::new(512)), /* - * Merge hard fork params + * Bellatrix hard fork params */ inactivity_penalty_quotient_bellatrix: u64::checked_pow(2, 24) .expect("pow does not overflow"), diff --git a/consensus/types/src/config_and_preset.rs b/consensus/types/src/config_and_preset.rs index f2a6e616daa..6fc6e0642ea 100644 --- a/consensus/types/src/config_and_preset.rs +++ b/consensus/types/src/config_and_preset.rs @@ -1,6 +1,6 @@ use crate::{ - consts::altair, AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, ChainSpec, Config, - DenebPreset, ElectraPreset, EthSpec, ForkName, + consts::altair, consts::deneb, AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, + ChainSpec, Config, DenebPreset, ElectraPreset, EthSpec, ForkName, }; use maplit::hashmap; use serde::{Deserialize, Serialize}; @@ -100,6 +100,7 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap { let u8_hex = |v: u8| hex_string(&v.to_le_bytes()); hashmap! { "bls_withdrawal_prefix".to_uppercase() => u8_hex(spec.bls_withdrawal_prefix_byte), + "eth1_address_withdrawal_prefix".to_uppercase() => u8_hex(spec.eth1_address_withdrawal_prefix_byte), "domain_beacon_proposer".to_uppercase() => u32_hex(spec.domain_beacon_proposer), "domain_beacon_attester".to_uppercase() => u32_hex(spec.domain_beacon_attester), "domain_randao".to_uppercase()=> u32_hex(spec.domain_randao), @@ -119,6 +120,13 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap { altair::SYNC_COMMITTEE_SUBNET_COUNT.to_string().into(), "target_aggregators_per_sync_subcommittee".to_uppercase() => altair::TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE.to_string().into(), + // Deneb + "versioned_hash_version_kzg".to_uppercase() => deneb::VERSIONED_HASH_VERSION_KZG.to_string().into(), + // Electra + "compounding_withdrawal_prefix".to_uppercase() => u8_hex(spec.compounding_withdrawal_prefix_byte), + "unset_deposit_receipts_start_index".to_uppercase() => spec.unset_deposit_receipts_start_index.to_string().into(), + "full_exit_request_amount".to_uppercase() => spec.full_exit_request_amount.to_string().into(), + "domain_consolidation".to_uppercase()=> u32_hex(spec.domain_consolidation), } } diff --git a/consensus/types/src/consts.rs b/consensus/types/src/consts.rs index a9377bc3e00..c20d5fe8f33 100644 --- a/consensus/types/src/consts.rs +++ b/consensus/types/src/consts.rs @@ -19,6 +19,9 @@ pub mod altair { pub const NUM_FLAG_INDICES: usize = 3; } -pub mod merge { +pub mod bellatrix { pub const INTERVALS_PER_SLOT: u64 = 3; } +pub mod deneb { + pub use crate::VERSIONED_HASH_VERSION_KZG; +} diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/eth_spec.rs index f9031f98908..e39b0dc9cf6 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/eth_spec.rs @@ -93,7 +93,7 @@ pub trait EthSpec: /// The number of `sync_committee` subnets. type SyncCommitteeSubnetCount: Unsigned + Clone + Sync + Send + Debug + PartialEq; /* - * New in Merge + * New in Bellatrix */ type MaxBytesPerTransaction: Unsigned + Clone + Sync + Send + Debug + PartialEq; type MaxTransactionsPerPayload: Unsigned + Clone + Sync + Send + Debug + PartialEq; @@ -422,6 +422,10 @@ impl EthSpec for MinimalEthSpec { type BytesPerBlob = U131072; type MaxBlobCommitmentsPerBlock = U16; type KzgCommitmentInclusionProofDepth = U9; + type PendingPartialWithdrawalsLimit = U64; + type PendingConsolidationsLimit = U64; + type MaxDepositReceiptsPerPayload = U4; + type MaxWithdrawalRequestsPerPayload = U2; params_from_eth_spec!(MainnetEthSpec { JustificationBitsLength, @@ -448,13 +452,9 @@ impl EthSpec for MinimalEthSpec { MaxBlobsPerBlock, BytesPerFieldElement, PendingBalanceDepositsLimit, - PendingPartialWithdrawalsLimit, - PendingConsolidationsLimit, MaxConsolidations, - MaxDepositReceiptsPerPayload, MaxAttesterSlashingsElectra, - MaxAttestationsElectra, - MaxWithdrawalRequestsPerPayload + MaxAttestationsElectra }); fn default_spec() -> ChainSpec { diff --git a/consensus/types/src/execution_payload.rs b/consensus/types/src/execution_payload.rs index 27dc8cab0a4..0946b9ecffa 100644 --- a/consensus/types/src/execution_payload.rs +++ b/consensus/types/src/execution_payload.rs @@ -15,7 +15,7 @@ pub type Transactions = VariableList< pub type Withdrawals = VariableList::MaxWithdrawalsPerPayload>; #[superstruct( - variants(Merge, Capella, Deneb, Electra), + variants(Bellatrix, Capella, Deneb, Electra), variant_attributes( derive( Default, @@ -89,6 +89,11 @@ pub struct ExecutionPayload { #[superstruct(only(Deneb, Electra), partial_getter(copy))] #[serde(with = "serde_utils::quoted_u64")] pub excess_blob_gas: u64, + #[superstruct(only(Electra))] + pub deposit_receipts: VariableList, + #[superstruct(only(Electra))] + pub withdrawal_requests: + VariableList, } impl<'a, E: EthSpec> ExecutionPayloadRef<'a, E> { @@ -107,7 +112,9 @@ impl ExecutionPayload { ForkName::Base | ForkName::Altair => Err(ssz::DecodeError::BytesInvalid(format!( "unsupported fork for ExecutionPayload: {fork_name}", ))), - ForkName::Merge => ExecutionPayloadMerge::from_ssz_bytes(bytes).map(Self::Merge), + ForkName::Bellatrix => { + ExecutionPayloadBellatrix::from_ssz_bytes(bytes).map(Self::Bellatrix) + } ForkName::Capella => ExecutionPayloadCapella::from_ssz_bytes(bytes).map(Self::Capella), ForkName::Deneb => ExecutionPayloadDeneb::from_ssz_bytes(bytes).map(Self::Deneb), ForkName::Electra => ExecutionPayloadElectra::from_ssz_bytes(bytes).map(Self::Electra), @@ -116,9 +123,9 @@ impl ExecutionPayload { #[allow(clippy::arithmetic_side_effects)] /// Returns the maximum size of an execution payload. - pub fn max_execution_payload_merge_size() -> usize { + pub fn max_execution_payload_bellatrix_size() -> usize { // Fixed part - ExecutionPayloadMerge::::default().as_ssz_bytes().len() + ExecutionPayloadBellatrix::::default().as_ssz_bytes().len() // Max size of variable length `extra_data` field + (E::max_extra_data_bytes() * ::ssz_fixed_len()) // Max size of variable length `transactions` field @@ -175,7 +182,9 @@ impl ForkVersionDeserialize for ExecutionPayload { }; Ok(match fork_name { - ForkName::Merge => Self::Merge(serde_json::from_value(value).map_err(convert_err)?), + ForkName::Bellatrix => { + Self::Bellatrix(serde_json::from_value(value).map_err(convert_err)?) + } ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?), ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?), ForkName::Electra => Self::Electra(serde_json::from_value(value).map_err(convert_err)?), @@ -192,7 +201,7 @@ impl ForkVersionDeserialize for ExecutionPayload { impl ExecutionPayload { pub fn fork_name(&self) -> ForkName { match self { - ExecutionPayload::Merge(_) => ForkName::Merge, + ExecutionPayload::Bellatrix(_) => ForkName::Bellatrix, ExecutionPayload::Capella(_) => ForkName::Capella, ExecutionPayload::Deneb(_) => ForkName::Deneb, ExecutionPayload::Electra(_) => ForkName::Electra, diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution_payload_header.rs index 02850304f1d..324d7b97472 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution_payload_header.rs @@ -8,7 +8,7 @@ use tree_hash::TreeHash; use tree_hash_derive::TreeHash; #[superstruct( - variants(Merge, Capella, Deneb, Electra), + variants(Bellatrix, Capella, Deneb, Electra), variant_attributes( derive( Default, @@ -88,6 +88,10 @@ pub struct ExecutionPayloadHeader { #[serde(with = "serde_utils::quoted_u64")] #[superstruct(getter(copy))] pub excess_blob_gas: u64, + #[superstruct(only(Electra), partial_getter(copy))] + pub deposit_receipts_root: Hash256, + #[superstruct(only(Electra), partial_getter(copy))] + pub withdrawal_requests_root: Hash256, } impl ExecutionPayloadHeader { @@ -100,7 +104,9 @@ impl ExecutionPayloadHeader { ForkName::Base | ForkName::Altair => Err(ssz::DecodeError::BytesInvalid(format!( "unsupported fork for ExecutionPayloadHeader: {fork_name}", ))), - ForkName::Merge => ExecutionPayloadHeaderMerge::from_ssz_bytes(bytes).map(Self::Merge), + ForkName::Bellatrix => { + ExecutionPayloadHeaderBellatrix::from_ssz_bytes(bytes).map(Self::Bellatrix) + } ForkName::Capella => { ExecutionPayloadHeaderCapella::from_ssz_bytes(bytes).map(Self::Capella) } @@ -118,7 +124,7 @@ impl ExecutionPayloadHeader { match fork_name { ForkName::Base | ForkName::Altair - | ForkName::Merge + | ForkName::Bellatrix | ForkName::Capella | ForkName::Deneb | ForkName::Electra => { @@ -138,7 +144,7 @@ impl<'a, E: EthSpec> ExecutionPayloadHeaderRef<'a, E> { } } -impl ExecutionPayloadHeaderMerge { +impl ExecutionPayloadHeaderBellatrix { pub fn upgrade_to_capella(&self) -> ExecutionPayloadHeaderCapella { ExecutionPayloadHeaderCapella { parent_hash: self.parent_hash, @@ -204,12 +210,14 @@ impl ExecutionPayloadHeaderDeneb { withdrawals_root: self.withdrawals_root, blob_gas_used: self.blob_gas_used, excess_blob_gas: self.excess_blob_gas, + deposit_receipts_root: Hash256::zero(), + withdrawal_requests_root: Hash256::zero(), } } } -impl<'a, E: EthSpec> From<&'a ExecutionPayloadMerge> for ExecutionPayloadHeaderMerge { - fn from(payload: &'a ExecutionPayloadMerge) -> Self { +impl<'a, E: EthSpec> From<&'a ExecutionPayloadBellatrix> for ExecutionPayloadHeaderBellatrix { + fn from(payload: &'a ExecutionPayloadBellatrix) -> Self { Self { parent_hash: payload.parent_hash, fee_recipient: payload.fee_recipient, @@ -295,13 +303,15 @@ impl<'a, E: EthSpec> From<&'a ExecutionPayloadElectra> for ExecutionPayloadHe withdrawals_root: payload.withdrawals.tree_hash_root(), blob_gas_used: payload.blob_gas_used, excess_blob_gas: payload.excess_blob_gas, + deposit_receipts_root: payload.deposit_receipts.tree_hash_root(), + withdrawal_requests_root: payload.withdrawal_requests.tree_hash_root(), } } } // These impls are required to work around an inelegance in `to_execution_payload_header`. // They only clone headers so they should be relatively cheap. -impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderMerge { +impl<'a, E: EthSpec> From<&'a Self> for ExecutionPayloadHeaderBellatrix { fn from(payload: &'a Self) -> Self { payload.clone() } @@ -335,11 +345,13 @@ impl<'a, E: EthSpec> From> for ExecutionPayloadHeader } } -impl TryFrom> for ExecutionPayloadHeaderMerge { +impl TryFrom> for ExecutionPayloadHeaderBellatrix { type Error = BeaconStateError; fn try_from(header: ExecutionPayloadHeader) -> Result { match header { - ExecutionPayloadHeader::Merge(execution_payload_header) => Ok(execution_payload_header), + ExecutionPayloadHeader::Bellatrix(execution_payload_header) => { + Ok(execution_payload_header) + } _ => Err(BeaconStateError::IncorrectStateVariant), } } @@ -369,7 +381,7 @@ impl<'a, E: EthSpec> ExecutionPayloadHeaderRefMut<'a, E> { /// Mutate through pub fn replace(self, header: ExecutionPayloadHeader) -> Result<(), BeaconStateError> { match self { - ExecutionPayloadHeaderRefMut::Merge(mut_ref) => { + ExecutionPayloadHeaderRefMut::Bellatrix(mut_ref) => { *mut_ref = header.try_into()?; } ExecutionPayloadHeaderRefMut::Capella(mut_ref) => { @@ -411,7 +423,9 @@ impl ForkVersionDeserialize for ExecutionPayloadHeader { }; Ok(match fork_name { - ForkName::Merge => Self::Merge(serde_json::from_value(value).map_err(convert_err)?), + ForkName::Bellatrix => { + Self::Bellatrix(serde_json::from_value(value).map_err(convert_err)?) + } ForkName::Capella => Self::Capella(serde_json::from_value(value).map_err(convert_err)?), ForkName::Deneb => Self::Deneb(serde_json::from_value(value).map_err(convert_err)?), ForkName::Electra => Self::Electra(serde_json::from_value(value).map_err(convert_err)?), diff --git a/consensus/types/src/fork_context.rs b/consensus/types/src/fork_context.rs index 6b052d83976..0f7f0eb769e 100644 --- a/consensus/types/src/fork_context.rs +++ b/consensus/types/src/fork_context.rs @@ -36,11 +36,11 @@ impl ForkContext { )); } - // Only add Merge to list of forks if it's enabled - // Note: `bellatrix_fork_epoch == None` implies merge hasn't been activated yet on the config. + // Only add Bellatrix to list of forks if it's enabled + // Note: `bellatrix_fork_epoch == None` implies bellatrix hasn't been activated yet on the config. if spec.bellatrix_fork_epoch.is_some() { fork_to_digest.push(( - ForkName::Merge, + ForkName::Bellatrix, ChainSpec::compute_fork_digest( spec.bellatrix_fork_version, genesis_validators_root, diff --git a/consensus/types/src/fork_name.rs b/consensus/types/src/fork_name.rs index f6bd5cbadf9..5cc66214733 100644 --- a/consensus/types/src/fork_name.rs +++ b/consensus/types/src/fork_name.rs @@ -13,7 +13,7 @@ use std::str::FromStr; pub enum ForkName { Base, Altair, - Merge, + Bellatrix, Capella, Deneb, Electra, @@ -24,7 +24,7 @@ impl ForkName { vec![ ForkName::Base, ForkName::Altair, - ForkName::Merge, + ForkName::Bellatrix, ForkName::Capella, ForkName::Deneb, ForkName::Electra, @@ -57,7 +57,7 @@ impl ForkName { spec.electra_fork_epoch = None; spec } - ForkName::Merge => { + ForkName::Bellatrix => { spec.altair_fork_epoch = Some(Epoch::new(0)); spec.bellatrix_fork_epoch = Some(Epoch::new(0)); spec.capella_fork_epoch = None; @@ -99,8 +99,8 @@ impl ForkName { match self { ForkName::Base => None, ForkName::Altair => Some(ForkName::Base), - ForkName::Merge => Some(ForkName::Altair), - ForkName::Capella => Some(ForkName::Merge), + ForkName::Bellatrix => Some(ForkName::Altair), + ForkName::Capella => Some(ForkName::Bellatrix), ForkName::Deneb => Some(ForkName::Capella), ForkName::Electra => Some(ForkName::Deneb), } @@ -112,8 +112,8 @@ impl ForkName { pub fn next_fork(self) -> Option { match self { ForkName::Base => Some(ForkName::Altair), - ForkName::Altair => Some(ForkName::Merge), - ForkName::Merge => Some(ForkName::Capella), + ForkName::Altair => Some(ForkName::Bellatrix), + ForkName::Bellatrix => Some(ForkName::Capella), ForkName::Capella => Some(ForkName::Deneb), ForkName::Deneb => Some(ForkName::Electra), ForkName::Electra => None, @@ -154,9 +154,9 @@ macro_rules! map_fork_name_with { let (value, extra_data) = $body; ($t::Altair(value), extra_data) } - ForkName::Merge => { + ForkName::Bellatrix => { let (value, extra_data) = $body; - ($t::Merge(value), extra_data) + ($t::Bellatrix(value), extra_data) } ForkName::Capella => { let (value, extra_data) = $body; @@ -181,7 +181,7 @@ impl FromStr for ForkName { Ok(match fork_name.to_lowercase().as_ref() { "phase0" | "base" => ForkName::Base, "altair" => ForkName::Altair, - "bellatrix" | "merge" => ForkName::Merge, + "bellatrix" | "merge" => ForkName::Bellatrix, "capella" => ForkName::Capella, "deneb" => ForkName::Deneb, "electra" => ForkName::Electra, @@ -195,7 +195,7 @@ impl Display for ForkName { match self { ForkName::Base => "phase0".fmt(f), ForkName::Altair => "altair".fmt(f), - ForkName::Merge => "bellatrix".fmt(f), + ForkName::Bellatrix => "bellatrix".fmt(f), ForkName::Capella => "capella".fmt(f), ForkName::Deneb => "deneb".fmt(f), ForkName::Electra => "electra".fmt(f), @@ -259,9 +259,9 @@ mod test { #[test] fn fork_name_bellatrix_or_merge() { - assert_eq!(ForkName::from_str("bellatrix"), Ok(ForkName::Merge)); - assert_eq!(ForkName::from_str("merge"), Ok(ForkName::Merge)); - assert_eq!(ForkName::Merge.to_string(), "bellatrix"); + assert_eq!(ForkName::from_str("bellatrix"), Ok(ForkName::Bellatrix)); + assert_eq!(ForkName::from_str("merge"), Ok(ForkName::Bellatrix)); + assert_eq!(ForkName::Bellatrix.to_string(), "bellatrix"); } #[test] diff --git a/consensus/types/src/fork_versioned_response.rs b/consensus/types/src/fork_versioned_response.rs index 195c083e295..cd78b5b3ca0 100644 --- a/consensus/types/src/fork_versioned_response.rs +++ b/consensus/types/src/fork_versioned_response.rs @@ -104,7 +104,8 @@ impl ForkVersionedResponse { #[cfg(test)] mod fork_version_response_tests { use crate::{ - ExecutionPayload, ExecutionPayloadMerge, ForkName, ForkVersionedResponse, MainnetEthSpec, + ExecutionPayload, ExecutionPayloadBellatrix, ForkName, ForkVersionedResponse, + MainnetEthSpec, }; use serde_json::json; @@ -114,9 +115,9 @@ mod fork_version_response_tests { let response_json = serde_json::to_string(&json!(ForkVersionedResponse::> { - version: Some(ForkName::Merge), + version: Some(ForkName::Bellatrix), metadata: Default::default(), - data: ExecutionPayload::Merge(ExecutionPayloadMerge::default()), + data: ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix::default()), })) .unwrap(); @@ -134,7 +135,7 @@ mod fork_version_response_tests { serde_json::to_string(&json!(ForkVersionedResponse::> { version: Some(ForkName::Capella), metadata: Default::default(), - data: ExecutionPayload::Merge(ExecutionPayloadMerge::default()), + data: ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix::default()), })) .unwrap(); diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 0349e776943..ebfc4ab8bb0 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -126,13 +126,13 @@ pub use crate::attester_slashing::{ AttesterSlashingRef, AttesterSlashingRefOnDisk, }; pub use crate::beacon_block::{ - BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockCapella, BeaconBlockDeneb, - BeaconBlockElectra, BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, + BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockBellatrix, BeaconBlockCapella, + BeaconBlockDeneb, BeaconBlockElectra, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, EmptyBlock, }; pub use crate::beacon_block_body::{ - BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyCapella, - BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyMerge, BeaconBlockBodyRef, + BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyBellatrix, + BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyRef, BeaconBlockBodyRefMut, }; pub use crate::beacon_block_header::BeaconBlockHeader; @@ -160,12 +160,12 @@ pub use crate::execution_block_hash::ExecutionBlockHash; pub use crate::execution_block_header::ExecutionBlockHeader; pub use crate::execution_layer_withdrawal_request::ExecutionLayerWithdrawalRequest; pub use crate::execution_payload::{ - ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, - ExecutionPayloadMerge, ExecutionPayloadRef, Transaction, Transactions, Withdrawals, + ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, + ExecutionPayloadElectra, ExecutionPayloadRef, Transaction, Transactions, Withdrawals, }; pub use crate::execution_payload_header::{ - ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, - ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, + ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, + ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, }; pub use crate::fork::Fork; @@ -200,9 +200,9 @@ pub use crate::light_client_update::{ pub use crate::participation_flags::ParticipationFlags; pub use crate::participation_list::ParticipationList; pub use crate::payload::{ - AbstractExecPayload, BlindedPayload, BlindedPayloadCapella, BlindedPayloadDeneb, - BlindedPayloadElectra, BlindedPayloadMerge, BlindedPayloadRef, BlockType, ExecPayload, - FullPayload, FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadMerge, + AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella, + BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadRef, BlockType, ExecPayload, + FullPayload, FullPayloadBellatrix, FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadRef, OwnedExecPayload, }; pub use crate::pending_attestation::PendingAttestation; @@ -221,9 +221,9 @@ pub use crate::shuffling_id::AttestationShufflingId; pub use crate::signed_aggregate_and_proof::SignedAggregateAndProof; pub use crate::signed_beacon_block::{ ssz_tagged_signed_beacon_block, ssz_tagged_signed_beacon_block_arc, SignedBeaconBlock, - SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockCapella, - SignedBeaconBlockDeneb, SignedBeaconBlockElectra, SignedBeaconBlockHash, - SignedBeaconBlockMerge, SignedBlindedBeaconBlock, + SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix, + SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra, + SignedBeaconBlockHash, SignedBlindedBeaconBlock, }; pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader; pub use crate::signed_bls_to_execution_change::SignedBlsToExecutionChange; diff --git a/consensus/types/src/light_client_bootstrap.rs b/consensus/types/src/light_client_bootstrap.rs index 4d42d357c19..61da0e1b117 100644 --- a/consensus/types/src/light_client_bootstrap.rs +++ b/consensus/types/src/light_client_bootstrap.rs @@ -78,7 +78,7 @@ impl LightClientBootstrap { pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result { let bootstrap = match fork_name { - ForkName::Altair | ForkName::Merge => { + ForkName::Altair | ForkName::Bellatrix => { Self::Altair(LightClientBootstrapAltair::from_ssz_bytes(bytes)?) } ForkName::Capella => Self::Capella(LightClientBootstrapCapella::from_ssz_bytes(bytes)?), @@ -101,7 +101,7 @@ impl LightClientBootstrap { match fork_name { ForkName::Base => 0, ForkName::Altair - | ForkName::Merge + | ForkName::Bellatrix | ForkName::Capella | ForkName::Deneb | ForkName::Electra => { @@ -128,7 +128,7 @@ impl LightClientBootstrap { .map_err(|_| Error::InconsistentFork)? { ForkName::Base => return Err(Error::AltairForkNotActive), - ForkName::Altair | ForkName::Merge => Self::Altair(LightClientBootstrapAltair { + ForkName::Altair | ForkName::Bellatrix => Self::Altair(LightClientBootstrapAltair { header: LightClientHeaderAltair::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch, diff --git a/consensus/types/src/light_client_finality_update.rs b/consensus/types/src/light_client_finality_update.rs index 5f83b0db523..29c526e2916 100644 --- a/consensus/types/src/light_client_finality_update.rs +++ b/consensus/types/src/light_client_finality_update.rs @@ -79,7 +79,7 @@ impl LightClientFinalityUpdate { .fork_name(chain_spec) .map_err(|_| Error::InconsistentFork)? { - ForkName::Altair | ForkName::Merge => { + ForkName::Altair | ForkName::Bellatrix => { let finality_update = LightClientFinalityUpdateAltair { attested_header: LightClientHeaderAltair::block_to_light_client_header( attested_block, @@ -147,7 +147,7 @@ impl LightClientFinalityUpdate { pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result { let finality_update = match fork_name { - ForkName::Altair | ForkName::Merge => { + ForkName::Altair | ForkName::Bellatrix => { Self::Altair(LightClientFinalityUpdateAltair::from_ssz_bytes(bytes)?) } ForkName::Capella => { @@ -172,7 +172,7 @@ impl LightClientFinalityUpdate { match fork_name { ForkName::Base => 0, ForkName::Altair - | ForkName::Merge + | ForkName::Bellatrix | ForkName::Capella | ForkName::Deneb | ForkName::Electra => { diff --git a/consensus/types/src/light_client_header.rs b/consensus/types/src/light_client_header.rs index 647ece99499..213ec90f955 100644 --- a/consensus/types/src/light_client_header.rs +++ b/consensus/types/src/light_client_header.rs @@ -75,7 +75,7 @@ impl LightClientHeader { .map_err(|_| Error::InconsistentFork)? { ForkName::Base => return Err(Error::AltairForkNotActive), - ForkName::Altair | ForkName::Merge => LightClientHeader::Altair( + ForkName::Altair | ForkName::Bellatrix => LightClientHeader::Altair( LightClientHeaderAltair::block_to_light_client_header(block)?, ), ForkName::Capella => LightClientHeader::Capella( @@ -90,7 +90,7 @@ impl LightClientHeader { pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result { let header = match fork_name { - ForkName::Altair | ForkName::Merge => { + ForkName::Altair | ForkName::Bellatrix => { LightClientHeader::Altair(LightClientHeaderAltair::from_ssz_bytes(bytes)?) } ForkName::Capella => { @@ -119,7 +119,7 @@ impl LightClientHeader { pub fn ssz_max_var_len_for_fork(fork_name: ForkName) -> usize { match fork_name { - ForkName::Base | ForkName::Altair | ForkName::Merge => 0, + ForkName::Base | ForkName::Altair | ForkName::Bellatrix => 0, ForkName::Capella | ForkName::Deneb | ForkName::Electra => { ExecutionPayloadHeader::::ssz_max_var_len_for_fork(fork_name) } @@ -198,7 +198,7 @@ impl ForkVersionDeserialize for LightClientHeader { fork_name: ForkName, ) -> Result { match fork_name { - ForkName::Altair | ForkName::Merge => serde_json::from_value(value) + ForkName::Altair | ForkName::Bellatrix => serde_json::from_value(value) .map(|light_client_header| Self::Altair(light_client_header)) .map_err(serde::de::Error::custom), ForkName::Capella => serde_json::from_value(value) diff --git a/consensus/types/src/light_client_optimistic_update.rs b/consensus/types/src/light_client_optimistic_update.rs index d22c4535f1e..4727673f6c0 100644 --- a/consensus/types/src/light_client_optimistic_update.rs +++ b/consensus/types/src/light_client_optimistic_update.rs @@ -70,13 +70,15 @@ impl LightClientOptimisticUpdate { .fork_name(chain_spec) .map_err(|_| Error::InconsistentFork)? { - ForkName::Altair | ForkName::Merge => Self::Altair(LightClientOptimisticUpdateAltair { - attested_header: LightClientHeaderAltair::block_to_light_client_header( - attested_block, - )?, - sync_aggregate, - signature_slot, - }), + ForkName::Altair | ForkName::Bellatrix => { + Self::Altair(LightClientOptimisticUpdateAltair { + attested_header: LightClientHeaderAltair::block_to_light_client_header( + attested_block, + )?, + sync_aggregate, + signature_slot, + }) + } ForkName::Capella => Self::Capella(LightClientOptimisticUpdateCapella { attested_header: LightClientHeaderCapella::block_to_light_client_header( attested_block, @@ -131,7 +133,7 @@ impl LightClientOptimisticUpdate { pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result { let optimistic_update = match fork_name { - ForkName::Altair | ForkName::Merge => { + ForkName::Altair | ForkName::Bellatrix => { Self::Altair(LightClientOptimisticUpdateAltair::from_ssz_bytes(bytes)?) } ForkName::Capella => { @@ -156,7 +158,7 @@ impl LightClientOptimisticUpdate { match fork_name { ForkName::Base => 0, ForkName::Altair - | ForkName::Merge + | ForkName::Bellatrix | ForkName::Capella | ForkName::Deneb | ForkName::Electra => { diff --git a/consensus/types/src/light_client_update.rs b/consensus/types/src/light_client_update.rs index d5e8cd592df..002fbea2d37 100644 --- a/consensus/types/src/light_client_update.rs +++ b/consensus/types/src/light_client_update.rs @@ -191,7 +191,7 @@ impl LightClientUpdate { .map_err(|_| Error::InconsistentFork)? { ForkName::Base => return Err(Error::AltairForkNotActive), - ForkName::Altair | ForkName::Merge => { + ForkName::Altair | ForkName::Bellatrix => { let attested_header = LightClientHeaderAltair::block_to_light_client_header(attested_block)?; let finalized_header = @@ -243,7 +243,7 @@ impl LightClientUpdate { pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result { let update = match fork_name { - ForkName::Altair | ForkName::Merge => { + ForkName::Altair | ForkName::Bellatrix => { Self::Altair(LightClientUpdateAltair::from_ssz_bytes(bytes)?) } ForkName::Capella => Self::Capella(LightClientUpdateCapella::from_ssz_bytes(bytes)?), diff --git a/consensus/types/src/payload.rs b/consensus/types/src/payload.rs index 18b3199bd35..80a70c171f5 100644 --- a/consensus/types/src/payload.rs +++ b/consensus/types/src/payload.rs @@ -79,22 +79,22 @@ pub trait AbstractExecPayload: + Sized + From> + TryFrom> - + TryInto + + TryInto + TryInto + TryInto + TryInto { type Ref<'a>: ExecPayload + Copy - + From<&'a Self::Merge> + + From<&'a Self::Bellatrix> + From<&'a Self::Capella> + From<&'a Self::Deneb> + From<&'a Self::Electra>; - type Merge: OwnedExecPayload + type Bellatrix: OwnedExecPayload + Into - + for<'a> From>> - + TryFrom>; + + for<'a> From>> + + TryFrom>; type Capella: OwnedExecPayload + Into + for<'a> From>> @@ -110,7 +110,7 @@ pub trait AbstractExecPayload: } #[superstruct( - variants(Merge, Capella, Deneb, Electra), + variants(Bellatrix, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -145,8 +145,11 @@ pub trait AbstractExecPayload: #[arbitrary(bound = "E: EthSpec")] #[tree_hash(enum_behaviour = "transparent")] pub struct FullPayload { - #[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))] - pub execution_payload: ExecutionPayloadMerge, + #[superstruct( + only(Bellatrix), + partial_getter(rename = "execution_payload_bellatrix") + )] + pub execution_payload: ExecutionPayloadBellatrix, #[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))] pub execution_payload: ExecutionPayloadCapella, #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))] @@ -252,7 +255,7 @@ impl ExecPayload for FullPayload { fn withdrawals_root(&self) -> Result { match self { - FullPayload::Merge(_) => Err(Error::IncorrectStateVariant), + FullPayload::Bellatrix(_) => Err(Error::IncorrectStateVariant), FullPayload::Capella(ref inner) => { Ok(inner.execution_payload.withdrawals.tree_hash_root()) } @@ -267,7 +270,9 @@ impl ExecPayload for FullPayload { fn blob_gas_used(&self) -> Result { match self { - FullPayload::Merge(_) | FullPayload::Capella(_) => Err(Error::IncorrectStateVariant), + FullPayload::Bellatrix(_) | FullPayload::Capella(_) => { + Err(Error::IncorrectStateVariant) + } FullPayload::Deneb(ref inner) => Ok(inner.execution_payload.blob_gas_used), FullPayload::Electra(ref inner) => Ok(inner.execution_payload.blob_gas_used), } @@ -296,7 +301,7 @@ impl FullPayload { pub fn default_at_fork(fork_name: ForkName) -> Result { match fork_name { ForkName::Base | ForkName::Altair => Err(Error::IncorrectStateVariant), - ForkName::Merge => Ok(FullPayloadMerge::default().into()), + ForkName::Bellatrix => Ok(FullPayloadBellatrix::default().into()), ForkName::Capella => Ok(FullPayloadCapella::default().into()), ForkName::Deneb => Ok(FullPayloadDeneb::default().into()), ForkName::Electra => Ok(FullPayloadElectra::default().into()), @@ -382,7 +387,7 @@ impl<'b, E: EthSpec> ExecPayload for FullPayloadRef<'b, E> { fn withdrawals_root(&self) -> Result { match self { - FullPayloadRef::Merge(_) => Err(Error::IncorrectStateVariant), + FullPayloadRef::Bellatrix(_) => Err(Error::IncorrectStateVariant), FullPayloadRef::Capella(inner) => { Ok(inner.execution_payload.withdrawals.tree_hash_root()) } @@ -397,7 +402,7 @@ impl<'b, E: EthSpec> ExecPayload for FullPayloadRef<'b, E> { fn blob_gas_used(&self) -> Result { match self { - FullPayloadRef::Merge(_) | FullPayloadRef::Capella(_) => { + FullPayloadRef::Bellatrix(_) | FullPayloadRef::Capella(_) => { Err(Error::IncorrectStateVariant) } FullPayloadRef::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used), @@ -420,7 +425,7 @@ impl<'b, E: EthSpec> ExecPayload for FullPayloadRef<'b, E> { impl AbstractExecPayload for FullPayload { type Ref<'a> = FullPayloadRef<'a, E>; - type Merge = FullPayloadMerge; + type Bellatrix = FullPayloadBellatrix; type Capella = FullPayloadCapella; type Deneb = FullPayloadDeneb; type Electra = FullPayloadElectra; @@ -442,7 +447,7 @@ impl TryFrom> for FullPayload { } #[superstruct( - variants(Merge, Capella, Deneb, Electra), + variants(Bellatrix, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -476,8 +481,11 @@ impl TryFrom> for FullPayload { #[arbitrary(bound = "E: EthSpec")] #[tree_hash(enum_behaviour = "transparent")] pub struct BlindedPayload { - #[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))] - pub execution_payload_header: ExecutionPayloadHeaderMerge, + #[superstruct( + only(Bellatrix), + partial_getter(rename = "execution_payload_bellatrix") + )] + pub execution_payload_header: ExecutionPayloadHeaderBellatrix, #[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))] pub execution_payload_header: ExecutionPayloadHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))] @@ -561,7 +569,7 @@ impl ExecPayload for BlindedPayload { fn withdrawals_root(&self) -> Result { match self { - BlindedPayload::Merge(_) => Err(Error::IncorrectStateVariant), + BlindedPayload::Bellatrix(_) => Err(Error::IncorrectStateVariant), BlindedPayload::Capella(ref inner) => { Ok(inner.execution_payload_header.withdrawals_root) } @@ -574,7 +582,7 @@ impl ExecPayload for BlindedPayload { fn blob_gas_used(&self) -> Result { match self { - BlindedPayload::Merge(_) | BlindedPayload::Capella(_) => { + BlindedPayload::Bellatrix(_) | BlindedPayload::Capella(_) => { Err(Error::IncorrectStateVariant) } BlindedPayload::Deneb(ref inner) => Ok(inner.execution_payload_header.blob_gas_used), @@ -662,7 +670,7 @@ impl<'b, E: EthSpec> ExecPayload for BlindedPayloadRef<'b, E> { fn withdrawals_root(&self) -> Result { match self { - BlindedPayloadRef::Merge(_) => Err(Error::IncorrectStateVariant), + BlindedPayloadRef::Bellatrix(_) => Err(Error::IncorrectStateVariant), BlindedPayloadRef::Capella(inner) => { Ok(inner.execution_payload_header.withdrawals_root) } @@ -675,7 +683,7 @@ impl<'b, E: EthSpec> ExecPayload for BlindedPayloadRef<'b, E> { fn blob_gas_used(&self) -> Result { match self { - BlindedPayloadRef::Merge(_) | BlindedPayloadRef::Capella(_) => { + BlindedPayloadRef::Bellatrix(_) | BlindedPayloadRef::Capella(_) => { Err(Error::IncorrectStateVariant) } BlindedPayloadRef::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used), @@ -699,12 +707,12 @@ impl<'b, E: EthSpec> ExecPayload for BlindedPayloadRef<'b, E> { } macro_rules! impl_exec_payload_common { - ($wrapper_type:ident, // BlindedPayloadMerge | FullPayloadMerge - $wrapped_type:ident, // ExecutionPayloadHeaderMerge | ExecutionPayloadMerge - $wrapped_type_full:ident, // ExecutionPayloadMerge | ExecutionPayloadMerge - $wrapped_type_header:ident, // ExecutionPayloadHeaderMerge | ExecutionPayloadHeaderMerge + ($wrapper_type:ident, // BlindedPayloadBellatrix | FullPayloadBellatrix + $wrapped_type:ident, // ExecutionPayloadHeaderBellatrix | ExecutionPayloadBellatrix + $wrapped_type_full:ident, // ExecutionPayloadBellatrix | ExecutionPayloadBellatrix + $wrapped_type_header:ident, // ExecutionPayloadHeaderBellatrix | ExecutionPayloadHeaderBellatrix $wrapped_field:ident, // execution_payload_header | execution_payload - $fork_variant:ident, // Merge | Merge + $fork_variant:ident, // Bellatrix | Bellatrix $block_type_variant:ident, // Blinded | Full $is_default_with_empty_roots:block, $f:block, @@ -783,17 +791,17 @@ macro_rules! impl_exec_payload_common { } macro_rules! impl_exec_payload_for_fork { - // BlindedPayloadMerge, FullPayloadMerge, ExecutionPayloadHeaderMerge, ExecutionPayloadMerge, Merge + // BlindedPayloadBellatrix, FullPayloadBellatrix, ExecutionPayloadHeaderBellatrix, ExecutionPayloadBellatrix, Bellatrix ($wrapper_type_header:ident, $wrapper_type_full:ident, $wrapped_type_header:ident, $wrapped_type_full:ident, $fork_variant:ident) => { //*************** Blinded payload implementations ******************// impl_exec_payload_common!( - $wrapper_type_header, // BlindedPayloadMerge - $wrapped_type_header, // ExecutionPayloadHeaderMerge - $wrapped_type_full, // ExecutionPayloadMerge - $wrapped_type_header, // ExecutionPayloadHeaderMerge + $wrapper_type_header, // BlindedPayloadBellatrix + $wrapped_type_header, // ExecutionPayloadHeaderBellatrix + $wrapped_type_full, // ExecutionPayloadBellatrix + $wrapped_type_header, // ExecutionPayloadHeaderBellatrix execution_payload_header, - $fork_variant, // Merge + $fork_variant, // Bellatrix Blinded, { |wrapper: &$wrapper_type_header| { @@ -872,12 +880,12 @@ macro_rules! impl_exec_payload_for_fork { //*************** Full payload implementations ******************// impl_exec_payload_common!( - $wrapper_type_full, // FullPayloadMerge - $wrapped_type_full, // ExecutionPayloadMerge - $wrapped_type_full, // ExecutionPayloadMerge - $wrapped_type_header, // ExecutionPayloadHeaderMerge + $wrapper_type_full, // FullPayloadBellatrix + $wrapped_type_full, // ExecutionPayloadBellatrix + $wrapped_type_full, // ExecutionPayloadBellatrix + $wrapped_type_header, // ExecutionPayloadHeaderBellatrix execution_payload, - $fork_variant, // Merge + $fork_variant, // Bellatrix Full, { |wrapper: &$wrapper_type_full| { @@ -952,11 +960,11 @@ macro_rules! impl_exec_payload_for_fork { } impl_exec_payload_for_fork!( - BlindedPayloadMerge, - FullPayloadMerge, - ExecutionPayloadHeaderMerge, - ExecutionPayloadMerge, - Merge + BlindedPayloadBellatrix, + FullPayloadBellatrix, + ExecutionPayloadHeaderBellatrix, + ExecutionPayloadBellatrix, + Bellatrix ); impl_exec_payload_for_fork!( BlindedPayloadCapella, @@ -982,7 +990,7 @@ impl_exec_payload_for_fork!( impl AbstractExecPayload for BlindedPayload { type Ref<'a> = BlindedPayloadRef<'a, E>; - type Merge = BlindedPayloadMerge; + type Bellatrix = BlindedPayloadBellatrix; type Capella = BlindedPayloadCapella; type Deneb = BlindedPayloadDeneb; type Electra = BlindedPayloadElectra; @@ -1002,8 +1010,8 @@ impl From> for BlindedPayload { impl From> for BlindedPayload { fn from(execution_payload_header: ExecutionPayloadHeader) -> Self { match execution_payload_header { - ExecutionPayloadHeader::Merge(execution_payload_header) => { - Self::Merge(BlindedPayloadMerge { + ExecutionPayloadHeader::Bellatrix(execution_payload_header) => { + Self::Bellatrix(BlindedPayloadBellatrix { execution_payload_header, }) } @@ -1029,8 +1037,8 @@ impl From> for BlindedPayload { impl From> for ExecutionPayloadHeader { fn from(blinded: BlindedPayload) -> Self { match blinded { - BlindedPayload::Merge(blinded_payload) => { - ExecutionPayloadHeader::Merge(blinded_payload.execution_payload_header) + BlindedPayload::Bellatrix(blinded_payload) => { + ExecutionPayloadHeader::Bellatrix(blinded_payload.execution_payload_header) } BlindedPayload::Capella(blinded_payload) => { ExecutionPayloadHeader::Capella(blinded_payload.execution_payload_header) diff --git a/consensus/types/src/preset.rs b/consensus/types/src/preset.rs index 626a45d971d..f4008d62e1d 100644 --- a/consensus/types/src/preset.rs +++ b/consensus/types/src/preset.rs @@ -230,13 +230,50 @@ impl DenebPreset { #[serde(rename_all = "UPPERCASE")] pub struct ElectraPreset { #[serde(with = "serde_utils::quoted_u64")] - pub electra_placeholder: u64, + pub min_activation_balance: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub max_effective_balance_electra: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub min_slashing_penalty_quotient_electra: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub whistleblower_reward_quotient_electra: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub max_pending_partials_per_withdrawals_sweep: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub pending_balance_deposits_limit: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub pending_partial_withdrawals_limit: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub pending_consolidations_limit: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub max_consolidations: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub max_deposit_receipts_per_payload: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub max_attester_slashings_electra: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub max_attestations_electra: u64, + #[serde(with = "serde_utils::quoted_u64")] + pub max_withdrawal_requests_per_payload: u64, } impl ElectraPreset { - pub fn from_chain_spec(_spec: &ChainSpec) -> Self { + pub fn from_chain_spec(spec: &ChainSpec) -> Self { Self { - electra_placeholder: 0, + min_activation_balance: spec.min_activation_balance, + max_effective_balance_electra: spec.max_effective_balance_electra, + min_slashing_penalty_quotient_electra: spec.min_slashing_penalty_quotient_electra, + whistleblower_reward_quotient_electra: spec.whistleblower_reward_quotient_electra, + max_pending_partials_per_withdrawals_sweep: spec + .max_pending_partials_per_withdrawals_sweep, + pending_balance_deposits_limit: E::pending_balance_deposits_limit() as u64, + pending_partial_withdrawals_limit: E::pending_partial_withdrawals_limit() as u64, + pending_consolidations_limit: E::pending_consolidations_limit() as u64, + max_consolidations: E::max_consolidations() as u64, + max_deposit_receipts_per_payload: E::max_deposit_receipts_per_payload() as u64, + max_attester_slashings_electra: E::max_attester_slashings_electra() as u64, + max_attestations_electra: E::max_attestations_electra() as u64, + max_withdrawal_requests_per_payload: E::max_withdrawal_requests_per_payload() as u64, } } } diff --git a/consensus/types/src/signed_beacon_block.rs b/consensus/types/src/signed_beacon_block.rs index 6c12277700a..a22df49ad7b 100644 --- a/consensus/types/src/signed_beacon_block.rs +++ b/consensus/types/src/signed_beacon_block.rs @@ -37,7 +37,7 @@ impl From for Hash256 { /// A `BeaconBlock` and a signature from its proposer. #[superstruct( - variants(Base, Altair, Merge, Capella, Deneb, Electra), + variants(Base, Altair, Bellatrix, Capella, Deneb, Electra), variant_attributes( derive( Debug, @@ -72,8 +72,8 @@ pub struct SignedBeaconBlock = FullP pub message: BeaconBlockBase, #[superstruct(only(Altair), partial_getter(rename = "message_altair"))] pub message: BeaconBlockAltair, - #[superstruct(only(Merge), partial_getter(rename = "message_merge"))] - pub message: BeaconBlockMerge, + #[superstruct(only(Bellatrix), partial_getter(rename = "message_bellatrix"))] + pub message: BeaconBlockBellatrix, #[superstruct(only(Capella), partial_getter(rename = "message_capella"))] pub message: BeaconBlockCapella, #[superstruct(only(Deneb), partial_getter(rename = "message_deneb"))] @@ -150,8 +150,8 @@ impl> SignedBeaconBlock BeaconBlock::Altair(message) => { SignedBeaconBlock::Altair(SignedBeaconBlockAltair { message, signature }) } - BeaconBlock::Merge(message) => { - SignedBeaconBlock::Merge(SignedBeaconBlockMerge { message, signature }) + BeaconBlock::Bellatrix(message) => { + SignedBeaconBlock::Bellatrix(SignedBeaconBlockBellatrix { message, signature }) } BeaconBlock::Capella(message) => { SignedBeaconBlock::Capella(SignedBeaconBlockCapella { message, signature }) @@ -310,20 +310,20 @@ impl From>> // Post-Bellatrix blocks can be "unblinded" by adding the full payload. // NOTE: It might be nice to come up with a `superstruct` pattern to abstract over this before // the first fork after Bellatrix. -impl SignedBeaconBlockMerge> { +impl SignedBeaconBlockBellatrix> { pub fn into_full_block( self, - execution_payload: ExecutionPayloadMerge, - ) -> SignedBeaconBlockMerge> { - let SignedBeaconBlockMerge { + execution_payload: ExecutionPayloadBellatrix, + ) -> SignedBeaconBlockBellatrix> { + let SignedBeaconBlockBellatrix { message: - BeaconBlockMerge { + BeaconBlockBellatrix { slot, proposer_index, parent_root, state_root, body: - BeaconBlockBodyMerge { + BeaconBlockBodyBellatrix { randao_reveal, eth1_data, graffiti, @@ -333,18 +333,18 @@ impl SignedBeaconBlockMerge> { deposits, voluntary_exits, sync_aggregate, - execution_payload: BlindedPayloadMerge { .. }, + execution_payload: BlindedPayloadBellatrix { .. }, }, }, signature, } = self; - SignedBeaconBlockMerge { - message: BeaconBlockMerge { + SignedBeaconBlockBellatrix { + message: BeaconBlockBellatrix { slot, proposer_index, parent_root, state_root, - body: BeaconBlockBodyMerge { + body: BeaconBlockBodyBellatrix { randao_reveal, eth1_data, graffiti, @@ -354,7 +354,7 @@ impl SignedBeaconBlockMerge> { deposits, voluntary_exits, sync_aggregate, - execution_payload: FullPayloadMerge { execution_payload }, + execution_payload: FullPayloadBellatrix { execution_payload }, }, }, signature, @@ -538,8 +538,8 @@ impl SignedBeaconBlock> { let full_block = match (self, execution_payload) { (SignedBeaconBlock::Base(block), _) => SignedBeaconBlock::Base(block.into()), (SignedBeaconBlock::Altair(block), _) => SignedBeaconBlock::Altair(block.into()), - (SignedBeaconBlock::Merge(block), Some(ExecutionPayload::Merge(payload))) => { - SignedBeaconBlock::Merge(block.into_full_block(payload)) + (SignedBeaconBlock::Bellatrix(block), Some(ExecutionPayload::Bellatrix(payload))) => { + SignedBeaconBlock::Bellatrix(block.into_full_block(payload)) } (SignedBeaconBlock::Capella(block), Some(ExecutionPayload::Capella(payload))) => { SignedBeaconBlock::Capella(block.into_full_block(payload)) @@ -552,7 +552,7 @@ impl SignedBeaconBlock> { } // avoid wildcard matching forks so that compiler will // direct us here when a new fork has been added - (SignedBeaconBlock::Merge(_), _) => return None, + (SignedBeaconBlock::Bellatrix(_), _) => return None, (SignedBeaconBlock::Capella(_), _) => return None, (SignedBeaconBlock::Deneb(_), _) => return None, (SignedBeaconBlock::Electra(_), _) => return None, @@ -689,8 +689,8 @@ pub mod ssz_tagged_signed_beacon_block { ForkName::Altair => Ok(SignedBeaconBlock::Altair( SignedBeaconBlockAltair::from_ssz_bytes(body)?, )), - ForkName::Merge => Ok(SignedBeaconBlock::Merge( - SignedBeaconBlockMerge::from_ssz_bytes(body)?, + ForkName::Bellatrix => Ok(SignedBeaconBlock::Bellatrix( + SignedBeaconBlockBellatrix::from_ssz_bytes(body)?, )), ForkName::Capella => Ok(SignedBeaconBlock::Capella( SignedBeaconBlockCapella::from_ssz_bytes(body)?, @@ -746,7 +746,10 @@ mod test { BeaconBlock::Altair(BeaconBlockAltair::empty(spec)), sig.clone(), ), - SignedBeaconBlock::from_block(BeaconBlock::Merge(BeaconBlockMerge::empty(spec)), sig), + SignedBeaconBlock::from_block( + BeaconBlock::Bellatrix(BeaconBlockBellatrix::empty(spec)), + sig, + ), ]; for block in blocks { @@ -785,7 +788,7 @@ mod test { sig.clone(), ), SignedBeaconBlock::from_block( - BeaconBlock::Merge(BeaconBlockMerge::empty(spec)), + BeaconBlock::Bellatrix(BeaconBlockBellatrix::empty(spec)), sig.clone(), ), SignedBeaconBlock::from_block( diff --git a/consensus/types/src/test_utils/test_random/secret_key.rs b/consensus/types/src/test_utils/test_random/secret_key.rs index 3f3f6ed5184..da1614aa24e 100644 --- a/consensus/types/src/test_utils/test_random/secret_key.rs +++ b/consensus/types/src/test_utils/test_random/secret_key.rs @@ -2,6 +2,8 @@ use super::*; impl TestRandom for SecretKey { fn random_for_test(_rng: &mut impl RngCore) -> Self { + // TODO: Not deterministic generation. Using `SecretKey::deserialize` results in + // `BlstError(BLST_BAD_ENCODING)`, need to debug with blst source on what encoding expects. SecretKey::random() } } diff --git a/consensus/types/src/test_utils/test_random/signature.rs b/consensus/types/src/test_utils/test_random/signature.rs index 5b952296b61..8bc0d711103 100644 --- a/consensus/types/src/test_utils/test_random/signature.rs +++ b/consensus/types/src/test_utils/test_random/signature.rs @@ -1,11 +1,10 @@ use super::*; impl TestRandom for Signature { - fn random_for_test(rng: &mut impl RngCore) -> Self { - let secret_key = SecretKey::random_for_test(rng); - let mut message = vec![0; 32]; - rng.fill_bytes(&mut message); - - secret_key.sign(Hash256::from_slice(&message)) + fn random_for_test(_rng: &mut impl RngCore) -> Self { + // TODO: `SecretKey::random_for_test` does not return a deterministic signature. Since this + // signature will not pass verification we could just return the generator point or the + // generator point multiplied by a random scalar if we want disctint signatures. + Signature::infinity().expect("infinity signature is valid") } } diff --git a/consensus/types/src/validator.rs b/consensus/types/src/validator.rs index 98567cd1e6c..8ed449ec8a7 100644 --- a/consensus/types/src/validator.rs +++ b/consensus/types/src/validator.rs @@ -57,10 +57,10 @@ impl Validator { /// Returns `true` if the validator is eligible to join the activation queue. /// - /// Spec v0.12.1 + /// Modified in electra pub fn is_eligible_for_activation_queue(&self, spec: &ChainSpec) -> bool { self.activation_eligibility_epoch == spec.far_future_epoch - && self.effective_balance == spec.max_effective_balance + && self.effective_balance >= spec.min_activation_balance } /// Returns `true` if the validator is eligible to be activated. @@ -102,6 +102,11 @@ impl Validator { .unwrap_or(false) } + /// Check if ``validator`` has an 0x02 prefixed "compounding" withdrawal credential. + pub fn has_compounding_withdrawal_credential(&self, spec: &ChainSpec) -> bool { + is_compounding_withdrawal_credential(self.withdrawal_credentials, spec) + } + /// Get the eth1 withdrawal address if this validator has one initialized. pub fn get_eth1_withdrawal_address(&self, spec: &ChainSpec) -> Option
{ self.has_eth1_withdrawal_credential(spec) @@ -125,15 +130,39 @@ impl Validator { } /// Returns `true` if the validator is fully withdrawable at some epoch. + /// + /// Note: Modified in electra. pub fn is_fully_withdrawable_at(&self, balance: u64, epoch: Epoch, spec: &ChainSpec) -> bool { - self.has_eth1_withdrawal_credential(spec) && self.withdrawable_epoch <= epoch && balance > 0 + self.has_execution_withdrawal_credential(spec) + && self.withdrawable_epoch <= epoch + && balance > 0 } /// Returns `true` if the validator is partially withdrawable. + /// + /// Note: Modified in electra. pub fn is_partially_withdrawable_validator(&self, balance: u64, spec: &ChainSpec) -> bool { - self.has_eth1_withdrawal_credential(spec) - && self.effective_balance == spec.max_effective_balance - && balance > spec.max_effective_balance + let max_effective_balance = self.get_validator_max_effective_balance(spec); + let has_max_effective_balance = self.effective_balance == max_effective_balance; + let has_excess_balance = balance > max_effective_balance; + self.has_execution_withdrawal_credential(spec) + && has_max_effective_balance + && has_excess_balance + } + + /// Returns `true` if the validator has a 0x01 or 0x02 prefixed withdrawal credential. + pub fn has_execution_withdrawal_credential(&self, spec: &ChainSpec) -> bool { + self.has_compounding_withdrawal_credential(spec) + || self.has_eth1_withdrawal_credential(spec) + } + + /// Returns the max effective balance for a validator in gwei. + pub fn get_validator_max_effective_balance(&self, spec: &ChainSpec) -> u64 { + if self.has_compounding_withdrawal_credential(spec) { + spec.max_effective_balance_electra + } else { + spec.min_activation_balance + } } } @@ -153,6 +182,17 @@ impl Default for Validator { } } +pub fn is_compounding_withdrawal_credential( + withdrawal_credentials: Hash256, + spec: &ChainSpec, +) -> bool { + withdrawal_credentials + .as_bytes() + .first() + .map(|prefix_byte| *prefix_byte == spec.compounding_withdrawal_prefix_byte) + .unwrap_or(false) +} + #[cfg(test)] mod tests { use super::*; diff --git a/consensus/types/src/voluntary_exit.rs b/consensus/types/src/voluntary_exit.rs index 74175423e34..4c7c16757ed 100644 --- a/consensus/types/src/voluntary_exit.rs +++ b/consensus/types/src/voluntary_exit.rs @@ -42,7 +42,7 @@ impl VoluntaryExit { ) -> SignedVoluntaryExit { let fork_name = spec.fork_name_at_epoch(self.epoch); let fork_version = match fork_name { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { spec.fork_version_for_name(fork_name) } // EIP-7044 diff --git a/lcli/src/create_payload_header.rs b/lcli/src/create_payload_header.rs index 7aa1ef70089..974a34591f0 100644 --- a/lcli/src/create_payload_header.rs +++ b/lcli/src/create_payload_header.rs @@ -5,8 +5,9 @@ use std::fs::File; use std::io::Write; use std::time::{SystemTime, UNIX_EPOCH}; use types::{ - EthSpec, ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, - ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderMerge, ForkName, + EthSpec, ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, + ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, + ForkName, }; pub fn run(matches: &ArgMatches) -> Result<(), String> { @@ -20,17 +21,17 @@ pub fn run(matches: &ArgMatches) -> Result<(), String> { let base_fee_per_gas = parse_required(matches, "base-fee-per-gas")?; let gas_limit = parse_required(matches, "gas-limit")?; let file_name = matches.value_of("file").ok_or("No file supplied")?; - let fork_name: ForkName = parse_optional(matches, "fork")?.unwrap_or(ForkName::Merge); + let fork_name: ForkName = parse_optional(matches, "fork")?.unwrap_or(ForkName::Bellatrix); let execution_payload_header: ExecutionPayloadHeader = match fork_name { ForkName::Base | ForkName::Altair => return Err("invalid fork name".to_string()), - ForkName::Merge => ExecutionPayloadHeader::Merge(ExecutionPayloadHeaderMerge { + ForkName::Bellatrix => ExecutionPayloadHeader::Bellatrix(ExecutionPayloadHeaderBellatrix { gas_limit, base_fee_per_gas, timestamp: genesis_time, block_hash: eth1_block_hash, prev_randao: eth1_block_hash.into_root(), - ..ExecutionPayloadHeaderMerge::default() + ..ExecutionPayloadHeaderBellatrix::default() }), ForkName::Capella => ExecutionPayloadHeader::Capella(ExecutionPayloadHeaderCapella { gas_limit, diff --git a/lcli/src/main.rs b/lcli/src/main.rs index c374a8f4b37..7b5c1598c9e 100644 --- a/lcli/src/main.rs +++ b/lcli/src/main.rs @@ -433,7 +433,7 @@ fn main() { .takes_value(true) .default_value("bellatrix") .help("The fork for which the execution payload header should be created.") - .possible_values(&["merge", "bellatrix", "capella", "deneb", "electra"]) + .possible_values(&["bellatrix", "capella", "deneb", "electra"]) ) ) .subcommand( diff --git a/lcli/src/new_testnet.rs b/lcli/src/new_testnet.rs index edba4249966..f6bfb2ac013 100644 --- a/lcli/src/new_testnet.rs +++ b/lcli/src/new_testnet.rs @@ -21,8 +21,8 @@ use std::time::{SystemTime, UNIX_EPOCH}; use types::ExecutionBlockHash; use types::{ test_utils::generate_deterministic_keypairs, Address, BeaconState, ChainSpec, Config, Epoch, - Eth1Data, EthSpec, ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, - ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderMerge, + Eth1Data, EthSpec, ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, + ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ForkName, Hash256, Keypair, PublicKey, Validator, }; @@ -114,9 +114,9 @@ pub fn run(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Resul ForkName::Base | ForkName::Altair => Err(ssz::DecodeError::BytesInvalid( "genesis fork must be post-merge".to_string(), )), - ForkName::Merge => { - ExecutionPayloadHeaderMerge::::from_ssz_bytes(bytes.as_slice()) - .map(ExecutionPayloadHeader::Merge) + ForkName::Bellatrix => { + ExecutionPayloadHeaderBellatrix::::from_ssz_bytes(bytes.as_slice()) + .map(ExecutionPayloadHeader::Bellatrix) } ForkName::Capella => { ExecutionPayloadHeaderCapella::::from_ssz_bytes(bytes.as_slice()) @@ -246,10 +246,10 @@ fn initialize_state_with_validators( ) -> Result, String> { // If no header is provided, then start from a Bellatrix state by default let default_header: ExecutionPayloadHeader = - ExecutionPayloadHeader::Merge(ExecutionPayloadHeaderMerge { + ExecutionPayloadHeader::Bellatrix(ExecutionPayloadHeaderBellatrix { block_hash: ExecutionBlockHash::from_root(eth1_block_hash), parent_hash: ExecutionBlockHash::zero(), - ..ExecutionPayloadHeaderMerge::default() + ..ExecutionPayloadHeaderBellatrix::default() }); let execution_payload_header = execution_payload_header.unwrap_or(default_header); // Empty eth1 data @@ -314,9 +314,9 @@ fn initialize_state_with_validators( // Override latest execution payload header. // See https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/bellatrix/beacon-chain.md#testing - if let ExecutionPayloadHeader::Merge(ref header) = execution_payload_header { + if let ExecutionPayloadHeader::Bellatrix(ref header) = execution_payload_header { *state - .latest_execution_payload_header_merge_mut() + .latest_execution_payload_header_bellatrix_mut() .or(Err("mismatched fork".to_string()))? = header.clone(); } } diff --git a/lcli/src/parse_ssz.rs b/lcli/src/parse_ssz.rs index 5b34fedfb9e..e86ffb73dc2 100644 --- a/lcli/src/parse_ssz.rs +++ b/lcli/src/parse_ssz.rs @@ -70,9 +70,11 @@ pub fn run_parse_ssz( "SignedBeaconBlockAltair" => { decode_and_print(&bytes, SignedBeaconBlockAltair::::from_ssz_bytes, format)? } - "SignedBeaconBlockMerge" | "SignedBeaconBlockBellatrix" => { - decode_and_print(&bytes, SignedBeaconBlockMerge::::from_ssz_bytes, format)? - } + "SignedBeaconBlockBellatrix" => decode_and_print( + &bytes, + SignedBeaconBlockBellatrix::::from_ssz_bytes, + format, + )?, "SignedBeaconBlockCapella" => decode_and_print( &bytes, SignedBeaconBlockCapella::::from_ssz_bytes, @@ -97,8 +99,8 @@ pub fn run_parse_ssz( "BeaconStateAltair" => { decode_and_print(&bytes, BeaconStateAltair::::from_ssz_bytes, format)? } - "BeaconStateMerge" | "BeaconStateBellatrix" => { - decode_and_print(&bytes, BeaconStateMerge::::from_ssz_bytes, format)? + "BeaconStateBellatrix" => { + decode_and_print(&bytes, BeaconStateBellatrix::::from_ssz_bytes, format)? } "BeaconStateCapella" => { decode_and_print(&bytes, BeaconStateCapella::::from_ssz_bytes, format)? diff --git a/lighthouse/environment/tests/testnet_dir/config.yaml b/lighthouse/environment/tests/testnet_dir/config.yaml index 86f4dce239b..c71feaa7dc5 100644 --- a/lighthouse/environment/tests/testnet_dir/config.yaml +++ b/lighthouse/environment/tests/testnet_dir/config.yaml @@ -32,7 +32,7 @@ GENESIS_DELAY: 604800 # Altair ALTAIR_FORK_VERSION: 0x01000000 ALTAIR_FORK_EPOCH: 18446744073709551615 -# Merge +# Bellatrix BELLATRIX_FORK_VERSION: 0x02000000 BELLATRIX_FORK_EPOCH: 18446744073709551615 # Sharding diff --git a/lighthouse/tests/beacon_node.rs b/lighthouse/tests/beacon_node.rs index 9f1e3d235d9..62bb067273e 100644 --- a/lighthouse/tests/beacon_node.rs +++ b/lighthouse/tests/beacon_node.rs @@ -416,7 +416,7 @@ fn eth1_cache_follow_distance_manual() { } // Tests for Bellatrix flags. -fn run_merge_execution_endpoints_flag_test(flag: &str) { +fn run_bellatrix_execution_endpoints_flag_test(flag: &str) { use sensitive_url::SensitiveUrl; let urls = vec!["http://sigp.io/no-way:1337", "http://infura.not_real:4242"]; // we don't support redundancy for execution-endpoints @@ -500,15 +500,15 @@ fn execution_timeout_multiplier_flag() { }); } #[test] -fn merge_execution_endpoints_flag() { - run_merge_execution_endpoints_flag_test("execution-endpoints") +fn bellatrix_execution_endpoints_flag() { + run_bellatrix_execution_endpoints_flag_test("execution-endpoints") } #[test] -fn merge_execution_endpoint_flag() { - run_merge_execution_endpoints_flag_test("execution-endpoint") +fn bellatrix_execution_endpoint_flag() { + run_bellatrix_execution_endpoints_flag_test("execution-endpoint") } #[test] -fn merge_jwt_secrets_flag() { +fn bellatrix_jwt_secrets_flag() { let dir = TempDir::new().expect("Unable to create temporary directory"); let mut file = File::create(dir.path().join("jwtsecrets")).expect("Unable to create file"); file.write_all(b"0x3cbc11b0d8fa16f3344eacfd6ff6430b9d30734450e8adcf5400f88d327dcb33") @@ -533,7 +533,7 @@ fn merge_jwt_secrets_flag() { }); } #[test] -fn merge_fee_recipient_flag() { +fn bellatrix_fee_recipient_flag() { let dir = TempDir::new().expect("Unable to create temporary directory"); CommandLineTest::new() .flag("execution-endpoint", Some("http://meow.cats")) @@ -1846,6 +1846,19 @@ fn block_cache_size_flag() { .with_config(|config| assert_eq!(config.store.block_cache_size, new_non_zero_usize(4))); } #[test] +fn state_cache_size_default() { + CommandLineTest::new() + .run_with_zero_port() + .with_config(|config| assert_eq!(config.store.state_cache_size, new_non_zero_usize(128))); +} +#[test] +fn state_cache_size_flag() { + CommandLineTest::new() + .flag("state-cache-size", Some("64")) + .run_with_zero_port() + .with_config(|config| assert_eq!(config.store.state_cache_size, new_non_zero_usize(64))); +} +#[test] fn historic_state_cache_size_flag() { CommandLineTest::new() .flag("historic-state-cache-size", Some("4")) diff --git a/testing/ef_tests/src/cases/common.rs b/testing/ef_tests/src/cases/common.rs index 342a48ba46c..6763edbe22b 100644 --- a/testing/ef_tests/src/cases/common.rs +++ b/testing/ef_tests/src/cases/common.rs @@ -63,8 +63,8 @@ pub fn previous_fork(fork_name: ForkName) -> ForkName { match fork_name { ForkName::Base => ForkName::Base, ForkName::Altair => ForkName::Base, - ForkName::Merge => ForkName::Altair, - ForkName::Capella => ForkName::Merge, + ForkName::Bellatrix => ForkName::Altair, + ForkName::Capella => ForkName::Bellatrix, ForkName::Deneb => ForkName::Capella, ForkName::Electra => ForkName::Deneb, } diff --git a/testing/ef_tests/src/cases/epoch_processing.rs b/testing/ef_tests/src/cases/epoch_processing.rs index a9c77c53c52..c4c592e4cf2 100644 --- a/testing/ef_tests/src/cases/epoch_processing.rs +++ b/testing/ef_tests/src/cases/epoch_processing.rs @@ -105,7 +105,7 @@ impl EpochTransition for JustificationAndFinalization { Ok(()) } BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => { @@ -128,7 +128,7 @@ impl EpochTransition for RewardsAndPenalties { base::process_rewards_and_penalties(state, &validator_statuses, spec) } BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => altair::process_rewards_and_penalties_slow(state, spec), @@ -161,7 +161,7 @@ impl EpochTransition for Slashings { )?; } BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => { @@ -203,7 +203,7 @@ impl EpochTransition for RandaoMixesReset { impl EpochTransition for HistoricalRootsUpdate { fn run(state: &mut BeaconState, _spec: &ChainSpec) -> Result<(), EpochProcessingError> { match state { - BeaconState::Base(_) | BeaconState::Altair(_) | BeaconState::Merge(_) => { + BeaconState::Base(_) | BeaconState::Altair(_) | BeaconState::Bellatrix(_) => { process_historical_roots_update(state) } _ => Ok(()), @@ -237,7 +237,7 @@ impl EpochTransition for SyncCommitteeUpdates { match state { BeaconState::Base(_) => Ok(()), BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => altair::process_sync_committee_updates(state, spec), @@ -250,7 +250,7 @@ impl EpochTransition for InactivityUpdates { match state { BeaconState::Base(_) => Ok(()), BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => altair::process_inactivity_updates_slow(state, spec), @@ -263,7 +263,7 @@ impl EpochTransition for ParticipationFlagUpdates { match state { BeaconState::Base(_) => Ok(()), BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => altair::process_participation_flag_updates(state), @@ -313,7 +313,7 @@ impl> Case for EpochProcessing { && T::name() != "historical_summaries_update" } // No phase0 tests for Altair and later. - ForkName::Altair | ForkName::Merge => { + ForkName::Altair | ForkName::Bellatrix => { T::name() != "participation_record_updates" && T::name() != "historical_summaries_update" } diff --git a/testing/ef_tests/src/cases/fork.rs b/testing/ef_tests/src/cases/fork.rs index be8a344a35a..132cfb4c0ae 100644 --- a/testing/ef_tests/src/cases/fork.rs +++ b/testing/ef_tests/src/cases/fork.rs @@ -63,7 +63,9 @@ impl Case for ForkTest { let mut result = match fork_name { ForkName::Base => panic!("phase0 not supported"), ForkName::Altair => upgrade_to_altair(&mut result_state, spec).map(|_| result_state), - ForkName::Merge => upgrade_to_bellatrix(&mut result_state, spec).map(|_| result_state), + ForkName::Bellatrix => { + upgrade_to_bellatrix(&mut result_state, spec).map(|_| result_state) + } ForkName::Capella => upgrade_to_capella(&mut result_state, spec).map(|_| result_state), ForkName::Deneb => upgrade_to_deneb(&mut result_state, spec).map(|_| result_state), ForkName::Electra => upgrade_to_electra(&mut result_state, spec).map(|_| result_state), diff --git a/testing/ef_tests/src/cases/merkle_proof_validity.rs b/testing/ef_tests/src/cases/merkle_proof_validity.rs index cf0b9f77c8f..8d5c0687753 100644 --- a/testing/ef_tests/src/cases/merkle_proof_validity.rs +++ b/testing/ef_tests/src/cases/merkle_proof_validity.rs @@ -92,7 +92,7 @@ pub struct KzgInclusionMerkleProofValidity { impl LoadCase for KzgInclusionMerkleProofValidity { fn load_from_dir(path: &Path, fork_name: ForkName) -> Result { let block: BeaconBlockBody> = match fork_name { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { return Err(Error::InternalError(format!( "KZG inclusion merkle proof validity test skipped for {:?}", fork_name diff --git a/testing/ef_tests/src/cases/operations.rs b/testing/ef_tests/src/cases/operations.rs index 15de13610fe..d76620bd22d 100644 --- a/testing/ef_tests/src/cases/operations.rs +++ b/testing/ef_tests/src/cases/operations.rs @@ -20,8 +20,8 @@ use state_processing::{ }; use std::fmt::Debug; use types::{ - Attestation, AttesterSlashing, BeaconBlock, BeaconBlockBody, BeaconBlockBodyCapella, - BeaconBlockBodyDeneb, BeaconBlockBodyMerge, BeaconState, BlindedPayload, Deposit, + Attestation, AttesterSlashing, BeaconBlock, BeaconBlockBody, BeaconBlockBodyBellatrix, + BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconState, BlindedPayload, Deposit, ExecutionPayload, FullPayload, ProposerSlashing, SignedBlsToExecutionChange, SignedVoluntaryExit, SyncAggregate, }; @@ -99,7 +99,7 @@ impl Operation for Attestation { spec, ), BeaconState::Altair(_) - | BeaconState::Merge(_) + | BeaconState::Bellatrix(_) | BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => { @@ -289,7 +289,7 @@ impl Operation for BeaconBlockBody> { fn decode(path: &Path, fork_name: ForkName, _spec: &ChainSpec) -> Result { ssz_decode_file_with(path, |bytes| { Ok(match fork_name { - ForkName::Merge => BeaconBlockBody::Merge(<_>::from_ssz_bytes(bytes)?), + ForkName::Bellatrix => BeaconBlockBody::Bellatrix(<_>::from_ssz_bytes(bytes)?), ForkName::Capella => BeaconBlockBody::Capella(<_>::from_ssz_bytes(bytes)?), ForkName::Deneb => BeaconBlockBody::Deneb(<_>::from_ssz_bytes(bytes)?), _ => panic!(), @@ -330,9 +330,10 @@ impl Operation for BeaconBlockBody> { fn decode(path: &Path, fork_name: ForkName, _spec: &ChainSpec) -> Result { ssz_decode_file_with(path, |bytes| { Ok(match fork_name { - ForkName::Merge => { - let inner = >>::from_ssz_bytes(bytes)?; - BeaconBlockBody::Merge(inner.clone_as_blinded()) + ForkName::Bellatrix => { + let inner = + >>::from_ssz_bytes(bytes)?; + BeaconBlockBody::Bellatrix(inner.clone_as_blinded()) } ForkName::Capella => { let inner = >>::from_ssz_bytes(bytes)?; @@ -375,7 +376,9 @@ impl Operation for WithdrawalsPayload { } fn is_enabled_for_fork(fork_name: ForkName) -> bool { - fork_name != ForkName::Base && fork_name != ForkName::Altair && fork_name != ForkName::Merge + fork_name != ForkName::Base + && fork_name != ForkName::Altair + && fork_name != ForkName::Bellatrix } fn decode(path: &Path, fork_name: ForkName, _spec: &ChainSpec) -> Result { @@ -407,7 +410,9 @@ impl Operation for SignedBlsToExecutionChange { } fn is_enabled_for_fork(fork_name: ForkName) -> bool { - fork_name != ForkName::Base && fork_name != ForkName::Altair && fork_name != ForkName::Merge + fork_name != ForkName::Base + && fork_name != ForkName::Altair + && fork_name != ForkName::Bellatrix } fn decode(path: &Path, _fork_name: ForkName, _spec: &ChainSpec) -> Result { diff --git a/testing/ef_tests/src/cases/transition.rs b/testing/ef_tests/src/cases/transition.rs index b2c49a96feb..dc5029d53e7 100644 --- a/testing/ef_tests/src/cases/transition.rs +++ b/testing/ef_tests/src/cases/transition.rs @@ -38,7 +38,7 @@ impl LoadCase for TransitionTest { ForkName::Altair => { spec.altair_fork_epoch = Some(metadata.fork_epoch); } - ForkName::Merge => { + ForkName::Bellatrix => { spec.altair_fork_epoch = Some(Epoch::new(0)); spec.bellatrix_fork_epoch = Some(metadata.fork_epoch); } diff --git a/testing/ef_tests/src/handler.rs b/testing/ef_tests/src/handler.rs index 59b5cb6ba74..2d5ea4149ef 100644 --- a/testing/ef_tests/src/handler.rs +++ b/testing/ef_tests/src/handler.rs @@ -217,8 +217,8 @@ impl SszStaticHandler { Self::for_forks(vec![ForkName::Altair]) } - pub fn merge_only() -> Self { - Self::for_forks(vec![ForkName::Merge]) + pub fn bellatrix_only() -> Self { + Self::for_forks(vec![ForkName::Bellatrix]) } pub fn capella_only() -> Self { @@ -558,7 +558,7 @@ impl Handler for ForkChoiceHandler { fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool { // Merge block tests are only enabled for Bellatrix. - if self.handler_name == "on_merge_block" && fork_name != ForkName::Merge { + if self.handler_name == "on_merge_block" && fork_name != ForkName::Bellatrix { return false; } @@ -823,7 +823,7 @@ impl Handler for KzgInclusionMerkleProofValidityHandler, MainnetEthSpec>::altair_only() .run(); - SszStaticHandler::, MinimalEthSpec>::merge_only() + SszStaticHandler::, MinimalEthSpec>::bellatrix_only() .run(); - SszStaticHandler::, MainnetEthSpec>::merge_only() + SszStaticHandler::, MainnetEthSpec>::bellatrix_only() .run(); SszStaticHandler::, MinimalEthSpec>::capella_only() .run(); @@ -290,10 +290,10 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::altair_only() .run(); - SszStaticHandler::, MinimalEthSpec>::merge_only( + SszStaticHandler::, MinimalEthSpec>::bellatrix_only( ) .run(); - SszStaticHandler::, MainnetEthSpec>::merge_only( + SszStaticHandler::, MainnetEthSpec>::bellatrix_only( ) .run(); SszStaticHandler::, MinimalEthSpec>::capella_only() @@ -313,9 +313,9 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::altair_only() .run(); - SszStaticHandler::, MinimalEthSpec>::merge_only() + SszStaticHandler::, MinimalEthSpec>::bellatrix_only() .run(); - SszStaticHandler::, MainnetEthSpec>::merge_only() + SszStaticHandler::, MainnetEthSpec>::bellatrix_only() .run(); SszStaticHandler::, MinimalEthSpec>::capella_only( @@ -340,10 +340,10 @@ mod ssz_static { SszStaticHandler::, MainnetEthSpec>::altair_only( ) .run(); - SszStaticHandler::, MinimalEthSpec>::merge_only( + SszStaticHandler::, MinimalEthSpec>::bellatrix_only( ) .run(); - SszStaticHandler::, MainnetEthSpec>::merge_only( + SszStaticHandler::, MainnetEthSpec>::bellatrix_only( ) .run(); SszStaticHandler::, MinimalEthSpec>::capella_only( @@ -369,10 +369,10 @@ mod ssz_static { SszStaticHandler::, MainnetEthSpec>::altair_only( ) .run(); - SszStaticHandler::, MinimalEthSpec>::merge_only( + SszStaticHandler::, MinimalEthSpec>::bellatrix_only( ) .run(); - SszStaticHandler::, MainnetEthSpec>::merge_only( + SszStaticHandler::, MainnetEthSpec>::bellatrix_only( ) .run(); SszStaticHandler::, MinimalEthSpec>::capella_only( @@ -396,9 +396,9 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::altair_only() .run(); - SszStaticHandler::, MinimalEthSpec>::merge_only() + SszStaticHandler::, MinimalEthSpec>::bellatrix_only() .run(); - SszStaticHandler::, MainnetEthSpec>::merge_only() + SszStaticHandler::, MainnetEthSpec>::bellatrix_only() .run(); SszStaticHandler::, MinimalEthSpec>::capella_only( ) @@ -448,12 +448,12 @@ mod ssz_static { SszStaticHandler::::altair_and_later().run(); } - // Merge and later + // Bellatrix and later #[test] fn execution_payload() { - SszStaticHandler::, MinimalEthSpec>::merge_only() + SszStaticHandler::, MinimalEthSpec>::bellatrix_only() .run(); - SszStaticHandler::, MainnetEthSpec>::merge_only() + SszStaticHandler::, MainnetEthSpec>::bellatrix_only() .run(); SszStaticHandler::, MinimalEthSpec>::capella_only() .run(); @@ -467,9 +467,9 @@ mod ssz_static { #[test] fn execution_payload_header() { - SszStaticHandler::, MinimalEthSpec>::merge_only() + SszStaticHandler::, MinimalEthSpec>::bellatrix_only() .run(); - SszStaticHandler::, MainnetEthSpec>::merge_only() + SszStaticHandler::, MainnetEthSpec>::bellatrix_only() .run(); SszStaticHandler::, MinimalEthSpec> ::capella_only().run(); diff --git a/testing/execution_engine_integration/src/test_rig.rs b/testing/execution_engine_integration/src/test_rig.rs index 0103f7074b5..c7d5e704524 100644 --- a/testing/execution_engine_integration/src/test_rig.rs +++ b/testing/execution_engine_integration/src/test_rig.rs @@ -180,7 +180,7 @@ impl TestRig { // Run the routine to check for online nodes. pair.execution_layer.watchdog_task().await; - if pair.execution_layer.is_synced().await { + if !pair.execution_layer.is_offline_or_erroring().await { break; } else if start_instant + EXECUTION_ENGINE_START_TIMEOUT > Instant::now() { sleep(Duration::from_millis(500)).await; diff --git a/testing/web3signer_tests/src/lib.rs b/testing/web3signer_tests/src/lib.rs index cf5d5f738d0..34493087c3b 100644 --- a/testing/web3signer_tests/src/lib.rs +++ b/testing/web3signer_tests/src/lib.rs @@ -724,11 +724,11 @@ mod tests { .await; } - /// Test all the Merge types. - async fn test_merge_types(network: &str, listen_port: u16) { + /// Test all the Bellatrix types. + async fn test_bellatrix_types(network: &str, listen_port: u16) { let network_config = Eth2NetworkConfig::constant(network).unwrap().unwrap(); let spec = &network_config.chain_spec::().unwrap(); - let merge_fork_slot = spec + let bellatrix_fork_slot = spec .bellatrix_fork_epoch .unwrap() .start_slot(E::slots_per_epoch()); @@ -740,14 +740,21 @@ mod tests { listen_port, ) .await - .assert_signatures_match("beacon_block_merge", |pubkey, validator_store| async move { - let mut merge_block = BeaconBlockMerge::empty(spec); - merge_block.slot = merge_fork_slot; - validator_store - .sign_block(pubkey, BeaconBlock::Merge(merge_block), merge_fork_slot) - .await - .unwrap() - }) + .assert_signatures_match( + "beacon_block_bellatrix", + |pubkey, validator_store| async move { + let mut bellatrix_block = BeaconBlockBellatrix::empty(spec); + bellatrix_block.slot = bellatrix_fork_slot; + validator_store + .sign_block( + pubkey, + BeaconBlock::Bellatrix(bellatrix_block), + bellatrix_fork_slot, + ) + .await + .unwrap() + }, + ) .await; } @@ -760,7 +767,7 @@ mod tests { let network_config = Eth2NetworkConfig::constant(network).unwrap().unwrap(); let spec = &network_config.chain_spec::().unwrap(); - let merge_fork_slot = spec + let bellatrix_fork_slot = spec .bellatrix_fork_epoch .unwrap() .start_slot(E::slots_per_epoch()); @@ -797,9 +804,9 @@ mod tests { }; let first_block = || { - let mut merge_block = BeaconBlockMerge::empty(spec); - merge_block.slot = merge_fork_slot; - BeaconBlock::Merge(merge_block) + let mut bellatrix_block = BeaconBlockBellatrix::empty(spec); + bellatrix_block.slot = bellatrix_fork_slot; + BeaconBlock::Bellatrix(bellatrix_block) }; let double_vote_block = || { @@ -914,8 +921,8 @@ mod tests { } #[tokio::test] - async fn sepolia_merge_types() { - test_merge_types("sepolia", 4252).await + async fn sepolia_bellatrix_types() { + test_bellatrix_types("sepolia", 4252).await } #[tokio::test] diff --git a/validator_client/Cargo.toml b/validator_client/Cargo.toml index d3dffc3d02e..0df687abec5 100644 --- a/validator_client/Cargo.toml +++ b/validator_client/Cargo.toml @@ -61,3 +61,4 @@ sysinfo = { workspace = true } system_health = { path = "../common/system_health" } logging = { workspace = true } strum = { workspace = true } +fdlimit = "0.3.0" diff --git a/validator_client/src/duties_service.rs b/validator_client/src/duties_service.rs index b5b56943c4b..6f25a1c05d7 100644 --- a/validator_client/src/duties_service.rs +++ b/validator_client/src/duties_service.rs @@ -88,14 +88,15 @@ const _: () = assert!({ /// bringing in the entire crate. const _: () = assert!(ATTESTATION_SUBSCRIPTION_OFFSETS[0] > 2); +// The info in the enum variants is displayed in logging, clippy thinks it's dead code. #[derive(Debug)] pub enum Error { UnableToReadSlotClock, - FailedToDownloadAttesters(String), - FailedToProduceSelectionProof(ValidatorStoreError), - InvalidModulo(ArithError), - Arith(ArithError), - SyncDutiesNotFound(u64), + FailedToDownloadAttesters(#[allow(dead_code)] String), + FailedToProduceSelectionProof(#[allow(dead_code)] ValidatorStoreError), + InvalidModulo(#[allow(dead_code)] ArithError), + Arith(#[allow(dead_code)] ArithError), + SyncDutiesNotFound(#[allow(dead_code)] u64), } impl From for Error { @@ -896,7 +897,7 @@ async fn poll_beacon_attesters_for_epoch( "Attester duties re-org"; "prior_dependent_root" => %prior_dependent_root, "dependent_root" => %dependent_root, - "msg" => "this may happen from time to time" + "note" => "this may happen from time to time" ) } *mut_value = (dependent_root, duty_and_proof); diff --git a/validator_client/src/http_metrics/mod.rs b/validator_client/src/http_metrics/mod.rs index de6c06437b4..67cab2bdc37 100644 --- a/validator_client/src/http_metrics/mod.rs +++ b/validator_client/src/http_metrics/mod.rs @@ -17,8 +17,8 @@ use warp::{http::Response, Filter}; #[derive(Debug)] pub enum Error { - Warp(warp::Error), - Other(String), + Warp(#[allow(dead_code)] warp::Error), + Other(#[allow(dead_code)] String), } impl From for Error { diff --git a/validator_client/src/lib.rs b/validator_client/src/lib.rs index 268c25cdf7d..377b0640483 100644 --- a/validator_client/src/lib.rs +++ b/validator_client/src/lib.rs @@ -47,7 +47,7 @@ use notifier::spawn_notifier; use parking_lot::RwLock; use preparation_service::{PreparationService, PreparationServiceBuilder}; use reqwest::Certificate; -use slog::{error, info, warn, Logger}; +use slog::{debug, error, info, warn, Logger}; use slot_clock::SlotClock; use slot_clock::SystemTimeSlotClock; use std::fs::File; @@ -121,6 +121,27 @@ impl ProductionValidatorClient { pub async fn new(context: RuntimeContext, config: Config) -> Result { let log = context.log().clone(); + // Attempt to raise soft fd limit. The behavior is OS specific: + // `linux` - raise soft fd limit to hard + // `macos` - raise soft fd limit to `min(kernel limit, hard fd limit)` + // `windows` & rest - noop + match fdlimit::raise_fd_limit().map_err(|e| format!("Unable to raise fd limit: {}", e))? { + fdlimit::Outcome::LimitRaised { from, to } => { + debug!( + log, + "Raised soft open file descriptor resource limit"; + "old_limit" => from, + "new_limit" => to + ); + } + fdlimit::Outcome::Unsupported => { + debug!( + log, + "Raising soft open file descriptor resource limit is not supported" + ); + } + }; + info!( log, "Starting validator client"; diff --git a/validator_client/src/signing_method/web3signer.rs b/validator_client/src/signing_method/web3signer.rs index 7973cab2c1e..8ad37a1620a 100644 --- a/validator_client/src/signing_method/web3signer.rs +++ b/validator_client/src/signing_method/web3signer.rs @@ -87,7 +87,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> Web3SignerObject<'a, E, Pa block: Some(block), block_header: None, }), - BeaconBlock::Merge(_) => Ok(Web3SignerObject::BeaconBlock { + BeaconBlock::Bellatrix(_) => Ok(Web3SignerObject::BeaconBlock { version: ForkName::Bellatrix, block: None, block_header: Some(block.block_header()), diff --git a/validator_client/src/validator_store.rs b/validator_client/src/validator_store.rs index 7334c35bfc7..8e69cb440a4 100644 --- a/validator_client/src/validator_store.rs +++ b/validator_client/src/validator_store.rs @@ -360,7 +360,7 @@ impl ValidatorStore { fn signing_context(&self, domain: Domain, signing_epoch: Epoch) -> SigningContext { if domain == Domain::VoluntaryExit { match self.spec.fork_name_at_epoch(signing_epoch) { - ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { + ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => { SigningContext { domain, epoch: signing_epoch, diff --git a/watch/src/database/mod.rs b/watch/src/database/mod.rs index 7d8c4c68cc2..b31583c6299 100644 --- a/watch/src/database/mod.rs +++ b/watch/src/database/mod.rs @@ -146,7 +146,7 @@ pub fn insert_beacon_block( let full_payload = block_message.execution_payload().ok(); let transaction_count: Option = if let Some(bellatrix_payload) = - full_payload.and_then(|payload| payload.execution_payload_merge().ok()) + full_payload.and_then(|payload| payload.execution_payload_bellatrix().ok()) { Some(bellatrix_payload.transactions.len() as i32) } else {