diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 34dcdf6482d4..bae511e8d963 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,3 +9,39 @@ /Dockerfile* @nethermindeth/core-devops @rubo /LICENSE* @rubo @LukaszRozmej @MarekM25 *.md @rubo @LukaszRozmej @MarekM25 + +/src/Nethermind/Ethereum.Test.Base/ @flcl42 +/src/Nethermind/Nethermind.Abi @LukaszRozmej +/src/Nethermind/Nethermind.Blockchain @LukaszRozmej @MarekM25 @flcl42 +/src/Nethermind/Nethermind.Consensus @LukaszRozmej @MarekM25 @flcl42 +/src/Nethermind/Nethermind.Crypto @LukaszRozmej @Marchhill +/src/Nethermind/Nethermind.Db.Rocks @LukaszRozmej @asdacap @damian-orzechowski +/src/Nethermind/Nethermind.Era1 @ak88 @Marchhill @smartprogrammer93 +/src/Nethermind/Nethermind.Evm.Precompiles @LukaszRozmej @benaadams @rubo @Marchhill +/src/Nethermind/Nethermind.Evm @LukaszRozmej @benaadams @Demuirgos +/src/Nethermind/Nethermind.ExternalSigner.Plugin @ak88 +/src/Nethermind/Nethermind.History @LukaszRozmej @Marchhill +/src/Nethermind/Nethermind.Hive @flcl42 @marcindsobczak +/src/Nethermind/Nethermind.Init @LukaszRozmej @asdacap +/src/Nethermind/Nethermind.JsonRpc.TraceStore @LukaszRozmej +/src/Nethermind/Nethermind.JsonRpc @LukaszRozmej @benaadams @smartprogrammer93 +/src/Nethermind/Nethermind.Merge.Plugin @LukaszRozmej @MarekM25 @flcl42 @benaadams @Marchhill +/src/Nethermind/Nethermind.Merkleization @ak88 @Marchhill @smartprogrammer93 @flcl42 +/src/Nethermind/Nethermind.Network.Discovery @LukaszRozmej @flcl42 @asdacap +/src/Nethermind/Nethermind.Network @LukaszRozmej @flcl42 @asdacap @marcindsobczak +/src/Nethermind/Nethermind.Optimism @emlautarom1 @deffrian +/src/Nethermind/Nethermind.Runner @LukaszRozmej @flcl42 +/src/Nethermind/Nethermind.Serialization.Json @LukaszRozmej @benaadams +/src/Nethermind/Nethermind.Serialization.Rlp @LukaszRozmej @Marchhill +/src/Nethermind/Nethermind.Serialization.Ssz @flcl42 @ak88 @Marchhill +/src/Nethermind/Nethermind.Serialization.SszGenerator @flcl42 @ak88 @Marchhill +/src/Nethermind/Nethermind.Shutter @Marchhill +/src/Nethermind/Nethermind.Sockets @LukaszRozmej @benaadams +/src/Nethermind/Nethermind.Specs @smartprogrammer93 +/src/Nethermind/Nethermind.State @LukaszRozmej @benaadams @asdacap @flcl42 @damian-orzechowski @tanishqjasoria +/src/Nethermind/Nethermind.Synchronization @LukaszRozmej @benaadams @asdacap @flcl42 @marcindsobczak +/src/Nethermind/Nethermind.Taiko @smartprogrammer93 @dipkakwani +/src/Nethermind/Nethermind.Trie @LukaszRozmej @benaadams @asdacap @flcl42 @damian-orzechowski @tanishqjasoria +/src/Nethermind/Nethermind.TxPool @LukaszRozmej @benaadams @flcl42 @marcindsobczak +/src/Nethermind/Nethermind.UI @benaadams +/src/Nethermind/Nethermind.Xdc @ak88 @Demuirgos @cicr99 diff --git a/.github/workflows/build-nethermind-packages.yml b/.github/workflows/build-nethermind-packages.yml index 4155421f3cc1..b2ed56d0e9bd 100644 --- a/.github/workflows/build-nethermind-packages.yml +++ b/.github/workflows/build-nethermind-packages.yml @@ -22,7 +22,7 @@ jobs: run: | sudo apt-get update && sudo apt-get install xmlstarlet -y --no-install-recommends version_prefix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionPrefix" Directory.Build.props) - version_suffix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionSuffix" Directory.Build.props) + version_suffix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionSuffix" -n Directory.Build.props) version=$([[ -n "$version_suffix" ]] && echo "$version_prefix-$version_suffix" || echo "$version_prefix") echo "Detected version $version" echo "PACKAGE_PREFIX=nethermind-$version-${GITHUB_SHA:0:8}" >> $GITHUB_ENV diff --git a/.github/workflows/nethermind-tests.yml b/.github/workflows/nethermind-tests.yml index 0a278fe8a11f..133b3f93b2f8 100644 --- a/.github/workflows/nethermind-tests.yml +++ b/.github/workflows/nethermind-tests.yml @@ -79,7 +79,6 @@ jobs: - Nethermind.Network.Enr.Test - Nethermind.Network.Test - Nethermind.Optimism.Test - # - Nethermind.Overseer.Test - Nethermind.Runner.Test - Nethermind.Serialization.Ssz.Test - Nethermind.Shutter.Test diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index a2c1838cf124..3e99e0361164 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -42,8 +42,8 @@ jobs: - name: Log in to Docker Hub uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_PASSWORD }} - name: Build and push image to Docker Hub (staging) run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 57c1ca44089f..7ca760bcc63d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,7 @@ jobs: run: | sudo apt-get update && sudo apt-get install xmlstarlet -y --no-install-recommends version_prefix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionPrefix" Directory.Build.props) - version_suffix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionSuffix" Directory.Build.props) + version_suffix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionSuffix" -n Directory.Build.props) version=$([[ -n "$version_suffix" ]] && echo "$version_prefix-$version_suffix" || echo "$version_prefix") package_prefix=nethermind-$version-${GITHUB_SHA:0:8} echo "Detected version $version" @@ -193,8 +193,8 @@ jobs: - name: Log in to Docker Hub uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_PASSWORD }} - name: Build and push image to Docker Hub run: | diff --git a/.github/workflows/run-a-single-node-from-branch.yml b/.github/workflows/run-a-single-node-from-branch.yml index 755afe1cb827..90ed995e3901 100644 --- a/.github/workflows/run-a-single-node-from-branch.yml +++ b/.github/workflows/run-a-single-node-from-branch.yml @@ -13,7 +13,6 @@ on: - gnosis - sepolia - chiado - - holesky - hoodi - op-mainnet - op-sepolia diff --git a/.github/workflows/sync-supported-chains.yml b/.github/workflows/sync-supported-chains.yml index 18cff36c31d9..724792f9daff 100644 --- a/.github/workflows/sync-supported-chains.yml +++ b/.github/workflows/sync-supported-chains.yml @@ -251,11 +251,6 @@ jobs: EXECUTION_URL="${{ secrets.MAINNET_EXECUTION_URL }}" stripped_network="mainnet" taiko_client_version="taiko-alethia-client-v0.43.2" - elif [[ "$network" == *hekla* ]]; then - CONSENSUS_URL="${{ secrets.HOLESKY_CONSENSUS_URL }}" - EXECUTION_URL="${{ secrets.HOLESKY_EXECUTION_URL }}" - stripped_network="holesky" - taiko_client_version="latest" else echo "Unknown network" exit 1 diff --git a/.github/workflows/update-dockerfiles.yml b/.github/workflows/update-dockerfiles.yml index 0348e9256d3e..ba11880f0fdc 100644 --- a/.github/workflows/update-dockerfiles.yml +++ b/.github/workflows/update-dockerfiles.yml @@ -54,10 +54,18 @@ jobs: file_count=$(git status --porcelain | wc -l) echo "file-count=$file_count" >> $GITHUB_OUTPUT + - name: Create GitHub app token + if: steps.update.outputs.file-count != '0' + id: gh-app + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Create a pull request if: steps.update.outputs.file-count != '0' env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ steps.gh-app.outputs.token }} run: | head_branch=chore/update-dockerfiles-$GITHUB_RUN_NUMBER-$GITHUB_RUN_ATTEMPT body=$(cat < + diff --git a/Dockerfile b/Dockerfile index 8b491797c68d..fad4df001759 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited # SPDX-License-Identifier: LGPL-3.0-only -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:953b8dd2d8e25c934579905b00d7077c5622632ff617f471a211ce9b72013205 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:d88e637d15248531967111fba05c6725ad45ea1dace3d35d8cfe2c4d4094e25d AS build ARG BUILD_CONFIG=release ARG CI=true @@ -25,7 +25,7 @@ RUN arch=$([ "$TARGETARCH" = "amd64" ] && echo "x64" || echo "$TARGETARCH") && \ # A temporary symlink to support the old executable name RUN ln -sr /publish/nethermind /publish/Nethermind.Runner -FROM mcr.microsoft.com/dotnet/aspnet:9.0.10-noble@sha256:d3c20e8e331018eb5e7402066fde168304b7c605ecde4dbadb40872dc8bf28db +FROM mcr.microsoft.com/dotnet/aspnet:9.0.10-noble@sha256:e183ff1306983bdb53592c3a76503c1c003461e2acd8fa7c62e04dff35819ee8 WORKDIR /nethermind diff --git a/Dockerfile.chiseled b/Dockerfile.chiseled index f0db4430f7a0..f5b272484348 100644 --- a/Dockerfile.chiseled +++ b/Dockerfile.chiseled @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited # SPDX-License-Identifier: LGPL-3.0-only -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:953b8dd2d8e25c934579905b00d7077c5622632ff617f471a211ce9b72013205 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:d88e637d15248531967111fba05c6725ad45ea1dace3d35d8cfe2c4d4094e25d AS build ARG BUILD_CONFIG=release ARG CI=true diff --git a/scripts/build/Dockerfile b/scripts/build/Dockerfile index d08a89349eca..49bbc7ab59b9 100644 --- a/scripts/build/Dockerfile +++ b/scripts/build/Dockerfile @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited # SPDX-License-Identifier: LGPL-3.0-only -FROM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:953b8dd2d8e25c934579905b00d7077c5622632ff617f471a211ce9b72013205 +FROM mcr.microsoft.com/dotnet/sdk:9.0.306-noble@sha256:d88e637d15248531967111fba05c6725ad45ea1dace3d35d8cfe2c4d4094e25d ARG COMMIT_HASH ARG SOURCE_DATE_EPOCH diff --git a/scripts/build/deb/Dockerfile b/scripts/build/deb/Dockerfile index dab95613c6e1..0c93559f4151 100644 --- a/scripts/build/deb/Dockerfile +++ b/scripts/build/deb/Dockerfile @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited # SPDX-License-Identifier: LGPL-3.0-only -FROM ubuntu:noble@sha256:985be7c735afdf6f18aaa122c23f87d989c30bba4e9aa24c8278912aac339a8d +FROM ubuntu:noble-20251001@sha256:d22e4fb389065efa4a61bb36416768698ef6d955fe8a7e0cdb3cd6de80fa7eec ARG ARTIFACTS ARG SOURCE_DATE_EPOCH @@ -9,19 +9,20 @@ ARG SOURCE_DATE_EPOCH WORKDIR /nethermind COPY src/Nethermind/Directory.Build.props . -COPY scripts/build/deb deb +COPY --chmod=0755 scripts/build/deb deb COPY $ARTIFACTS/linux-x64 deb/nethermind/opt/nethermind -RUN apt-get update && apt-get install xmlstarlet -y --no-install-recommends && \ +RUN apt-get update && apt-get install xmlstarlet=1.6.1-4 -y --no-install-recommends && \ rm -rf /var/lib/apt/lists/* -RUN version=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionPrefix" Directory.Build.props) && \ +RUN version_prefix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionPrefix" Directory.Build.props) && \ + version_suffix=$(xmlstarlet sel -t -v "//Project/PropertyGroup/VersionSuffix" -n Directory.Build.props) && \ + version=$([[ -n "$version_suffix" ]] && echo "$version_prefix-$version_suffix" || echo "$version_prefix") && \ sed -i "s/^Version: .*/Version: $version/" deb/nethermind/DEBIAN/control WORKDIR /nethermind/deb -RUN chmod 0755 nethermind/DEBIAN && \ - dpkg-deb --build --root-owner-group nethermind +RUN dpkg-deb --build --root-owner-group nethermind VOLUME /output diff --git a/scripts/config/testnet-matrix.json b/scripts/config/testnet-matrix.json index 412582e5d1a4..951afd681f57 100644 --- a/scripts/config/testnet-matrix.json +++ b/scripts/config/testnet-matrix.json @@ -7,14 +7,6 @@ "timeout": 180, "agent": "g6-standard-6" }, - { - "network": "holesky", - "cl": "lodestar", - "cl_image": "chainsafe/lodestar:latest", - "checkpoint-sync-url": "https://checkpoint-sync.holesky.ethpandaops.io/", - "timeout": 180, - "agent": "g6-standard-6" - }, { "network": "chiado", "cl": "lodestar", diff --git a/scripts/known-failing-hive-tests.txt b/scripts/known-failing-hive-tests.txt index ac0c1b77ca7e..2e002d854eb4 100644 --- a/scripts/known-failing-hive-tests.txt +++ b/scripts/known-failing-hive-tests.txt @@ -1,42 +1,17 @@ # rpc-compat -debug_getRawBlock/get-invalid-number (nethermind) -debug_getRawHeader/get-invalid-number (nethermind) -debug_getRawReceipts/get-invalid-number (nethermind) -debug_getRawTransaction/get-invalid-hash (nethermind) -debug_getRawTransaction/get-tx (nethermind) -eth_getBlockReceipts/get-block-receipts-by-hash (nethermind) -eth_getBlockReceipts/get-block-receipts-n (nethermind) -eth_getProof/get-account-proof-with-storage (nethermind) eth_getStorageAt/get-storage-invalid-key (nethermind) eth_getStorageAt/get-storage-invalid-key-too-large (nethermind) -eth_getTransactionReceipt/get-legacy-contract (nethermind) -eth_getTransactionReceipt/get-legacy-input (nethermind) -eth_getTransactionReceipt/get-legacy-receipt (nethermind) eth_sendRawTransaction/send-blob-tx (nethermind) eth_simulateV1/ethSimulate-empty-with-block-num-set-plus1 (nethermind) eth_simulateV1/ethSimulate-eth-send-should-not-produce-logs-on-revert (nethermind) eth_simulateV1/ethSimulate-eth-send-should-produce-no-logs-on-forward-revert (nethermind) eth_simulateV1/ethSimulate-fee-recipient-receiving-funds (nethermind) -eth_simulateV1/ethSimulate-gas-fees-and-value-error-38014 (nethermind) eth_simulateV1/ethSimulate-instrict-gas-38013 (nethermind) eth_simulateV1/ethSimulate-make-call-with-future-block (nethermind) -eth_simulateV1/ethSimulate-move-to-address-itself-reference-38022 (nethermind) -eth_simulateV1/ethSimulate-move-two-non-precompiles-accounts-to-same (nethermind) -eth_simulateV1/ethSimulate-overflow-nonce (nethermind) -eth_simulateV1/ethSimulate-overflow-nonce-validation (nethermind) -eth_simulateV1/ethSimulate-override-address-twice (nethermind) eth_simulateV1/ethSimulate-run-gas-spending (nethermind) eth_simulateV1/ethSimulate-run-out-of-gas-in-block-38015 (nethermind) -eth_simulateV1/ethSimulate-send-eth-and-delegate-call (nethermind) -eth_simulateV1/ethSimulate-send-eth-and-delegate-call-to-eoa (nethermind) -eth_simulateV1/ethSimulate-send-eth-and-delegate-call-to-payble-contract (nethermind) eth_simulateV1/ethSimulate-simple-more-params-validate (nethermind) -eth_simulateV1/ethSimulate-simple-no-funds (nethermind) -eth_simulateV1/ethSimulate-simple-no-funds-with-balance-querying (nethermind) -eth_simulateV1/ethSimulate-simple-send-from-contract-no-balance (nethermind) -eth_simulateV1/ethSimulate-transaction-too-high-nonce (nethermind) -eth_simulateV1/ethSimulate-try-to-move-non-precompile (nethermind) eth_simulateV1/ethSimulate-two-blocks-with-complete-eth-sends (nethermind) eth_simulateV1/ethSimulate-use-as-many-features-as-possible (nethermind) diff --git a/scripts/sync-settings.py b/scripts/sync-settings.py index c4320f588139..6a92eac817f9 100644 --- a/scripts/sync-settings.py +++ b/scripts/sync-settings.py @@ -13,7 +13,7 @@ configs = { # fast sync section "mainnet": { - "url": "api.etherscan.io", + "url": "https://api.etherscan.io/v2/api?chainid=1", "blockReduced": 1000, "multiplierRequirement": 1000, "isPoS": True @@ -31,7 +31,7 @@ "isPoS": True }, "sepolia": { - "url": "api-sepolia.etherscan.io", + "url": "https://api.etherscan.io/v2/api?chainid=11155111", "blockReduced": 1000, "multiplierRequirement": 1000, "isPoS": True @@ -105,7 +105,7 @@ def fastBlocksSettings(configuration, apiUrl, blockReduced, multiplierRequiremen 'action': 'eth_blockNumber', 'apikey': key, } - response = requests.get(f'https://{apiUrl}/api', params=params) + response = requests.get(apiUrl, params=params) else: data_req = '{"id":0,"jsonrpc":"2.0","method": "eth_blockNumber","params": []}' response = requests.post(apiUrl, headers=APPLICATION_JSON, data=data_req) @@ -122,7 +122,7 @@ def fastBlocksSettings(configuration, apiUrl, blockReduced, multiplierRequiremen 'boolean': 'true', 'apikey': key, } - response = requests.get(f'https://{apiUrl}/api', params=params) + response = requests.get(apiUrl, params=params) else: data_req = f'{{"id":0,"jsonrpc":"2.0","method": "eth_getBlockByNumber","params": ["{hex(baseBlock)}", false]}}' response = requests.post(apiUrl, headers=APPLICATION_JSON, data=data_req) diff --git a/src/Nethermind/Chains/base-mainnet.json.zst b/src/Nethermind/Chains/base-mainnet.json.zst index 3c4823ae55b5..6401d6a84d4d 100644 Binary files a/src/Nethermind/Chains/base-mainnet.json.zst and b/src/Nethermind/Chains/base-mainnet.json.zst differ diff --git a/src/Nethermind/Chains/base-sepolia.json.zst b/src/Nethermind/Chains/base-sepolia.json.zst index 4bde8d050d70..0ebb4d1fa34e 100644 Binary files a/src/Nethermind/Chains/base-sepolia.json.zst and b/src/Nethermind/Chains/base-sepolia.json.zst differ diff --git a/src/Nethermind/Chains/boba-mainnet.json.zst b/src/Nethermind/Chains/boba-mainnet.json.zst index da7c1824172f..71fd1372d268 100644 Binary files a/src/Nethermind/Chains/boba-mainnet.json.zst and b/src/Nethermind/Chains/boba-mainnet.json.zst differ diff --git a/src/Nethermind/Chains/boba-sepolia.json.zst b/src/Nethermind/Chains/boba-sepolia.json.zst index 4fae065e1580..fe40e08c362d 100644 Binary files a/src/Nethermind/Chains/boba-sepolia.json.zst and b/src/Nethermind/Chains/boba-sepolia.json.zst differ diff --git a/src/Nethermind/Chains/chiado.json b/src/Nethermind/Chains/chiado.json index 0ed220833d9a..60a243dbe2c5 100644 --- a/src/Nethermind/Chains/chiado.json +++ b/src/Nethermind/Chains/chiado.json @@ -91,14 +91,6 @@ "eip7623TransitionTimestamp": "0x67c96e4c", "eip7702TransitionTimestamp": "0x67c96e4c", "eip4844FeeCollectorTransitionTimestamp": "0x67c96e4c", - "eip7594TransitionTimestamp": "0x68f74e4c", - "eip7823TransitionTimestamp": "0x68f74e4c", - "eip7825TransitionTimestamp": "0x68f74e4c", - "eip7883TransitionTimestamp": "0x68f74e4c", - "eip7918TransitionTimestamp": "0x68f74e4c", - "eip7934TransitionTimestamp": "0x68f74e4c", - "eip7939TransitionTimestamp": "0x68f74e4c", - "eip7951TransitionTimestamp": "0x68f74e4c", "depositContractAddress": "0xb97036a26259b7147018913bd58a774cf91acf25", "blobSchedule": [ { diff --git a/src/Nethermind/Chains/dictionary b/src/Nethermind/Chains/dictionary index ad0ccadf414f..20e4d3d6b4c3 100644 Binary files a/src/Nethermind/Chains/dictionary and b/src/Nethermind/Chains/dictionary differ diff --git a/src/Nethermind/Chains/lyra-mainnet.json.zst b/src/Nethermind/Chains/lyra-mainnet.json.zst index 118816e9466f..3efc8fb54798 100644 Binary files a/src/Nethermind/Chains/lyra-mainnet.json.zst and b/src/Nethermind/Chains/lyra-mainnet.json.zst differ diff --git a/src/Nethermind/Chains/metal-sepolia.json.zst b/src/Nethermind/Chains/metal-sepolia.json.zst index a3675cb52591..e06f84b5277d 100644 Binary files a/src/Nethermind/Chains/metal-sepolia.json.zst and b/src/Nethermind/Chains/metal-sepolia.json.zst differ diff --git a/src/Nethermind/Chains/mode-mainnet.json.zst b/src/Nethermind/Chains/mode-mainnet.json.zst index df4f98be4c7d..ebbba73331d6 100644 Binary files a/src/Nethermind/Chains/mode-mainnet.json.zst and b/src/Nethermind/Chains/mode-mainnet.json.zst differ diff --git a/src/Nethermind/Chains/mode-sepolia.json.zst b/src/Nethermind/Chains/mode-sepolia.json.zst index a3dcaf0946d9..148ca283c3cc 100644 Binary files a/src/Nethermind/Chains/mode-sepolia.json.zst and b/src/Nethermind/Chains/mode-sepolia.json.zst differ diff --git a/src/Nethermind/Chains/op-mainnet.json.zst b/src/Nethermind/Chains/op-mainnet.json.zst index b3704e1520dd..82270fcad1ec 100644 Binary files a/src/Nethermind/Chains/op-mainnet.json.zst and b/src/Nethermind/Chains/op-mainnet.json.zst differ diff --git a/src/Nethermind/Chains/op-sepolia.json.zst b/src/Nethermind/Chains/op-sepolia.json.zst index e30576647c0b..505c7b220db6 100644 Binary files a/src/Nethermind/Chains/op-sepolia.json.zst and b/src/Nethermind/Chains/op-sepolia.json.zst differ diff --git a/src/Nethermind/Chains/orderly-mainnet.json.zst b/src/Nethermind/Chains/orderly-mainnet.json.zst index 2106c3170010..c9f4111f7f25 100644 Binary files a/src/Nethermind/Chains/orderly-mainnet.json.zst and b/src/Nethermind/Chains/orderly-mainnet.json.zst differ diff --git a/src/Nethermind/Chains/swell-mainnet.json.zst b/src/Nethermind/Chains/swell-mainnet.json.zst index ab19326a6977..0aecfd71be14 100644 Binary files a/src/Nethermind/Chains/swell-mainnet.json.zst and b/src/Nethermind/Chains/swell-mainnet.json.zst differ diff --git a/src/Nethermind/Chains/worldchain-sepolia.json.zst b/src/Nethermind/Chains/worldchain-sepolia.json.zst index 84ad7ec26290..cbf0f2d87b13 100644 Binary files a/src/Nethermind/Chains/worldchain-sepolia.json.zst and b/src/Nethermind/Chains/worldchain-sepolia.json.zst differ diff --git a/src/Nethermind/Chains/zora-mainnet.json.zst b/src/Nethermind/Chains/zora-mainnet.json.zst index 7d39faf4aa13..da176a17eaac 100644 Binary files a/src/Nethermind/Chains/zora-mainnet.json.zst and b/src/Nethermind/Chains/zora-mainnet.json.zst differ diff --git a/src/Nethermind/Chains/zora-sepolia.json.zst b/src/Nethermind/Chains/zora-sepolia.json.zst index 723ac1039556..f397523dd780 100644 Binary files a/src/Nethermind/Chains/zora-sepolia.json.zst and b/src/Nethermind/Chains/zora-sepolia.json.zst differ diff --git a/src/Nethermind/Directory.Build.props b/src/Nethermind/Directory.Build.props index 33bfcb030fdb..fa845ca36708 100644 --- a/src/Nethermind/Directory.Build.props +++ b/src/Nethermind/Directory.Build.props @@ -5,7 +5,7 @@ $(SOURCE_DATE_EPOCH) $([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()) - 1.35.0 + 1.36.0 unstable diff --git a/src/Nethermind/Ethereum.Blockchain.Legacy.Test/RevertTests.cs b/src/Nethermind/Ethereum.Blockchain.Legacy.Test/RevertTests.cs index 3b69a316454a..8d62cf44b283 100644 --- a/src/Nethermind/Ethereum.Blockchain.Legacy.Test/RevertTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Legacy.Test/RevertTests.cs @@ -27,7 +27,7 @@ public static IEnumerable LoadTests() "RevertPrecompiledTouch", }; - return tests.Where(t => !ignoredTests.Any(pattern => t.Name.Contains(pattern))); ; + return tests.Where(t => !ignoredTests.Any(pattern => t.Name.Contains(pattern))); } } } diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTest.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTest.cs index 19f62afb8007..6329281f660f 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTest.cs @@ -24,7 +24,6 @@ public class BlockchainTest : EthereumTest public Dictionary? Pre { get; set; } public Dictionary? PostState { get; set; } public Hash256? PostStateRoot { get; set; } - public bool SealEngineUsed { get; set; } public override string? ToString() { diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 13adce157a32..202c09ade825 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -30,7 +30,6 @@ using Nethermind.Specs.Forks; using Nethermind.Specs.Test; using Nethermind.Evm.State; -using Nethermind.Evm.Tracing; using Nethermind.Init.Modules; using Nethermind.TxPool; using NUnit.Framework; @@ -93,10 +92,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? ISpecProvider specProvider = new CustomSpecProvider(test.ChainId, test.ChainId, transitions.ToArray()); - if (test.ChainId != GnosisSpecProvider.Instance.ChainId && specProvider.GenesisSpec != Frontier.Instance) - { - Assert.Fail("Expected genesis spec to be Frontier for blockchain tests"); - } + Assert.That(test.ChainId == GnosisSpecProvider.Instance.ChainId || specProvider.GenesisSpec == Frontier.Instance, "Expected genesis spec to be Frontier for blockchain tests"); if (test.Network is Cancun || test.NetworkAfterTransition is Cancun) { @@ -191,42 +187,40 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? // For tests with reorgs, find the actual parent header from block tree parentHeader = blockTree.FindHeader(correctRlp[i].Block.ParentHash) ?? parentHeader; - if (correctRlp[i].Block.Hash is null) - { - Assert.Fail($"null hash in {test.Name} block {i}"); - } + Assert.That(correctRlp[i].Block.Hash is not null, $"null hash in {test.Name} block {i}"); - try + bool expectsException = correctRlp[i].ExpectedException is not null; + // Validate block structure first (mimics SyncServer validation) + if (blockValidator.ValidateSuggestedBlock(correctRlp[i].Block, parentHeader, out string? validationError)) { - // Validate block structure first (mimics SyncServer validation) - if (blockValidator.ValidateSuggestedBlock(correctRlp[i].Block, parentHeader, out var validationError)) + Assert.That(!expectsException, $"Expected block {correctRlp[i].Block.Hash} to fail with '{correctRlp[i].ExpectedException}', but it passed validation"); + try { // All validations passed, suggest the block blockTree.SuggestBlock(correctRlp[i].Block); + } - else + catch (InvalidBlockException e) { - if (correctRlp[i].ExpectedException is not null) - { - Assert.Fail($"Unexpected invalid block {correctRlp[i].Block.Hash}: {validationError}"); - } + // Exception thrown during block processing + Assert.That(expectsException, $"Unexpected invalid block {correctRlp[i].Block.Hash}: {validationError}, Exception: {e}"); + // else: Expected to fail and did fail via exception → this is correct behavior } - } - catch (InvalidBlockException e) - { - if (correctRlp[i].ExpectedException is not null) + catch (Exception e) { - Assert.Fail($"Unexpected invalid block {correctRlp[i].Block.Hash}: {e}"); + Assert.Fail($"Unexpected exception during processing: {e}"); + } + finally + { + // Dispose AccountChanges to prevent memory leaks in tests + correctRlp[i].Block.DisposeAccountChanges(); } } - catch (Exception e) - { - Assert.Fail($"Unexpected exception during processing: {e}"); - } - finally + else { - // Dispose AccountChanges to prevent memory leaks in tests - correctRlp[i].Block.DisposeAccountChanges(); + // Validation FAILED + Assert.That(expectsException, $"Unexpected invalid block {correctRlp[i].Block.Hash}: {validationError}"); + // else: Expected to fail and did fail → this is correct behavior } parentHeader = correctRlp[i].Block.Header; @@ -288,9 +282,6 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? { RlpStream rlpContext = Bytes.FromHexString(testBlockJson.Rlp!).AsRlpStream(); Block suggestedBlock = Rlp.Decode(rlpContext); - suggestedBlock.Header.SealEngineType = - test.SealEngineUsed ? SealEngineType.Ethash : SealEngineType.None; - if (testBlockJson.BlockHeader is not null) { Assert.That(suggestedBlock.Header.Hash, Is.EqualTo(new Hash256(testBlockJson.BlockHeader.Hash))); @@ -308,16 +299,10 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? if (testBlockJson.ExpectedException is null) { string invalidRlpMessage = $"Invalid RLP ({i}) {e}"; - if (failOnInvalidRlp) - { - Assert.Fail(invalidRlpMessage); - } - else - { - // ForgedTests don't have ExpectedException and at the same time have invalid rlps - // Don't fail here. If test executed incorrectly will fail at last check - _logger.Warn(invalidRlpMessage); - } + Assert.That(!failOnInvalidRlp, invalidRlpMessage); + // ForgedTests don't have ExpectedException and at the same time have invalid rlps + // Don't fail here. If test executed incorrectly will fail at last check + _logger.Warn(invalidRlpMessage); } else { diff --git a/src/Nethermind/Nethermind.Api/IInitConfig.cs b/src/Nethermind/Nethermind.Api/IInitConfig.cs index 1b87000de148..9716f609b914 100644 --- a/src/Nethermind/Nethermind.Api/IInitConfig.cs +++ b/src/Nethermind/Nethermind.Api/IInitConfig.cs @@ -39,10 +39,10 @@ public interface IInitConfig : IConfig [ConfigItem(Description = "The hash of the genesis block. If not specified, the genesis block validity is not checked which is useful in the case of ad hoc test/private networks.", DefaultValue = "null")] string? GenesisHash { get; set; } - [ConfigItem(Description = "The path to the static nodes file.", DefaultValue = "Data/static-nodes.json")] + [ConfigItem(Description = "The path to the static nodes file.", DefaultValue = "static-nodes.json")] string StaticNodesPath { get; set; } - [ConfigItem(Description = "The path to the trusted nodes file.", DefaultValue = "Data/trusted-nodes.json")] + [ConfigItem(Description = "The path to the trusted nodes file.", DefaultValue = "trusted-nodes.json")] string TrustedNodesPath { get; set; } [ConfigItem(Description = "The name of the log file.", DefaultValue = "log.txt")] @@ -69,6 +69,9 @@ public interface IInitConfig : IConfig [ConfigItem(Description = "The maximum number of bad blocks observed on the network that will be stored on disk.", DefaultValue = "100")] long? BadBlocksStored { get; set; } + [ConfigItem(Description = "The path to the Nethermind data directory. Defaults to Nethermind's current directory.", DefaultValue = "null", HiddenFromDocs = true)] + string? DataDir { get; set; } + [ConfigItem(Description = "[TECHNICAL] Disable garbage collector on newPayload", DefaultValue = "true", HiddenFromDocs = true)] bool DisableGcOnNewPayload { get; set; } diff --git a/src/Nethermind/Nethermind.Api/InitConfig.cs b/src/Nethermind/Nethermind.Api/InitConfig.cs index d766d41342b8..db0dbfdd706f 100644 --- a/src/Nethermind/Nethermind.Api/InitConfig.cs +++ b/src/Nethermind/Nethermind.Api/InitConfig.cs @@ -18,8 +18,8 @@ public class InitConfig : IInitConfig public string BaseDbPath { get; set; } = "db"; public string LogFileName { get; set; } = "log.txt"; public string? GenesisHash { get; set; } - public string StaticNodesPath { get; set; } = "Data/static-nodes.json"; - public string TrustedNodesPath { get; set; } = "Data/trusted-nodes.json"; + public string StaticNodesPath { get; set; } = "static-nodes.json"; + public string TrustedNodesPath { get; set; } = "trusted-nodes.json"; public string? KzgSetupPath { get; set; } = null; public string LogDirectory { get; set; } = "logs"; public string? LogRules { get; set; } = null; @@ -28,7 +28,7 @@ public class InitConfig : IInitConfig public DiagnosticMode DiagnosticMode { get; set; } = DiagnosticMode.None; public DumpOptions AutoDump { get; set; } = DumpOptions.Default; - public string RpcDbUrl { get; set; } = String.Empty; + public string RpcDbUrl { get; set; } = string.Empty; public long? MemoryHint { get; set; } public long? BadBlocksStored { get; set; } = 100; public bool DisableGcOnNewPayload { get; set; } = true; @@ -39,6 +39,7 @@ public class InitConfig : IInitConfig public int BackgroundTaskConcurrency { get; set; } = 2; public int BackgroundTaskMaxNumber { get; set; } = 1024; public bool InRunnerTest { get; set; } = false; + public string? DataDir { get; set; } [Obsolete("Use DiagnosticMode with MemDb instead")] public bool UseMemDb diff --git a/src/Nethermind/Nethermind.AuRa.Test/AuraBlockProcessorTests.cs b/src/Nethermind/Nethermind.AuRa.Test/AuraBlockProcessorTests.cs index 0b6513134d49..c9c17ad3c2d3 100644 --- a/src/Nethermind/Nethermind.AuRa.Test/AuraBlockProcessorTests.cs +++ b/src/Nethermind/Nethermind.AuRa.Test/AuraBlockProcessorTests.cs @@ -173,7 +173,7 @@ static BlockHeader Process(BranchProcessor auRaBlockProcessor, BlockHeader paren IWorldState stateProvider = TestWorldStateFactory.CreateForTest(); ITransactionProcessor transactionProcessor = Substitute.For(); AuRaBlockProcessor processor = new AuRaBlockProcessor( - HoleskySpecProvider.Instance, + HoodiSpecProvider.Instance, TestBlockValidator.AlwaysValid, NoBlockRewards.Instance, new BlockProcessor.BlockValidationTransactionsExecutor(new ExecuteTransactionProcessorAdapter(transactionProcessor), stateProvider), @@ -190,7 +190,7 @@ static BlockHeader Process(BranchProcessor auRaBlockProcessor, BlockHeader paren BranchProcessor branchProcessor = new BranchProcessor( processor, - HoleskySpecProvider.Instance, + HoodiSpecProvider.Instance, stateProvider, new BeaconBlockRootHandler(transactionProcessor, stateProvider), LimboLogs.Instance); diff --git a/src/Nethermind/Nethermind.AuRa.Test/Contract/ContractDataStoreWithLocalDataTests.cs b/src/Nethermind/Nethermind.AuRa.Test/Contract/ContractDataStoreWithLocalDataTests.cs index 9624eea6e505..d9ec5662e3b7 100644 --- a/src/Nethermind/Nethermind.AuRa.Test/Contract/ContractDataStoreWithLocalDataTests.cs +++ b/src/Nethermind/Nethermind.AuRa.Test/Contract/ContractDataStoreWithLocalDataTests.cs @@ -79,18 +79,24 @@ public void combines_contract_and_local_data_correctly() testCase.ContractDataStore.GetItemsFromContractAtBlock(blockHeader).Should().BeEquivalentTo(expected.Cast()); Block secondBlock = Build.A.Block.WithHeader(Build.A.BlockHeader.WithNumber(3).WithHash(TestItem.KeccakB).WithParentHash(TestItem.KeccakC).TestObject).TestObject; - expected = new[] { TestItem.AddressC, TestItem.AddressB }; + expected = [TestItem.AddressC, TestItem.AddressB]; testCase.DataContract.GetAllItemsFromBlock(secondBlock.Header).Returns(new[] { TestItem.AddressB }); testCase.ContractDataStore.GetItemsFromContractAtBlock(blockHeader); testCase.BlockTree.NewHeadBlock += Raise.EventWith(new BlockEventArgs(secondBlock)); - testCase.ContractDataStore.GetItemsFromContractAtBlock(secondBlock.Header).Should().BeEquivalentTo(expected.Cast()); + Assert.That( + () => testCase.ContractDataStore.GetItemsFromContractAtBlock(secondBlock.Header), + Is.EquivalentTo(expected.Cast()).After(1000, 100) + ); localDataSource.Data.Returns(new[] { TestItem.AddressC, TestItem.AddressD }); - expected = new[] { TestItem.AddressC, TestItem.AddressD, TestItem.AddressB }; + expected = [TestItem.AddressC, TestItem.AddressD, TestItem.AddressB]; localDataSource.Changed += Raise.Event(); - testCase.ContractDataStore.GetItemsFromContractAtBlock(secondBlock.Header).Should().BeEquivalentTo(expected.Cast()); + Assert.That( + () => testCase.ContractDataStore.GetItemsFromContractAtBlock(secondBlock.Header), + Is.EquivalentTo(expected.Cast()).After(1000, 100) + ); } protected override TestCase BuildTestCase(IComparer keyComparer = null, IComparer valueComparer = null) => diff --git a/src/Nethermind/Nethermind.Benchmark/Store/PatriciaTreeBenchmarks.cs b/src/Nethermind/Nethermind.Benchmark/Store/PatriciaTreeBenchmarks.cs index 9061fff098b4..0155d6a36c03 100644 --- a/src/Nethermind/Nethermind.Benchmark/Store/PatriciaTreeBenchmarks.cs +++ b/src/Nethermind/Nethermind.Benchmark/Store/PatriciaTreeBenchmarks.cs @@ -433,7 +433,7 @@ public void LargeBulkSetPreSorted() StateTree tempTree = new StateTree(trieStore, NullLogManager.Instance); tempTree.RootHash = Keccak.EmptyTreeHash; - using ArrayPoolList bulkSet = new ArrayPoolList(_largerEntryCount); + using ArrayPoolListRef bulkSet = new(_largerEntryCount); for (int i = 0; i < _largerEntryCount; i++) { @@ -524,7 +524,7 @@ private void DoBulkSetRepeatedly(int repeatBatchSize) var originalRootHash = Keccak.EmptyTreeHash; tempTree.RootHash = Keccak.EmptyTreeHash; - using ArrayPoolList bulkSet = new ArrayPoolList(_repeatedlyFactor); + using ArrayPoolListRef bulkSet = new(_repeatedlyFactor); for (int i = 0; i < _largerEntryCount; i++) { if (i % repeatBatchSize == 0) @@ -551,7 +551,7 @@ private void DoBulkSetRepeatedlyNoParallel(int repeatBatchSize) var originalRootHash = Keccak.EmptyTreeHash; tempTree.RootHash = Keccak.EmptyTreeHash; - using ArrayPoolList bulkSet = new ArrayPoolList(_repeatedlyFactor); + using ArrayPoolListRef bulkSet = new(_repeatedlyFactor); for (int i = 0; i < _largerEntryCount; i++) { if (i % repeatBatchSize == 0) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockProcessorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockProcessorTests.cs index add9079bfe89..91dfd349a46a 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockProcessorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockProcessorTests.cs @@ -45,7 +45,7 @@ public void Prepared_block_contains_author_field() { IWorldState stateProvider = TestWorldStateFactory.CreateForTest(); ITransactionProcessor transactionProcessor = Substitute.For(); - BlockProcessor processor = new BlockProcessor(HoleskySpecProvider.Instance, + BlockProcessor processor = new BlockProcessor(HoodiSpecProvider.Instance, TestBlockValidator.AlwaysValid, NoBlockRewards.Instance, new BlockProcessor.BlockValidationTransactionsExecutor(new ExecuteTransactionProcessorAdapter(transactionProcessor), stateProvider), @@ -58,7 +58,7 @@ public void Prepared_block_contains_author_field() new ExecutionRequestsProcessor(transactionProcessor)); BranchProcessor branchProcessor = new BranchProcessor( processor, - HoleskySpecProvider.Instance, + HoodiSpecProvider.Instance, stateProvider, new BeaconBlockRootHandler(transactionProcessor, stateProvider), LimboLogs.Instance); @@ -80,7 +80,7 @@ public void Recovers_state_on_cancel() IWorldState stateProvider = TestWorldStateFactory.CreateForTest(); ITransactionProcessor transactionProcessor = Substitute.For(); BlockProcessor processor = new BlockProcessor( - HoleskySpecProvider.Instance, + HoodiSpecProvider.Instance, TestBlockValidator.AlwaysValid, new RewardCalculator(MainnetSpecProvider.Instance), new BlockProcessor.BlockValidationTransactionsExecutor(new ExecuteTransactionProcessorAdapter(transactionProcessor), stateProvider), @@ -93,7 +93,7 @@ public void Recovers_state_on_cancel() new ExecutionRequestsProcessor(transactionProcessor)); BranchProcessor branchProcessor = new BranchProcessor( processor, - HoleskySpecProvider.Instance, + HoodiSpecProvider.Instance, stateProvider, new BeaconBlockRootHandler(transactionProcessor, stateProvider), LimboLogs.Instance); diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockTreeTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockTreeTests.cs index 2d32e4b03952..8042d3144068 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockTreeTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockTreeTests.cs @@ -1881,7 +1881,7 @@ public void On_restart_loads_already_processed_genesis_block(bool wereProcessed) // First run { Hash256 uncleHash = new("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"); - BlockTree tree = Build.A.BlockTree(HoleskySpecProvider.Instance) + BlockTree tree = Build.A.BlockTree(HoodiSpecProvider.Instance) .WithBlockStore(new BlockStore(blocksDb)) .WithBlocksNumberDb(blockNumberDb) .WithHeadersDb(headersDb) @@ -1947,7 +1947,7 @@ public void On_restart_loads_already_processed_genesis_block(bool wereProcessed) // Assume Nethermind got restarted { - BlockTree tree = Build.A.BlockTree(HoleskySpecProvider.Instance) + BlockTree tree = Build.A.BlockTree(HoodiSpecProvider.Instance) .WithBlockStore(new BlockStore(blocksDb)) .WithBlocksNumberDb(blockNumberDb) .WithHeadersDb(headersDb) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Blocks/HeaderStoreTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Blocks/HeaderStoreTests.cs index bc1d967eb9ec..c918b6a3a845 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Blocks/HeaderStoreTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Blocks/HeaderStoreTests.cs @@ -17,7 +17,7 @@ public class HeaderStoreTests [Test] public void TestCanStoreAndGetHeader() { - HeaderStore store = new(new MemDb(), new MemDb()); + IHeaderStore store = new HeaderStore(new MemDb(), new MemDb()); BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject; BlockHeader header2 = Build.A.BlockHeader.WithNumber(102).TestObject; @@ -39,7 +39,7 @@ public void TestCanStoreAndGetHeader() public void TestCanReadHeaderStoredWithHash() { IDb headerDb = new MemDb(); - HeaderStore store = new(headerDb, new MemDb()); + IHeaderStore store = new HeaderStore(headerDb, new MemDb()); BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject; headerDb.Set(header.Hash!, new HeaderDecoder().Encode(header).Bytes); @@ -50,7 +50,7 @@ public void TestCanReadHeaderStoredWithHash() [Test] public void TestCanReadCacheHeader() { - HeaderStore store = new(new MemDb(), new MemDb()); + IHeaderStore store = new HeaderStore(new MemDb(), new MemDb()); BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject; store.Cache(header); @@ -60,7 +60,7 @@ public void TestCanReadCacheHeader() [Test] public void TestCanDeleteHeader() { - HeaderStore store = new(new MemDb(), new MemDb()); + IHeaderStore store = new HeaderStore(new MemDb(), new MemDb()); BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject; store.Insert(header); store.Delete(header.Hash!); @@ -72,7 +72,7 @@ public void TestCanDeleteHeader() public void TestCanDeleteHeaderStoredWithHash() { IDb headerDb = new MemDb(); - HeaderStore store = new(headerDb, new MemDb()); + IHeaderStore store = new HeaderStore(headerDb, new MemDb()); BlockHeader header = Build.A.BlockHeader.WithNumber(100).TestObject; headerDb.Set(header.Hash!, new HeaderDecoder().Encode(header).Bytes); diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Producers/BlockProducerBaseTests.IsProducingBlocks.cs b/src/Nethermind/Nethermind.Blockchain.Test/Producers/BlockProducerBaseTests.IsProducingBlocks.cs index 3b9582889cbb..1470ec994713 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Producers/BlockProducerBaseTests.IsProducingBlocks.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Producers/BlockProducerBaseTests.IsProducingBlocks.cs @@ -145,6 +145,33 @@ public async Task CliqueBlockProducer_IsProducingBlocks_returns_expected_results await AssertIsProducingBlocks(runner); } + [Test, MaxTime(Timeout.MaxTestTime)] + public async Task DevBlockProducer_OnGlobalWorldState_IsProducingAndSuggestingBlocks() + { + TestRpcBlockchain testRpc = await CreateTestRpc(); + DevBlockProducer blockProducer = new( + Substitute.For(), + testRpc.BlockchainProcessor, + testRpc.WorldStateManager.GlobalWorldState, + testRpc.BlockTree, + testRpc.Timestamper, + testRpc.SpecProvider, + new BlocksConfig(), + LimboLogs.Instance); + + BuildBlocksWhenRequested trigger = new(); + StandardBlockProducerRunner runner = new(trigger, testRpc.BlockTree, blockProducer); + long currentHead = testRpc.BlockTree.Head?.Number ?? 0; + + _ = new NonProcessingProducedBlockSuggester(testRpc.BlockTree, runner); + + runner.Start(); + + await trigger.BuildBlock(testRpc.BlockTree.Head?.Header); + + Assert.That(testRpc.BlockTree.BestSuggestedHeader?.Number, Is.EqualTo(currentHead + 1)); + } + private async Task CreateTestRpc() { Address address = TestItem.Addresses[0]; diff --git a/src/Nethermind/Nethermind.Blockchain.Test/TxPoolSourceTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/TxPoolSourceTests.cs index 5a5f7a7c2f72..1e409cfae0c9 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/TxPoolSourceTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/TxPoolSourceTests.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Consensus.Transactions; @@ -36,7 +36,7 @@ public void GetTransactions_should_respect_customizable_blob_gas_limit(int[] blo txPool.GetPendingLightBlobTransactionsBySender().Returns(transactionsWithBlobs); ITxFilterPipeline txFilterPipeline = Substitute.For(); - txFilterPipeline.Execute(Arg.Any(), Arg.Any(), Arg.Any()).Returns(AcceptTxResult.Accepted); + txFilterPipeline.Execute(Arg.Any(), Arg.Any(), Arg.Any()).Returns(true); TxPoolTxSource transactionSelector = new(txPool, specProvider, transactionComparerProvider, LimboLogs.Instance, txFilterPipeline, new BlocksConfig { SecondsPerSlot = 12 }); diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs index 771354b9161c..dff67022674e 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs @@ -202,6 +202,44 @@ public void ValidateProcessedBlock_StateRootIsWrong_ErrorIsSet() Assert.That(error, Does.StartWith("InvalidStateRoot")); } + [Test] + public void ValidateProcessedBlock_ReceiptCountMismatch_DoesNotThrow() + { + TxValidator txValidator = new(TestBlockchainIds.ChainId); + ISpecProvider specProvider = Substitute.For(); + BlockValidator sut = new(txValidator, Always.Valid, Always.Valid, specProvider, LimboLogs.Instance); + Block suggestedBlock = Build.A.Block.TestObject; + Block processedBlock = Build.A.Block + .WithStateRoot(Keccak.Zero) + .WithTransactions(2, specProvider) + .TestObject; + + Assert.DoesNotThrow(() => sut.ValidateProcessedBlock( + processedBlock, + [], + suggestedBlock)); + } + + [Test] + public void ValidateProcessedBlock_ReceiptCountMismatch_ReturnsFalse() + { + TxValidator txValidator = new(TestBlockchainIds.ChainId); + ISpecProvider specProvider = Substitute.For(); + BlockValidator sut = new(txValidator, Always.Valid, Always.Valid, specProvider, LimboLogs.Instance); + Block suggestedBlock = Build.A.Block.TestObject; + Block processedBlock = Build.A.Block + .WithStateRoot(Keccak.Zero) + .WithTransactions(3, specProvider) + .TestObject; + + bool result = sut.ValidateProcessedBlock( + processedBlock, + [Build.A.Receipt.TestObject], + suggestedBlock); + + Assert.That(result, Is.False); + } + private static IEnumerable BadSuggestedBlocks() { BlockHeader parent = Build.A.BlockHeader.TestObject; diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs index 0eaea4fdee05..445672cdf87a 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Linq; using System.Numerics; using Nethermind.Consensus; using Nethermind.Consensus.Ethash; @@ -58,7 +59,6 @@ public void Setup() [Test, MaxTime(Timeout.MaxTestTime)] public void Valid_when_valid() { - _block.Header.SealEngineType = SealEngineType.None; bool result = _validator.Validate(_block.Header, _parentBlock.Header); if (!result) { @@ -75,7 +75,6 @@ public void Valid_when_valid() public void When_gas_limit_too_high() { _block.Header.GasLimit = _parentBlock.Header.GasLimit + (long)BigInteger.Divide(_parentBlock.Header.GasLimit, 1024); - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -86,7 +85,6 @@ public void When_gas_limit_too_high() public void When_gas_limit_just_correct_high() { _block.Header.GasLimit = _parentBlock.Header.GasLimit + (long)BigInteger.Divide(_parentBlock.Header.GasLimit, 1024) - 1; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -97,7 +95,6 @@ public void When_gas_limit_just_correct_high() public void When_gas_limit_just_correct_low() { _block.Header.GasLimit = _parentBlock.Header.GasLimit - (long)BigInteger.Divide(_parentBlock.Header.GasLimit, 1024) + 1; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -108,7 +105,6 @@ public void When_gas_limit_just_correct_low() public void When_gas_limit_is_just_too_low() { _block.Header.GasLimit = _parentBlock.Header.GasLimit - (long)BigInteger.Divide(_parentBlock.Header.GasLimit, 1024); - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -119,7 +115,6 @@ public void When_gas_limit_is_just_too_low() public void When_gas_used_above_gas_limit() { _block.Header.GasUsed = _parentBlock.Header.GasLimit + 1; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -130,7 +125,6 @@ public void When_gas_used_above_gas_limit() public void When_no_parent_invalid() { _block.Header.ParentHash = Keccak.Zero; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -141,7 +135,6 @@ public void When_no_parent_invalid() public void When_timestamp_same_as_parent() { _block.Header.Timestamp = _parentBlock.Header.Timestamp; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -152,7 +145,6 @@ public void When_timestamp_same_as_parent() public void When_extra_data_too_long() { _block.Header.ExtraData = new byte[33]; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -163,7 +155,6 @@ public void When_extra_data_too_long() public void When_incorrect_difficulty_then_invalid() { _block.Header.Difficulty = 1; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -174,7 +165,6 @@ public void When_incorrect_difficulty_then_invalid() public void When_incorrect_number_then_invalid() { _block.Header.Number += 1; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -215,7 +205,6 @@ public void When_gaslimit_is_on_london_fork(long parentGasLimit, long blockNumbe .WithNumber(_parentBlock.Number + 1) .WithBaseFeePerGas(BaseFeeCalculator.Calculate(_parentBlock.Header, specProvider.GetSpec((ForkActivation)(_parentBlock.Number + 1)))) .WithNonce(0).TestObject; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -236,7 +225,6 @@ public void When_gas_limit_is_long_max_value() .WithGasLimit(long.MaxValue) .WithNumber(_parentBlock.Number + 1) .WithNonce(0).TestObject; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); bool result = _validator.Validate(_block.Header, _parentBlock.Header); @@ -269,7 +257,6 @@ public void When_total_difficulty_null_we_should_skip_total_difficulty_validatio { _block.Header.Difficulty = 1; _block.Header.TotalDifficulty = null; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); HeaderValidator validator = new HeaderValidator(_blockTree, Always.Valid, _specProvider, new OneLoggerLogManager(new(_testLogger))); @@ -289,7 +276,6 @@ public void When_total_difficulty_zero_we_should_skip_total_difficulty_validatio { _block.Header.Difficulty = 1; _block.Header.TotalDifficulty = 0; - _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); { @@ -340,4 +326,117 @@ public void When_given_parent_is_wrong() Assert.That(result, Is.False); Assert.That(error, Does.StartWith("Mismatched parent")); } + + [Test] + public void BaseFee_validation_must_not_be_bypassed_for_NoProof_seal_engine() + { + // Arrange: Create London fork with EIP-1559 + OverridableReleaseSpec spec = new(London.Instance) + { + Eip1559TransitionBlock = 0 + }; + TestSpecProvider specProvider = new(spec); + + // Create validator with no-op seal validator (simulates NoProof) + _validator = new HeaderValidator( + _blockTree, + Always.Valid, // No seal validation (NoProof behavior) + specProvider, + new OneLoggerLogManager(new(_testLogger)) + ); + + // Parent block: baseFee=10, gasUsed=0, gasLimit=300000000 + _parentBlock = Build.A.Block + .WithDifficulty(0) // Post-merge + .WithBaseFeePerGas(10) + .WithGasUsed(0) + .WithGasLimit(300000000) + .WithNumber(5) + .TestObject; + + // Calculate expected baseFee for child block + // parentGasTarget = 300000000 / 2 = 150000000 + // gasDelta = 150000000 - 0 = 150000000 + // feeDelta = 10 * 150000000 / 150000000 / 8 = 1 + // expectedBaseFee = 10 - 1 = 9 + UInt256 expectedBaseFee = BaseFeeCalculator.Calculate( + _parentBlock.Header, + specProvider.GetSpec((ForkActivation)(_parentBlock.Number + 1)) + ); + Assert.That(expectedBaseFee, Is.EqualTo((UInt256)9), "Test setup: expected baseFee should be 9"); + + // Create block with INCORRECT baseFee (10 instead of 9) + _block = Build.A.Block + .WithParent(_parentBlock) + .WithDifficulty(0) + .WithBaseFeePerGas(10) // WRONG! Should be 9 + .WithGasUsed(0) + .WithGasLimit(300000000) + .WithNumber(_parentBlock.Number + 1) + .TestObject; + + _block.Header.Hash = _block.CalculateHash(); + + // Act: Validate the block + bool result = _validator.Validate(_block.Header, _parentBlock.Header); + + // Assert: Block MUST be rejected due to invalid baseFee + // Even though seal engine is NoProof, consensus rules must be enforced + Assert.That(result, Is.False, + "Block with invalid baseFee must be rejected even with NoProof seal engine. " + + $"Expected baseFee=9, actual baseFee=10"); + + // Verify the error message mentions baseFee + bool baseFeeErrorLogged = _testLogger.LogList.Any(log => + log.Contains("base fee", StringComparison.OrdinalIgnoreCase) || + log.Contains("baseFee", StringComparison.OrdinalIgnoreCase)); + Assert.That(baseFeeErrorLogged, Is.True, + "Validation should log baseFee mismatch error"); + } + + [Test] + public void Valid_baseFee_with_NoProof_seal_engine_should_pass() + { + // Arrange: Same setup as above + OverridableReleaseSpec spec = new(London.Instance) + { + Eip1559TransitionBlock = 0 + }; + TestSpecProvider specProvider = new(spec); + _validator = new HeaderValidator(_blockTree, Always.Valid, specProvider, + new OneLoggerLogManager(new(_testLogger))); + + _parentBlock = Build.A.Block + .WithDifficulty(0) + .WithBaseFeePerGas(10) + .WithGasUsed(0) + .WithGasLimit(300000000) + .WithNumber(5) + .TestObject; + + // Calculate CORRECT baseFee + UInt256 correctBaseFee = BaseFeeCalculator.Calculate( + _parentBlock.Header, + specProvider.GetSpec((ForkActivation)(_parentBlock.Number + 1)) + ); + + // Create block with CORRECT baseFee (9) + _block = Build.A.Block + .WithParent(_parentBlock) + .WithDifficulty(0) + .WithBaseFeePerGas(correctBaseFee) // CORRECT: 9 + .WithGasUsed(0) + .WithGasLimit(300000000) + .WithNumber(_parentBlock.Number + 1) + .TestObject; + + _block.Header.Hash = _block.CalculateHash(); + + // Act + bool result = _validator.Validate(_block.Header, _parentBlock.Header); + + // Assert: Valid block should pass + Assert.That(result, Is.True, + "Block with correct baseFee should be accepted with NoProof seal engine"); + } } diff --git a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs index c17cd8becb16..a317fd8176d5 100644 --- a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs +++ b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs @@ -106,6 +106,8 @@ public BlockHeader? LowestInsertedBeaconHeader private TaskCompletionSource? _taskCompletionSource; + private readonly long _genesisBlockNumber; + public BlockTree( IBlockStore? blockStore, IHeaderStore? headerDb, @@ -116,7 +118,8 @@ public BlockTree( ISpecProvider? specProvider, IBloomStorage? bloomStorage, ISyncConfig? syncConfig, - ILogManager? logManager) + ILogManager? logManager, + long genesisBlockNumber = 0) { _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _blockStore = blockStore ?? throw new ArgumentNullException(nameof(blockStore)); @@ -131,6 +134,8 @@ public BlockTree( throw new ArgumentNullException(nameof(chainLevelInfoRepository)); _oldestBlock = syncConfig.AncientBodiesBarrierCalc; + _genesisBlockNumber = genesisBlockNumber; + LoadSyncPivot(); byte[]? deletePointer = _blockInfoDb.Get(DeletePointerAddressInDb); @@ -142,7 +147,7 @@ public BlockTree( // Need to be here because it still need to run even if there are no genesis to store the null entry. LoadLowestInsertedHeader(); - ChainLevelInfo? genesisLevel = LoadLevel(0); + ChainLevelInfo? genesisLevel = LoadLevel(_genesisBlockNumber); if (genesisLevel is not null) { BlockInfo genesisBlockInfo = genesisLevel.BlockInfos[0]; @@ -203,7 +208,7 @@ public AddBlockResult Insert(BlockHeader header, BlockTreeInsertHeaderOptions he throw new InvalidOperationException("An attempt to insert a block header without a known bloom."); } - if (header.Number == 0) + if (header.Number == _genesisBlockNumber) { throw new InvalidOperationException("Genesis block should not be inserted."); } @@ -288,8 +293,8 @@ public void BulkInsertHeader(IReadOnlyList headers, throw new InvalidOperationException("Cannot accept new blocks at the moment."); } - using ArrayPoolList<(long, Bloom)> bloomToStore = new ArrayPoolList<(long, Bloom)>(headers.Count); - foreach (var header in headers) + using ArrayPoolList<(long, Bloom)> bloomToStore = new(headers.Count); + foreach (BlockHeader? header in headers) { if (header.Hash is null) { @@ -301,7 +306,7 @@ public void BulkInsertHeader(IReadOnlyList headers, throw new InvalidOperationException("An attempt to insert a block header without a known bloom."); } - if (header.Number == 0) + if (header.Number == _genesisBlockNumber) { throw new InvalidOperationException("Genesis block should not be inserted."); } @@ -325,9 +330,9 @@ public void BulkInsertHeader(IReadOnlyList headers, bool isOnMainChain = (headerOptions & BlockTreeInsertHeaderOptions.NotOnMainChain) == 0; bool beaconInsert = (headerOptions & BlockTreeInsertHeaderOptions.BeaconHeaderMetadata) != 0; - using ArrayPoolList<(long, BlockInfo)> blockInfos = new ArrayPoolList<(long, BlockInfo)>(headers.Count); + using ArrayPoolListRef<(long, BlockInfo)> blockInfos = new(headers.Count); - foreach (var header in headers) + foreach (BlockHeader? header in headers) { BlockInfo blockInfo = new(header.Hash, header.TotalDifficulty ?? 0); if (!beaconInsert) @@ -405,7 +410,7 @@ public AddBlockResult Insert(Block block, BlockTreeInsertBlockOptions insertBloc return AddBlockResult.CannotAccept; } - if (block.Number == 0) + if (block.Number == _genesisBlockNumber) { throw new InvalidOperationException("Genesis block should not be inserted."); } @@ -566,7 +571,7 @@ public AddBlockResult SuggestBlock(Block block, BlockTreeSuggestOptions options return null; } - BlockHeader? header = _headerStore.Get(blockHash, shouldCache: false, blockNumber: blockNumber); + BlockHeader? header = _headerStore.Get(blockHash, out bool fromCache, shouldCache: false, blockNumber: blockNumber); if (header is null) { bool allowInvalid = (options & BlockTreeLookupOptions.AllowInvalid) == BlockTreeLookupOptions.AllowInvalid; @@ -616,7 +621,7 @@ public AddBlockResult SuggestBlock(Block block, BlockTreeSuggestOptions options } } - if (header is not null && ShouldCache(header.Number)) + if (header is not null && !fromCache && ShouldCache(header.Number)) { _headerStore.Cache(header); } @@ -683,7 +688,7 @@ static ArrayPoolList FindHeadersReversedFast(BlockTree tree, BlockH return new ArrayPoolList(1) { startHeader }; } - ArrayPoolList result = new ArrayPoolList(numberOfBlocks, numberOfBlocks); + ArrayPoolList result = new(numberOfBlocks, numberOfBlocks); BlockHeader current = startHeader; int responseIndex = reverse ? 0 : numberOfBlocks - 1; @@ -720,7 +725,7 @@ as it does not require the step of resolving number -> hash */ } } - ArrayPoolList result = new ArrayPoolList(numberOfBlocks, numberOfBlocks); + ArrayPoolList result = new(numberOfBlocks, numberOfBlocks); BlockHeader current = startHeader; int directionMultiplier = reverse ? -1 : 1; int responseIndex = 0; @@ -1178,7 +1183,7 @@ private void MoveToMain(Block block, BatchWrite batch, bool wasProcessed, bool f if (forceUpdateHeadBlock || block.IsGenesis || HeadImprovementRequirementsSatisfied(block.Header)) { - if (block.Number == 0) + if (block.Number == _genesisBlockNumber) { Genesis = block.Header; } @@ -1340,11 +1345,11 @@ private ChainLevelInfo UpdateOrCreateLevel(long number, BlockInfo blockInfo, boo return level; } - private void UpdateOrCreateLevel(IReadOnlyList<(long number, BlockInfo blockInfo)> blockInfos, bool setAsMain = false) + private void UpdateOrCreateLevel(in ArrayPoolListRef<(long number, BlockInfo blockInfo)> blockInfos, bool setAsMain = false) { using BatchWrite? batch = _chainLevelInfoRepository.StartBatch(); - using ArrayPoolList blockNumbers = blockInfos.Select(b => b.number).ToPooledList(blockInfos.Count); + using ArrayPoolListRef blockNumbers = blockInfos.Select(b => b.number); // Yes, this is measurably faster using IOwnedReadOnlyList levels = _chainLevelInfoRepository.MultiLoadLevel(blockNumbers); @@ -1408,7 +1413,7 @@ private void UpdateOrCreateLevel(IReadOnlyList<(long number, BlockInfo blockInfo /// private bool ShouldCache(long number) { - return number == 0L || Head is null || number >= Head.Number - BlockStore.CacheSize; + return number == _genesisBlockNumber || Head is null || number >= Head.Number - BlockStore.CacheSize; } public ChainLevelInfo? FindLevel(long number) @@ -1429,6 +1434,7 @@ private bool ShouldCache(long number) return null; } + bool fromCache = false; Block? block = null; blockNumber ??= _headerStore.GetBlockNumber(blockHash); if (blockNumber is not null) @@ -1436,6 +1442,7 @@ private bool ShouldCache(long number) block = _blockStore.Get( blockNumber.Value, blockHash, + out fromCache, (options & BlockTreeLookupOptions.ExcludeTxHashes) != 0 ? RlpBehaviors.ExcludeHashes : RlpBehaviors.None, shouldCache: false); } @@ -1491,7 +1498,7 @@ private bool ShouldCache(long number) } } - if (block is not null && ShouldCache(block.Number)) + if (block is not null && !fromCache && ShouldCache(block.Number)) { _blockStore.Cache(block); _headerStore.Cache(block.Header); diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs index 3cb168ba391f..9c6dffeae72d 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs @@ -13,13 +13,12 @@ namespace Nethermind.Blockchain.Blocks; -public class BlockStore([KeyFilter(DbNames.Blocks)] IDb blockDb) : IBlockStore +public class BlockStore([KeyFilter(DbNames.Blocks)] IDb blockDb, IHeaderDecoder headerDecoder = null) : IBlockStore { - private readonly BlockDecoder _blockDecoder = new(); + private readonly BlockDecoder _blockDecoder = new(headerDecoder ?? new HeaderDecoder()); public const int CacheSize = 128 + 32; - private readonly ClockCache - _blockCache = new(CacheSize); + private readonly ClockCache _blockCache = new(CacheSize); public void SetMetadata(byte[] key, byte[] value) { @@ -67,9 +66,16 @@ public void Delete(long blockNumber, Hash256 blockHash) public Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = false) { - Block? b = blockDb.Get(blockNumber, blockHash, _blockDecoder, _blockCache, rlpBehaviors, shouldCache); + Block? b = blockDb.Get(blockNumber, blockHash, _blockDecoder, out _, _blockCache, rlpBehaviors, shouldCache); if (b is not null) return b; - return blockDb.Get(blockHash, _blockDecoder, _blockCache, rlpBehaviors, shouldCache); + return blockDb.Get(blockHash, _blockDecoder, out _, _blockCache, rlpBehaviors, shouldCache); + } + + public Block? Get(long blockNumber, Hash256 blockHash, out bool fromCache, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = false) + { + Block? b = blockDb.Get(blockNumber, blockHash, _blockDecoder, out fromCache, _blockCache, rlpBehaviors, shouldCache); + if (b is not null) return b; + return blockDb.Get(blockHash, _blockDecoder, out fromCache, _blockCache, rlpBehaviors, shouldCache); } public byte[]? GetRlp(long blockNumber, Hash256 blockHash) @@ -89,7 +95,7 @@ public void Delete(long blockNumber, Hash256 blockHash) MemoryManager? memoryOwner = blockDb.GetOwnedMemory(keyWithBlockNumber); memoryOwner ??= blockDb.GetOwnedMemory(blockHash.Bytes); - return BlockDecoder.DecodeToReceiptRecoveryBlock(memoryOwner, memoryOwner?.Memory ?? Memory.Empty, RlpBehaviors.None); + return _blockDecoder.DecodeToReceiptRecoveryBlock(memoryOwner, memoryOwner?.Memory ?? Memory.Empty, RlpBehaviors.None); } public void Cache(Block block) diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs index eca635bc9d21..2efb22ed83d5 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs @@ -15,7 +15,9 @@ public interface IBlockStore { void Insert(Block block, WriteFlags writeFlags = WriteFlags.None); void Delete(long blockNumber, Hash256 blockHash); - Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true); + Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true) + => Get(blockNumber, blockHash, out _, rlpBehaviors, shouldCache); + Block? Get(long blockNumber, Hash256 blockHash, out bool fromCache, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true); byte[]? GetRlp(long blockNumber, Hash256 blockHash); ReceiptRecoveryBlock? GetReceiptRecoveryBlock(long blockNumber, Hash256 blockHash); void Cache(Block block); diff --git a/src/Nethermind/Nethermind.Blockchain/Find/BlockParameter.cs b/src/Nethermind/Nethermind.Blockchain/Find/BlockParameter.cs index cf6136e7d67b..3aa65b7e8d86 100644 --- a/src/Nethermind/Nethermind.Blockchain/Find/BlockParameter.cs +++ b/src/Nethermind/Nethermind.Blockchain/Find/BlockParameter.cs @@ -189,7 +189,7 @@ public override void Write(Utf8JsonWriter writer, BlockParameter value, JsonSeri { return BlockParameter.Latest; } - if (tokenType == JsonTokenType.Number) + if (tokenType == JsonTokenType.Number & !EthereumJsonSerializer.StrictHexFormat) { return new BlockParameter(reader.GetInt64()); } @@ -249,7 +249,7 @@ public override void Write(Utf8JsonWriter writer, BlockParameter value, JsonSeri return new BlockParameter(value); } - if (Utf8Parser.TryParse(span, out value, out _)) + if (!EthereumJsonSerializer.StrictHexFormat && Utf8Parser.TryParse(span, out value, out _)) { return new BlockParameter(value); } @@ -291,7 +291,7 @@ public override void Write(Utf8JsonWriter writer, BlockParameter value, JsonSeri private static void ThrowInvalidFormatting() { - throw new InvalidOperationException("unknown block parameter type"); + throw new FormatException("unknown block parameter type"); } public static BlockParameter GetBlockParameter(string? value) diff --git a/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs b/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs index 425d56d19559..6dc9d7674536 100644 --- a/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Headers/HeaderStore.cs @@ -19,17 +19,20 @@ public class HeaderStore : IHeaderStore { // SyncProgressResolver MaxLookupBack is 256, add 16 wiggle room public const int CacheSize = 256 + 16; + // Go a bit further back for numbers as smaller + public const int NumberCacheSize = CacheSize * 4; private readonly IDb _headerDb; private readonly IDb _blockNumberDb; - private readonly HeaderDecoder _headerDecoder = new(); - private readonly ClockCache _headerCache = - new(CacheSize); + private readonly IHeaderDecoder _headerDecoder; + private readonly ClockCache _headerCache = new(CacheSize); + private readonly ClockCache _numberCache = new(NumberCacheSize); - public HeaderStore([KeyFilter(DbNames.Headers)] IDb headerDb, [KeyFilter(DbNames.BlockNumbers)] IDb blockNumberDb) + public HeaderStore([KeyFilter(DbNames.Headers)] IDb headerDb, [KeyFilter(DbNames.BlockNumbers)] IDb blockNumberDb, IHeaderDecoder? decoder = null) { _headerDb = headerDb; _blockNumberDb = blockNumberDb; + _headerDecoder = decoder ?? new HeaderDecoder(); } public void Insert(BlockHeader header) @@ -45,30 +48,37 @@ public void BulkInsert(IReadOnlyList headers) using IWriteBatch blockNumberWriteBatch = _blockNumberDb.StartWriteBatch(); Span blockNumberSpan = stackalloc byte[8]; - foreach (var header in headers) + foreach (BlockHeader header in headers) { using NettyRlpStream newRlp = _headerDecoder.EncodeToNewNettyStream(header); headerWriteBatch.Set(header.Number, header.Hash, newRlp.AsSpan()); header.Number.WriteBigEndian(blockNumberSpan); blockNumberWriteBatch.Set(header.Hash, blockNumberSpan); + CacheNumber(header.Hash, header.Number); } } - public BlockHeader? Get(Hash256 blockHash, bool shouldCache = false, long? blockNumber = null) + public BlockHeader? Get(Hash256 blockHash, out bool fromCache, bool shouldCache = false, long? blockNumber = null) { blockNumber ??= GetBlockNumberFromBlockNumberDb(blockHash); - BlockHeader? header = null; + BlockHeader? header; if (blockNumber is not null) { - header = _headerDb.Get(blockNumber.Value, blockHash, _headerDecoder, _headerCache, shouldCache: shouldCache); + header = _headerDb.Get(blockNumber.Value, blockHash, _headerDecoder, out fromCache, _headerCache, shouldCache: shouldCache); + if (header is not null) + { + return header; + } } - return header ?? _headerDb.Get(blockHash, _headerDecoder, _headerCache, shouldCache: shouldCache); + + return _headerDb.Get(blockHash, _headerDecoder, out fromCache, _headerCache, shouldCache: shouldCache); } public void Cache(BlockHeader header) { + CacheNumber(header.Hash, header.Number); _headerCache.Set(header.Hash, header); } @@ -79,6 +89,7 @@ public void Delete(Hash256 blockHash) _blockNumberDb.Delete(blockHash); _headerDb.Delete(blockHash); _headerCache.Delete(blockHash); + _numberCache.Delete(blockHash); } public void InsertBlockNumber(Hash256 blockHash, long blockNumber) @@ -86,19 +97,50 @@ public void InsertBlockNumber(Hash256 blockHash, long blockNumber) Span blockNumberSpan = stackalloc byte[8]; blockNumber.WriteBigEndian(blockNumberSpan); _blockNumberDb.Set(blockHash, blockNumberSpan); + CacheNumber(blockHash, blockNumber); } + private void CacheNumber(Hash256 blockHash, long blockNumber) + => _numberCache.Set(blockHash, blockNumber); + public long? GetBlockNumber(Hash256 blockHash) { + if (_numberCache.TryGet(blockHash, out long number)) + { + return number; + } + + return GetBlockNumberThroughHeaderCache(blockHash); + } + + private long? GetBlockNumberThroughHeaderCache(Hash256 blockHash) + { + if (_headerCache.TryGet(blockHash, out BlockHeader? header)) + { + CacheNumber(blockHash, header.Number); + return header.Number; + } + long? blockNumber = GetBlockNumberFromBlockNumberDb(blockHash); if (blockNumber is not null) return blockNumber.Value; // Probably still hash based - return Get(blockHash)?.Number; + blockNumber = Get(blockHash, out _)?.Number; + if (blockNumber.HasValue) + { + CacheNumber(blockHash, blockNumber.Value); + } + return blockNumber; } private long? GetBlockNumberFromBlockNumberDb(Hash256 blockHash) { + // Double check cache as we have done a fair amount of checks + // since the cache check and something else might have populated it. + if (_numberCache.TryGet(blockHash, out long number)) + { + return number; + } Span numberSpan = _blockNumberDb.GetSpan(blockHash); if (numberSpan.IsNullOrEmpty()) return null; try @@ -108,7 +150,9 @@ public void InsertBlockNumber(Hash256 blockHash, long blockNumber) throw new InvalidDataException($"Unexpected number span length: {numberSpan.Length}"); } - return BinaryPrimitives.ReadInt64BigEndian(numberSpan); + number = BinaryPrimitives.ReadInt64BigEndian(numberSpan); + CacheNumber(blockHash, number); + return number; } finally { diff --git a/src/Nethermind/Nethermind.Blockchain/Headers/IHeaderStore.cs b/src/Nethermind/Nethermind.Blockchain/Headers/IHeaderStore.cs index dc56e2d91167..09dcea7103b6 100644 --- a/src/Nethermind/Nethermind.Blockchain/Headers/IHeaderStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Headers/IHeaderStore.cs @@ -12,7 +12,9 @@ public interface IHeaderStore { void Insert(BlockHeader header); void BulkInsert(IReadOnlyList headers); - BlockHeader? Get(Hash256 blockHash, bool shouldCache, long? blockNumber = null); + BlockHeader? Get(Hash256 blockHash, bool shouldCache = true, long? blockNumber = null) + => Get(blockHash, out _, shouldCache, blockNumber); + BlockHeader? Get(Hash256 blockHash, out bool fromCache, bool shouldCache = true, long? blockNumber = null); void Cache(BlockHeader header); void Delete(Hash256 blockHash); void InsertBlockNumber(Hash256 blockHash, long blockNumber); diff --git a/src/Nethermind/Nethermind.Blockchain/InvalidTransactionException.cs b/src/Nethermind/Nethermind.Blockchain/InvalidTransactionException.cs new file mode 100644 index 000000000000..afa267ad792d --- /dev/null +++ b/src/Nethermind/Nethermind.Blockchain/InvalidTransactionException.cs @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Evm.TransactionProcessing; + +namespace Nethermind.Blockchain; + +public class InvalidTransactionException : InvalidBlockException +{ + public InvalidTransactionException(BlockHeader header, string message, TransactionResult result, Exception? innerException = null) + : base(header, message, innerException) => Reason = result; + + public TransactionResult Reason; +} diff --git a/src/Nethermind/Nethermind.Blockchain/ReceiptCanonicalityMonitor.cs b/src/Nethermind/Nethermind.Blockchain/ReceiptCanonicalityMonitor.cs index 4d6b55283d95..eea17931dabe 100644 --- a/src/Nethermind/Nethermind.Blockchain/ReceiptCanonicalityMonitor.cs +++ b/src/Nethermind/Nethermind.Blockchain/ReceiptCanonicalityMonitor.cs @@ -25,7 +25,7 @@ public ReceiptCanonicalityMonitor(IReceiptStorage? receiptStorage, ILogManager? { _receiptStorage = receiptStorage ?? throw new ArgumentNullException(nameof(receiptStorage)); _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - _receiptStorage.ReceiptsInserted += OnBlockAddedToMain; + _receiptStorage.NewCanonicalReceipts += OnBlockAddedToMain; } private void OnBlockAddedToMain(object sender, BlockReplacementEventArgs e) @@ -55,7 +55,7 @@ private void TriggerReceiptInsertedEvent(Block newBlock, Block? previousBlock) public void Dispose() { - _receiptStorage.ReceiptsInserted -= OnBlockAddedToMain; + _receiptStorage.NewCanonicalReceipts -= OnBlockAddedToMain; } } } diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/IReceiptStorage.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/IReceiptStorage.cs index 65633a1fa7ad..dd7caa7366b7 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/IReceiptStorage.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/IReceiptStorage.cs @@ -19,8 +19,16 @@ public interface IReceiptStorage : IReceiptFinder void RemoveReceipts(Block block); /// - /// Receipts for a block are inserted + /// Receipts for canonical chain changed. /// - event EventHandler ReceiptsInserted; + event EventHandler? NewCanonicalReceipts; + + /// + /// Receipts for any block are inserted. + /// + /// + /// This is invoked for both canonical and non-canonical blocks. + /// + event EventHandler? ReceiptsInserted; } } diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/InMemoryReceiptStorage.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/InMemoryReceiptStorage.cs index 1e3eec074a49..a8824cb66a4c 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/InMemoryReceiptStorage.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/InMemoryReceiptStorage.cs @@ -18,7 +18,8 @@ public class InMemoryReceiptStorage : IReceiptStorage private readonly ConcurrentDictionary _transactions = new(); #pragma warning disable CS0067 - public event EventHandler ReceiptsInserted; + public event EventHandler? NewCanonicalReceipts; + public event EventHandler? ReceiptsInserted; #pragma warning restore CS0067 public InMemoryReceiptStorage(bool allowReceiptIterator = true, IBlockTree? blockTree = null) @@ -32,7 +33,7 @@ public InMemoryReceiptStorage(bool allowReceiptIterator = true, IBlockTree? bloc private void BlockTree_BlockAddedToMain(object? sender, BlockReplacementEventArgs e) { EnsureCanonical(e.Block); - ReceiptsInserted?.Invoke(this, e); + NewCanonicalReceipts?.Invoke(this, e); } public Hash256 FindBlockHash(Hash256 txHash) @@ -73,6 +74,8 @@ public void Insert(Block block, TxReceipt[] txReceipts, IReleaseSpec spec, bool { EnsureCanonical(block); } + + ReceiptsInserted?.Invoke(this, new(block.Header, txReceipts)); } public bool HasBlock(long blockNumber, Hash256 hash) diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/NullReceiptStorage.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/NullReceiptStorage.cs index 1c06f8b3d21e..7ed7bb5637ae 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/NullReceiptStorage.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/NullReceiptStorage.cs @@ -13,7 +13,8 @@ public class NullReceiptStorage : IReceiptStorage public static NullReceiptStorage Instance { get; } = new(); #pragma warning disable CS0067 - public event EventHandler ReceiptsInserted; + public event EventHandler? NewCanonicalReceipts; + public event EventHandler? ReceiptsInserted; #pragma warning restore CS0067 public Hash256? FindBlockHash(Hash256 hash) => null; diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs index 5d5715278943..5f2cbd740052 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs @@ -38,7 +38,8 @@ public class PersistentReceiptStorage : IReceiptStorage private const int CacheSize = 64; private readonly LruCache _receiptsCache = new(CacheSize, CacheSize, "receipts"); - public event EventHandler ReceiptsInserted; + public event EventHandler? NewCanonicalReceipts; + public event EventHandler? ReceiptsInserted; public PersistentReceiptStorage( IColumnsDb receiptsDb, @@ -73,7 +74,7 @@ public PersistentReceiptStorage( private void BlockTreeOnBlockAddedToMain(object? sender, BlockReplacementEventArgs e) { EnsureCanonical(e.Block); - ReceiptsInserted?.Invoke(this, e); + NewCanonicalReceipts?.Invoke(this, e); // Dont block main loop Task.Run(() => @@ -288,6 +289,8 @@ public void Insert(Block block, TxReceipt[]? txReceipts, IReleaseSpec spec, bool { EnsureCanonical(block, lastBlockNumber); } + + ReceiptsInserted?.Invoke(this, new(block.Header, txReceipts)); } public long MigratedBlockNumber diff --git a/src/Nethermind/Nethermind.Clique.Test/CliqueRpcModuleTests.cs b/src/Nethermind/Nethermind.Clique.Test/CliqueRpcModuleTests.cs index 943ed3cf80cd..497061367a98 100644 --- a/src/Nethermind/Nethermind.Clique.Test/CliqueRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.Clique.Test/CliqueRpcModuleTests.cs @@ -40,7 +40,7 @@ public void Sets_clique_block_producer_properly() Substitute.For(), Substitute.For(), new CliqueSealer(signer, cliqueConfig, Substitute.For(), LimboLogs.Instance), - new TargetAdjustedGasLimitCalculator(HoleskySpecProvider.Instance, new BlocksConfig()), + new TargetAdjustedGasLimitCalculator(HoodiSpecProvider.Instance, new BlocksConfig()), MainnetSpecProvider.Instance, cliqueConfig, LimboLogs.Instance); diff --git a/src/Nethermind/Nethermind.Config.Test/ConfigFileTestsBase.cs b/src/Nethermind/Nethermind.Config.Test/ConfigFileTestsBase.cs index 9056ca2a2f34..3d8d49777668 100644 --- a/src/Nethermind/Nethermind.Config.Test/ConfigFileTestsBase.cs +++ b/src/Nethermind/Nethermind.Config.Test/ConfigFileTestsBase.cs @@ -77,9 +77,9 @@ protected IEnumerable SepoliaConfigs protected IEnumerable ChiadoConfigs => Configs.Where(static config => config.Contains("chiado")); - [ConfigFileGroup("holesky")] + [ConfigFileGroup("hoodi")] protected IEnumerable HoleskyConfigs - => Configs.Where(static config => config.Contains("holesky")); + => Configs.Where(static config => config.Contains("hoodi")); [ConfigFileGroup("spaceneth")] protected IEnumerable SpacenethConfigs diff --git a/src/Nethermind/Nethermind.Config/BlocksConfig.cs b/src/Nethermind/Nethermind.Config/BlocksConfig.cs index 36a1c5c5834d..4d8df415df72 100644 --- a/src/Nethermind/Nethermind.Config/BlocksConfig.cs +++ b/src/Nethermind/Nethermind.Config/BlocksConfig.cs @@ -85,6 +85,9 @@ public string ExtraData _extraDataBytes = bytes; } } + + public bool BuildBlocksOnMainState { get; set; } + public byte[] GetExtraDataBytes() { return _extraDataBytes; diff --git a/src/Nethermind/Nethermind.Config/IBlocksConfig.cs b/src/Nethermind/Nethermind.Config/IBlocksConfig.cs index 91d4345d1eb5..320bee4879c1 100644 --- a/src/Nethermind/Nethermind.Config/IBlocksConfig.cs +++ b/src/Nethermind/Nethermind.Config/IBlocksConfig.cs @@ -55,5 +55,8 @@ public interface IBlocksConfig : IConfig [ConfigItem(Description = "The ticker that gas rewards are denominated in for processing logs", DefaultValue = "ETH", HiddenFromDocs = true)] string GasToken { get; set; } + [ConfigItem(Description = "Builds blocks on main (non-readonly) state", DefaultValue = "false", HiddenFromDocs = true)] + bool BuildBlocksOnMainState { get; set; } + byte[] GetExtraDataBytes(); } diff --git a/src/Nethermind/Nethermind.Consensus.Test/GenesisLoaderTests.cs b/src/Nethermind/Nethermind.Consensus.Test/GenesisLoaderTests.cs new file mode 100644 index 000000000000..9c41299d4ec6 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus.Test/GenesisLoaderTests.cs @@ -0,0 +1,217 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Threading; +using FluentAssertions; +using Nethermind.Blockchain; +using Nethermind.Consensus.Processing; +using Nethermind.Core; +using Nethermind.Core.Test.Builders; +using Nethermind.Evm.State; +using Nethermind.Logging; +using Nethermind.State; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Consensus.Test; + +[TestFixture] +public class GenesisLoaderTests +{ + [Test] + public void Load_ShouldFlushCacheAfterSuccessfulGenesisProcessing() + { + // Arrange + Block genesisBlock = Build.A.Block.Genesis.TestObject; + + IGenesisBuilder genesisBuilder = Substitute.For(); + genesisBuilder.Build().Returns(genesisBlock); + + IStateReader stateReader = Substitute.For(); + + IBlockTree blockTree = Substitute.For(); + blockTree.When(x => x.SuggestBlock(Arg.Any())).Do(_ => + { + // Simulate block processing by triggering NewHeadBlock event + blockTree.NewHeadBlock += Raise.EventWith(blockTree, new BlockEventArgs(genesisBlock)); + }); + + IWorldState worldState = Substitute.For(); + IDisposable scopeDisposable = Substitute.For(); + worldState.BeginScope(IWorldState.PreGenesis).Returns(scopeDisposable); + + IWorldStateManager worldStateManager = Substitute.For(); + + IBlockchainProcessor blockchainProcessor = Substitute.For(); + + GenesisLoader.Config config = new(null, TimeSpan.FromSeconds(10)); + ILogManager logManager = LimboLogs.Instance; + + GenesisLoader loader = new( + genesisBuilder, + stateReader, + blockTree, + worldState, + worldStateManager, + blockchainProcessor, + config, + logManager + ); + + // Act + loader.Load(); + + // Assert - verify FlushCache was called + worldStateManager.Received(1).FlushCache(Arg.Any()); + } + + [Test] + public void Load_ShouldNotFlushCache_WhenGenesisProcessingTimesOut() + { + // Arrange + Block genesisBlock = Build.A.Block.Genesis.TestObject; + + IGenesisBuilder genesisBuilder = Substitute.For(); + genesisBuilder.Build().Returns(genesisBlock); + + IStateReader stateReader = Substitute.For(); + + IBlockTree blockTree = Substitute.For(); + blockTree.When(x => x.SuggestBlock(Arg.Any())).Do(_ => + { + // Do nothing - simulate timeout + }); + + IWorldState worldState = Substitute.For(); + IDisposable scopeDisposable = Substitute.For(); + worldState.BeginScope(IWorldState.PreGenesis).Returns(scopeDisposable); + + IWorldStateManager worldStateManager = Substitute.For(); + + IBlockchainProcessor blockchainProcessor = Substitute.For(); + + GenesisLoader.Config config = new(null, TimeSpan.FromMilliseconds(100)); + ILogManager logManager = LimboLogs.Instance; + + GenesisLoader loader = new( + genesisBuilder, + stateReader, + blockTree, + worldState, + worldStateManager, + blockchainProcessor, + config, + logManager + ); + + // Act & Assert - expect timeout exception + Assert.Throws(() => loader.Load()); + + // Verify FlushCache was NOT called since genesis processing failed + worldStateManager.DidNotReceive().FlushCache(Arg.Any()); + } + + [Test] + public void Load_ShouldNotFlushCache_WhenGenesisBlockIsInvalid() + { + // Arrange + Block genesisBlock = Build.A.Block.Genesis.TestObject; + + IGenesisBuilder genesisBuilder = Substitute.For(); + genesisBuilder.Build().Returns(genesisBlock); + + IStateReader stateReader = Substitute.For(); + + IBlockTree blockTree = Substitute.For(); + + IWorldState worldState = Substitute.For(); + IDisposable scopeDisposable = Substitute.For(); + worldState.BeginScope(IWorldState.PreGenesis).Returns(scopeDisposable); + + IWorldStateManager worldStateManager = Substitute.For(); + + IBlockchainProcessor blockchainProcessor = Substitute.For(); + blockTree.When(x => x.SuggestBlock(Arg.Any())).Do(_ => + { + // Simulate invalid block by triggering InvalidBlock event + blockchainProcessor.InvalidBlock += Raise.EventWith( + blockchainProcessor, + new IBlockchainProcessor.InvalidBlockEventArgs { InvalidBlock = genesisBlock }); + }); + + GenesisLoader.Config config = new(null, TimeSpan.FromSeconds(10)); + ILogManager logManager = LimboLogs.Instance; + + GenesisLoader loader = new( + genesisBuilder, + stateReader, + blockTree, + worldState, + worldStateManager, + blockchainProcessor, + config, + logManager + ); + + // Act & Assert - expect InvalidBlockException + Assert.Throws(() => loader.Load()); + + // Verify FlushCache was NOT called since genesis was invalid + worldStateManager.DidNotReceive().FlushCache(Arg.Any()); + } + + [Test] + public void Load_ShouldFlushCacheAfterScopeExit() + { + // Arrange + Block genesisBlock = Build.A.Block.Genesis.TestObject; + + IGenesisBuilder genesisBuilder = Substitute.For(); + genesisBuilder.Build().Returns(genesisBlock); + + IStateReader stateReader = Substitute.For(); + + IBlockTree blockTree = Substitute.For(); + bool scopeExited = false; + blockTree.When(x => x.SuggestBlock(Arg.Any())).Do(_ => + { + // Simulate block processing by triggering NewHeadBlock event + blockTree.NewHeadBlock += Raise.EventWith(blockTree, new BlockEventArgs(genesisBlock)); + }); + + IWorldState worldState = Substitute.For(); + IDisposable scopeDisposable = Substitute.For(); + scopeDisposable.When(x => x.Dispose()).Do(_ => scopeExited = true); + worldState.BeginScope(IWorldState.PreGenesis).Returns(scopeDisposable); + + IWorldStateManager worldStateManager = Substitute.For(); + worldStateManager.When(x => x.FlushCache(Arg.Any())).Do(_ => + { + // Verify that scope was exited before FlushCache was called + scopeExited.Should().BeTrue("FlushCache should be called after scope exit"); + }); + + IBlockchainProcessor blockchainProcessor = Substitute.For(); + + GenesisLoader.Config config = new(null, TimeSpan.FromSeconds(10)); + ILogManager logManager = LimboLogs.Instance; + + GenesisLoader loader = new( + genesisBuilder, + stateReader, + blockTree, + worldState, + worldStateManager, + blockchainProcessor, + config, + logManager + ); + + // Act + loader.Load(); + + // Assert - verify FlushCache was called after scope exit + worldStateManager.Received(1).FlushCache(Arg.Any()); + } +} diff --git a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs index b45d4c7299b6..f510d9f8413e 100644 --- a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs @@ -61,27 +61,36 @@ public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] if (!spec.RequestsEnabled || block.IsGenesis) return; - using ArrayPoolList requests = new(3); + ArrayPoolListRef requests = new(3); + try + { + ProcessDeposits(block, receipts, spec, ref requests); - ProcessDeposits(block, receipts, spec, requests); + if (spec.WithdrawalRequestsEnabled) + { + ReadRequests(block, state, spec.Eip7002ContractAddress, ref requests, _withdrawalTransaction, + ExecutionRequestType.WithdrawalRequest, + BlockErrorMessages.WithdrawalsContractEmpty, BlockErrorMessages.WithdrawalsContractFailed); + } - if (spec.WithdrawalRequestsEnabled) - { - ReadRequests(block, state, spec.Eip7002ContractAddress, requests, _withdrawalTransaction, ExecutionRequestType.WithdrawalRequest, - BlockErrorMessages.WithdrawalsContractEmpty, BlockErrorMessages.WithdrawalsContractFailed); - } + if (spec.ConsolidationRequestsEnabled) + { + ReadRequests(block, state, spec.Eip7251ContractAddress, ref requests, _consolidationTransaction, + ExecutionRequestType.ConsolidationRequest, + BlockErrorMessages.ConsolidationsContractEmpty, BlockErrorMessages.ConsolidationsContractFailed); + } - if (spec.ConsolidationRequestsEnabled) + block.ExecutionRequests = [.. requests]; + block.Header.RequestsHash = + ExecutionRequestExtensions.CalculateHashFromFlatEncodedRequests(block.ExecutionRequests); + } + finally { - ReadRequests(block, state, spec.Eip7251ContractAddress, requests, _consolidationTransaction, ExecutionRequestType.ConsolidationRequest, - BlockErrorMessages.ConsolidationsContractEmpty, BlockErrorMessages.ConsolidationsContractFailed); + requests.Dispose(); } - - block.ExecutionRequests = [.. requests]; - block.Header.RequestsHash = ExecutionRequestExtensions.CalculateHashFromFlatEncodedRequests(block.ExecutionRequests); } - private void ProcessDeposits(Block block, TxReceipt[] receipts, IReleaseSpec spec, ArrayPoolList requests) + private void ProcessDeposits(Block block, TxReceipt[] receipts, IReleaseSpec spec, ref ArrayPoolListRef requests) { if (!spec.DepositsEnabled) return; @@ -160,7 +169,7 @@ static void Validate(Block block, object obj, string name, int expectedSize) } } - private void ReadRequests(Block block, IWorldState state, Address contractAddress, ArrayPoolList requests, + private void ReadRequests(Block block, IWorldState state, Address contractAddress, ref ArrayPoolListRef requests, Transaction systemTx, ExecutionRequestType type, string contractEmptyError, string contractFailedError) { if (!state.HasCode(contractAddress)) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionPicker.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionPicker.cs index d06684f97b2c..b1ff6fdd855e 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionPicker.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionPicker.cs @@ -78,7 +78,7 @@ public virtual AddingTxEventArgs CanAddTransaction(Block block, Transaction curr IReleaseSpec spec = _specProvider.GetSpec(block.Header); if (currentTx.IsAboveInitCode(spec)) { - return args.Set(TxAction.Skip, TransactionResult.TransactionSizeOverMaxInitCodeSize.Error); + return args.Set(TxAction.Skip, TransactionResult.TransactionSizeOverMaxInitCodeSize.ErrorDescription); } if (!_ignoreEip3607 && stateProvider.IsInvalidContractSender(spec, currentTx.SenderAddress)) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs index 19f15ffb97d0..1418fe284017 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs @@ -56,7 +56,7 @@ public virtual TxReceipt[] ProcessTransactions(Block block, ProcessingOptions pr int txCount = blockToProduce is not null ? defaultTxCount : block.Transactions.Length; IEnumerable transactions = blockToProduce?.Transactions ?? block.Transactions; - using ArrayPoolList includedTx = new(txCount); + using ArrayPoolListRef includedTx = new(txCount); HashSet consideredTx = new(ByHashTxComparer.Instance); int i = 0; @@ -112,7 +112,7 @@ private TxAction ProcessTransaction( } else { - args.Set(TxAction.Skip, result.Error!); + args.Set(TxAction.Skip, result.ErrorDescription!); } } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs index 0f1dc661c729..f33be3a63766 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs @@ -45,14 +45,14 @@ public TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processing protected virtual void ProcessTransaction(Block block, Transaction currentTx, int index, BlockReceiptsTracer receiptsTracer, ProcessingOptions processingOptions) { TransactionResult result = transactionProcessor.ProcessTransaction(currentTx, receiptsTracer, processingOptions, stateProvider); - if (!result) ThrowInvalidBlockException(result, block.Header, currentTx, index); + if (!result) ThrowInvalidTransactionException(result, block.Header, currentTx, index); transactionProcessedEventHandler?.OnTransactionProcessed(new TxProcessedEventArgs(index, currentTx, block.Header, receiptsTracer.TxReceipts[index])); } [DoesNotReturn, StackTraceHidden] - private void ThrowInvalidBlockException(TransactionResult result, BlockHeader header, Transaction currentTx, int index) + private void ThrowInvalidTransactionException(TransactionResult result, BlockHeader header, Transaction currentTx, int index) { - throw new InvalidBlockException(header, $"Transaction {currentTx.Hash} at index {index} failed with error {result.Error}"); + throw new InvalidTransactionException(header, $"Transaction {currentTx.Hash} at index {index} failed with error {result.Error}", result); } /// diff --git a/src/Nethermind/Nethermind.Consensus/Processing/GenesisLoader.cs b/src/Nethermind/Nethermind.Consensus/Processing/GenesisLoader.cs index c743aefa99ef..fd369d528e87 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/GenesisLoader.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/GenesisLoader.cs @@ -18,6 +18,7 @@ public class GenesisLoader( IStateReader stateReader, IBlockTree blockTree, IWorldState worldState, + IWorldStateManager worldStateManager, IBlockchainProcessor blockchainProcessor, GenesisLoader.Config genesisConfig, ILogManager logManager @@ -29,6 +30,12 @@ public record Config(Hash256? ExpectedGenesisHash, TimeSpan GenesisTimeout); ILogger _logger = logManager.GetClassLogger(); public void Load() + { + DoLoad(); + worldStateManager.FlushCache(CancellationToken.None); + } + + private void DoLoad() { using var _ = worldState.BeginScope(IWorldState.PreGenesis); diff --git a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingOptions.cs b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingOptions.cs index 82799e5578dd..d137d0ad04a2 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingOptions.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingOptions.cs @@ -38,7 +38,7 @@ public enum ProcessingOptions /// /// Does not verify transaction nonces during processing. /// - DoNotVerifyNonce = 32, + LoadNonceFromState = 32, /// /// After processing it will not update the block tree head even if the processed block has the highest @@ -64,7 +64,7 @@ public enum ProcessingOptions /// /// EVM tracing needs to process blocks without storing the data on chain. /// - Trace = ForceProcessing | ReadOnlyChain | DoNotVerifyNonce | NoValidation, + Trace = ForceProcessing | ReadOnlyChain | LoadNonceFromState | NoValidation, /// /// EVM tracing needs to process one or more transactions on top of the specified block (instead of the previous one) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/TransactionProcessorAdapterExtensions.cs b/src/Nethermind/Nethermind.Consensus/Processing/TransactionProcessorAdapterExtensions.cs index b8a8e275233e..2f125d18f1c7 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/TransactionProcessorAdapterExtensions.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/TransactionProcessorAdapterExtensions.cs @@ -17,7 +17,7 @@ public static TransactionResult ProcessTransaction(this ITransactionProcessorAda ProcessingOptions processingOptions, IWorldState stateProvider) { - if (processingOptions.ContainsFlag(ProcessingOptions.DoNotVerifyNonce) && currentTx.SenderAddress != Address.SystemUser) + if (processingOptions.ContainsFlag(ProcessingOptions.LoadNonceFromState) && currentTx.SenderAddress != Address.SystemUser) { currentTx.Nonce = stateProvider.GetNonce(currentTx.SenderAddress!); } diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs index 00d5328a5bc2..ce7b47a24d0e 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs @@ -34,7 +34,7 @@ namespace Nethermind.Consensus.Producers /// public abstract class BlockProducerBase : IBlockProducer { - private IBlockchainProcessor Processor { get; } + protected IBlockchainProcessor Processor { get; } protected IBlockTree BlockTree { get; } private ITimestamper Timestamper { get; } @@ -189,8 +189,11 @@ protected BlockProducerBase( protected virtual Task SealBlock(Block block, BlockHeader parent, CancellationToken token) => Sealer.SealBlock(block, token); - protected virtual Block? ProcessPreparedBlock(Block block, IBlockTracer? blockTracer, CancellationToken token = default) => - Processor.Process(block, ProcessingOptions.ProducingBlock, blockTracer ?? NullBlockTracer.Instance, token); + protected virtual Block? ProcessPreparedBlock(Block block, IBlockTracer? blockTracer, + CancellationToken token = default) + { + return Processor.Process(block, GetProcessingOptions(), blockTracer ?? NullBlockTracer.Instance, token); + } private bool PreparedBlockCanBeMined(Block? block) { @@ -244,5 +247,12 @@ protected virtual BlockToProduce PrepareBlock(BlockHeader parent, PayloadAttribu return new BlockToProduce(header, transactions, Array.Empty(), payloadAttributes?.Withdrawals); } + + private ProcessingOptions GetProcessingOptions() + { + if (_blocksConfig.BuildBlocksOnMainState) + return ProcessingOptions.NoValidation | ProcessingOptions.StoreReceipts | ProcessingOptions.DoNotUpdateHead; + return ProcessingOptions.ProducingBlock; + } } } diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs index 6f5923352525..83b40ae45eb1 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs @@ -27,7 +27,6 @@ protected virtual ContainerBuilder ConfigureBuilder(ContainerBuilder builder) => .AddScoped() .AddDecorator() .AddDecorator() - .AddScoped(); public IBlockProducerEnv Create() diff --git a/src/Nethermind/Nethermind.Consensus/Producers/GlobalWorldStateBlockProducerEnvFactory.cs b/src/Nethermind/Nethermind.Consensus/Producers/GlobalWorldStateBlockProducerEnvFactory.cs new file mode 100644 index 000000000000..3427d9f494b7 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Producers/GlobalWorldStateBlockProducerEnvFactory.cs @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Autofac; +using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Transactions; +using Nethermind.Consensus.Withdrawals; +using Nethermind.Core; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.State; + +namespace Nethermind.Consensus.Producers +{ + /// + /// Block producer environment factory that uses the global world state and stores receipts by default. + /// Combined with NonProcessingProducedBlockSuggester will add blocks to block tree + /// + /// + /// + /// + public class GlobalWorldStateBlockProducerEnvFactory( + ILifetimeScope rootLifetime, + IWorldStateManager worldStateManager, + IBlockProducerTxSourceFactory txSourceFactory) + : IBlockProducerEnvFactory + { + protected virtual ContainerBuilder ConfigureBuilder(ContainerBuilder builder) => builder + .AddScoped(txSourceFactory.Create()) + .AddScoped(BlockchainProcessor.Options.Default) + .AddScoped() + .AddScoped() + .AddDecorator() + .AddDecorator() + + .AddScoped(); + + public virtual IBlockProducerEnv Create() + { + ILifetimeScope lifetimeScope = rootLifetime.BeginLifetimeScope(builder => + ConfigureBuilder(builder) + .AddScoped(worldStateManager.GlobalWorldState)); + + rootLifetime.Disposer.AddInstanceForAsyncDisposal(lifetimeScope); + return lifetimeScope.Resolve(); + } + } +} diff --git a/src/Nethermind/Nethermind.Consensus/Producers/IProducedBlockSuggester.cs b/src/Nethermind/Nethermind.Consensus/Producers/IProducedBlockSuggester.cs new file mode 100644 index 000000000000..82b2bf7ccf28 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Producers/IProducedBlockSuggester.cs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; + +namespace Nethermind.Consensus.Producers; + +public interface IProducedBlockSuggester : IDisposable +{ + // Just a marker interface to support DI +} diff --git a/src/Nethermind/Nethermind.Consensus/Producers/NonProcessingProducedBlockSuggester.cs b/src/Nethermind/Nethermind.Consensus/Producers/NonProcessingProducedBlockSuggester.cs new file mode 100644 index 000000000000..d1d46263fb17 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Producers/NonProcessingProducedBlockSuggester.cs @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain; +using Nethermind.Core; + +namespace Nethermind.Consensus.Producers; +public class NonProcessingProducedBlockSuggester : IProducedBlockSuggester +{ + private readonly IBlockTree _blockTree; + private readonly IBlockProducerRunner _blockProducerRunner; + + public NonProcessingProducedBlockSuggester(IBlockTree blockTree, IBlockProducerRunner blockProducer) + { + _blockTree = blockTree; + _blockProducerRunner = blockProducer; + _blockProducerRunner.BlockProduced += OnBlockProduced; + } + + private void OnBlockProduced(object? sender, BlockEventArgs e) + { + if (_blockTree.SuggestBlock(e.Block, BlockTreeSuggestOptions.None) == AddBlockResult.Added) + _blockTree.UpdateMainChain([e.Block], true); + } + + public void Dispose() => _blockProducerRunner.BlockProduced -= OnBlockProduced; +} diff --git a/src/Nethermind/Nethermind.Consensus/Producers/ProducedBlockSuggester.cs b/src/Nethermind/Nethermind.Consensus/Producers/ProducedBlockSuggester.cs index a061e7f6e8ce..b74aba7c5714 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/ProducedBlockSuggester.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/ProducedBlockSuggester.cs @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using Nethermind.Blockchain; using Nethermind.Core; namespace Nethermind.Consensus.Producers { - public class ProducedBlockSuggester : IDisposable + public class ProducedBlockSuggester : IProducedBlockSuggester { private readonly IBlockTree _blockTree; private readonly IBlockProducerRunner _blockProducerRunner; diff --git a/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs b/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs index e4f43bab1188..450dabfebc05 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs @@ -234,7 +234,7 @@ private static void ChooseBestBlobTransactions( { int maxCapacity = leftoverCapacity + 1; // The maximum total fee achievable with capacity - using ArrayPoolList dpFeesPooled = new(capacity: maxCapacity, count: maxCapacity); + using ArrayPoolListRef dpFeesPooled = new(maxCapacity, maxCapacity); Span dpFees = dpFeesPooled.AsSpan(); using ArrayPoolBitMap isChosen = new(candidateTxs.Count * maxCapacity); diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 13c0c15bf1e9..95332e5d22aa 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -212,12 +212,20 @@ public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, B error ??= BlockErrorMessages.InvalidRequestsHash(suggestedBlock.Header.RequestsHash, processedBlock.Header.RequestsHash); } - for (int i = 0; i < processedBlock.Transactions.Length; i++) + if (receipts.Length != processedBlock.Transactions.Length) { - if (receipts[i].Error is not null && receipts[i].GasUsed == 0 && receipts[i].Error == "invalid") + if (_logger.IsWarn) _logger.Warn($"- receipt count mismatch: expected {processedBlock.Transactions.Length} receipts to match transaction count, got {receipts.Length}"); + error ??= BlockErrorMessages.ReceiptCountMismatch(processedBlock.Transactions.Length, receipts.Length); + } + else + { + for (int i = 0; i < processedBlock.Transactions.Length; i++) { - if (_logger.IsWarn) _logger.Warn($"- invalid transaction {i}"); - error ??= BlockErrorMessages.InvalidTxInBlock(i); + if (receipts[i].Error is not null && receipts[i].GasUsed == 0 && receipts[i].Error == "invalid") + { + if (_logger.IsWarn) _logger.Warn($"- invalid transaction {i}"); + error ??= BlockErrorMessages.InvalidTxInBlock(i); + } } } diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/RandomExtensions.cs b/src/Nethermind/Nethermind.Core.Test/Builders/RandomExtensions.cs new file mode 100644 index 000000000000..17135ff14fa3 --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Builders/RandomExtensions.cs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using Nethermind.Crypto; + +namespace Nethermind.Core.Test.Builders; + +public static class RandomExtensions +{ + public static T NextFrom(this Random random, IReadOnlyList values) => values[random.Next(values.Count)]; + + public static T NextFrom(this ICryptoRandom random, IReadOnlyList values) => values[random.NextInt(values.Count)]; +} diff --git a/src/Nethermind/Nethermind.Core.Test/Caching/ClockCacheTests.cs b/src/Nethermind/Nethermind.Core.Test/Caching/ClockCacheTests.cs index f4bdf703576e..0dd477bcaeb9 100644 --- a/src/Nethermind/Nethermind.Core.Test/Caching/ClockCacheTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Caching/ClockCacheTests.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -242,6 +242,19 @@ public void Can_delete() cache.Delete(_addresses[0]).Should().BeFalse(); } + [Test] + public void Delete_returns_value() + { + Cache cache = Create(); + cache.Set(_addresses[0], _accounts[0]); + + cache.Delete(_addresses[0], out Account? value).Should().BeTrue(); + value.Should().Be(_accounts[0]); + + cache.Delete(_addresses[0], out Account? noValue).Should().BeFalse(); + noValue.Should().BeNull(); + } + [Test] public void Clear_should_free_all_capacity() { diff --git a/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListRefTests.cs b/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListRefTests.cs new file mode 100644 index 000000000000..b363aaf69339 --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListRefTests.cs @@ -0,0 +1,276 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Nethermind.Core.Collections; +using NUnit.Framework; + +namespace Nethermind.Core.Test.Collections; + +[Parallelizable(ParallelScope.All)] +public class ArrayPoolListRefTests +{ + [Test] + public void Empty_list() + { + using ArrayPoolListRef list = new(1024); + list.AsSpan().ToArray().Should().BeEquivalentTo(Array.Empty()); + list.Count.Should().Be(0); + list.Capacity.Should().Be(1024); + } + + [Test] + public void Should_not_hang_when_capacity_is_zero() + { + using ArrayPoolListRef list = new(0); + list.AsSpan().ToArray().Should().BeEquivalentTo(Array.Empty()); + list.Add(1); + list.Count.Should().Be(1); + list.Remove(1); + list.Count.Should().Be(0); + list.Add(1); + list.Count.Should().Be(1); + } + + [Test] + public void Add_should_work() + { + using ArrayPoolListRef list = new(1024); + list.AddRange(Enumerable.Range(0, 4)); + list.AsSpan().ToArray().Should().BeEquivalentTo(Enumerable.Range(0, 4)); + } + + [Test] + public void Add_should_expand() + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 50)); + list.AsSpan().ToArray().Should().BeEquivalentTo(Enumerable.Range(0, 50)); + list.Count.Should().Be(50); + list.Capacity.Should().Be(64); + } + + [Test] + public void Clear_should_clear() + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 50)); + list.Clear(); + list.AsSpan().ToArray().Should().BeEquivalentTo(Array.Empty()); + list.Count.Should().Be(0); + list.Capacity.Should().Be(64); + } + + [TestCase(0, ExpectedResult = true)] + [TestCase(20, ExpectedResult = true)] + [TestCase(100, ExpectedResult = false)] + [TestCase(-1, ExpectedResult = false)] + public bool Contains_should_check_ok(int item) + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 50)); + return list.Contains(item); + } + + [Test] + public void Can_enumerate() + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 50)); + list.ToArray().Should().BeEquivalentTo(Enumerable.Range(0, 50)); + } + + [TestCase(0, new[] { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 })] + [TestCase(4, new[] { 0, 1, 2, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 })] + [TestCase(16, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1 })] + public void Insert_should_expand(int index, int[] expected) + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 16)); + list.Insert(index, -1); + list.AsSpan().ToArray().Should().BeEquivalentTo(expected); + } + + [TestCase(10)] + [TestCase(-1)] + public void Insert_should_throw(int index) + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 8)); + bool thrown = false; + try + { + list.Insert(index, -1); + } + catch (ArgumentOutOfRangeException) + { + thrown = true; + } + + thrown.Should().BeTrue(); + } + + [TestCase(0, ExpectedResult = 0)] + [TestCase(40, ExpectedResult = 40)] + [TestCase(50, ExpectedResult = -1)] + [TestCase(-1, ExpectedResult = -1)] + public int IndexOf_should_return_index(int item) + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 50)); + return list.IndexOf(item); + } + + + [TestCase(0, true, new[] { 1, 2, 3, 4, 5, 6, 7 })] + [TestCase(7, true, new[] { 0, 1, 2, 3, 4, 5, 6 })] + [TestCase(8, false, new[] { 0, 1, 2, 3, 4, 5, 6, 7 })] + [TestCase(-1, false, new[] { 0, 1, 2, 3, 4, 5, 6, 7 })] + public void Remove_should_remove(int item, bool removed, int[] expected) + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 8)); + list.Remove(item).Should().Be(removed); + list.AsSpan().ToArray().Should().BeEquivalentTo(expected); + } + + [TestCase(0, new[] { 1, 2, 3, 4, 5, 6, 7 })] + [TestCase(7, new[] { 0, 1, 2, 3, 4, 5, 6 })] + public void RemoveAt_should_remove(int item, int[] expected) + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 8)); + list.RemoveAt(item); + list.AsSpan().ToArray().Should().BeEquivalentTo(expected); + } + + [TestCase(8, new[] { 0, 1, 2, 3, 4, 5, 6, 7 })] + [TestCase(-1, new[] { 0, 1, 2, 3, 4, 5, 6, 7 })] + public void RemoveAt_should_throw(int item, int[] expected) + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 8)); + bool thrown = false; + try + { + list.RemoveAt(item); + } + catch (ArgumentOutOfRangeException) + { + thrown = true; + } + + thrown.Should().BeTrue(); + } + + [Test] + public void CopyTo_should_copy() + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 50)); + int[] array = new int[51]; + list.CopyTo(array, 1); + array.Should().BeEquivalentTo(Enumerable.Range(0, 1).Concat(Enumerable.Range(0, 50))); + } + + [TestCase(0, ExpectedResult = 0)] + [TestCase(7, ExpectedResult = 7)] + public int Get_should_return(int item) + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 8)); + return list[item]; + } + + [TestCase(8)] + [TestCase(-1)] + public void Get_should_throw(int item) + { + using ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 8)); + bool thrown = false; + try + { + int _ = list[item]; + } + catch (ArgumentOutOfRangeException) + { + thrown = true; + } + + thrown.Should().BeTrue(); + } + + [TestCase(0, ExpectedResult = -1)] + [TestCase(7, ExpectedResult = -1)] + public int Set_should_set(int item) + { + ArrayPoolListRef list = new(4); + try + { + list.AddRange(Enumerable.Range(0, 8)); + list[item] = -1; + return list[item]; + } + finally + { + list.Dispose(); + } + } + + [TestCase(8)] + [TestCase(-1)] + public void Set_should_throw(int item) + { + ArrayPoolListRef list = new(4); + list.AddRange(Enumerable.Range(0, 8)); + bool thrown = false; + try + { + list[item] = 1; + } + catch (ArgumentOutOfRangeException) + { + thrown = true; + } + finally + { + list.Dispose(); + } + + thrown.Should().BeTrue(); + } + + [TestCase(1, 16)] + [TestCase(14, 16)] + [TestCase(15, 32)] + [TestCase(20, 32)] + [TestCase(100, 128)] + public void AddRange_should_expand(int items, int expectedCapacity) + { + using ArrayPoolListRef list = new(16, 0, 1); + list.AddRange(Enumerable.Range(2, items)); + list.AsSpan().ToArray().Should().BeEquivalentTo(Enumerable.Range(0, items + 2)); + list.Capacity.Should().Be(expectedCapacity); + } + + [Test] + public void Dispose_ShouldNotHaveAnEffect_OnEmptyPool() + { + var list = new ArrayPoolListRef(0); + list.Dispose(); + int _ = list.Count; + } + + [Test] + public void Can_resize_totally_empty_list() + { + using ArrayPoolListRef list = new(0); + list.Add(1); + list.Count.Should().Be(1); + } +} diff --git a/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListTests.cs b/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListTests.cs index 26abdb0f47f6..296a58c98939 100644 --- a/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListTests.cs @@ -76,6 +76,14 @@ public bool Contains_should_check_ok(int item) return list.Contains(item); } + [Test] + public void Can_enumerate() + { + using ArrayPoolList list = new(4); + list.AddRange(Enumerable.Range(0, 50)); + list.ToArray().Should().BeEquivalentTo(Enumerable.Range(0, 50)); + } + [TestCase(0, new[] { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 })] [TestCase(4, new[] { 0, 1, 2, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 })] [TestCase(16, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1 })] diff --git a/src/Nethermind/Nethermind.Core.Test/Json/ByteArrayConverterTests.cs b/src/Nethermind/Nethermind.Core.Test/Json/ByteArrayConverterTests.cs index d1413676fa33..5888c30c65e1 100644 --- a/src/Nethermind/Nethermind.Core.Test/Json/ByteArrayConverterTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Json/ByteArrayConverterTests.cs @@ -8,6 +8,8 @@ using System.Text.Json; using FluentAssertions; using Nethermind.Core.Extensions; +using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; using Nethermind.Serialization.Json; using NUnit.Framework; @@ -294,6 +296,22 @@ public void Fuzz_RandomHex_SegmentationInvariant() } } + [Test] + public void Test_DictionaryKey() + { + var random = new CryptoRandom(); + var dictionary = new Dictionary + { + { Bytes.FromHexString("0x0"), null }, + { Bytes.FromHexString("0x1"), random.NextInt(int.MaxValue) }, + { Build.An.Address.TestObject.Bytes, random.NextInt(int.MaxValue) }, + { random.GenerateRandomBytes(10), random.NextInt(int.MaxValue) }, + { random.GenerateRandomBytes(32), random.NextInt(int.MaxValue) }, + }; + + TestConverter(dictionary, new ByteArrayConverter()); + } + private static ReadOnlySequence JsonForLiteral(string literal) => MakeSequence(Encoding.UTF8.GetBytes(literal)); diff --git a/src/Nethermind/Nethermind.Core.Test/Json/ConverterTestBase.cs b/src/Nethermind/Nethermind.Core.Test/Json/ConverterTestBase.cs index 7f38126cda19..8725b3750dba 100644 --- a/src/Nethermind/Nethermind.Core.Test/Json/ConverterTestBase.cs +++ b/src/Nethermind/Nethermind.Core.Test/Json/ConverterTestBase.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections; using System.Text.Json; using System.Text.Json.Serialization; using NUnit.Framework; @@ -10,7 +11,10 @@ namespace Nethermind.Core.Test.Json; public class ConverterTestBase { - protected void TestConverter(T? item, Func equalityComparer, JsonConverter converter) + protected void TestConverter(T? item, Func equalityComparer, JsonConverter converter) => + TestConverter(item, converter, equalityComparer); + + protected void TestConverter(TItem? item, JsonConverter converter, Func? equalityComparer = null) { var options = new JsonSerializerOptions { @@ -22,10 +26,15 @@ protected void TestConverter(T? item, Func equalityComparer, JsonCon string result = JsonSerializer.Serialize(item, options); - T? deserialized = JsonSerializer.Deserialize(result, options); + TItem? deserialized = JsonSerializer.Deserialize(result, options); #pragma warning disable CS8604 - Assert.That(equalityComparer(item, deserialized), Is.True); + if (equalityComparer is not null) + Assert.That(equalityComparer(item, deserialized), Is.True); + else if (item is IEnumerable itemE && deserialized is IEnumerable deserializedE) + Assert.That(deserializedE, Is.EquivalentTo(itemE)); + else + Assert.That(deserialized, Is.EqualTo(item)); #pragma warning restore CS8604 } } diff --git a/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNetworkModule.cs b/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNetworkModule.cs index f8dc845cba1a..03273a33b501 100644 --- a/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNetworkModule.cs +++ b/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNetworkModule.cs @@ -9,7 +9,6 @@ using Nethermind.Network; using Nethermind.Network.Config; using Nethermind.Network.Contract.P2P; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.State; using Nethermind.State.SnapServer; using Nethermind.Stats.Model; @@ -31,7 +30,6 @@ protected override void Load(ContainerBuilder builder) .AddSingleton(NoPoS.Instance) .AddSingleton() - .AddSingleton() .AddSingleton(Policy.FullGossip) .AddComposite() diff --git a/src/Nethermind/Nethermind.Core.Test/TestHardwareInfo.cs b/src/Nethermind/Nethermind.Core.Test/TestHardwareInfo.cs index 59e15d1f33a3..63947aa97c0e 100644 --- a/src/Nethermind/Nethermind.Core.Test/TestHardwareInfo.cs +++ b/src/Nethermind/Nethermind.Core.Test/TestHardwareInfo.cs @@ -3,7 +3,8 @@ namespace Nethermind.Core.Test; -public class TestHardwareInfo(long availableMemory = 10000000) : IHardwareInfo +public class TestHardwareInfo(long availableMemory = 10000000, int? maxOpenFilesLimit = null) : IHardwareInfo { public long AvailableMemoryBytes => availableMemory; + public int? MaxOpenFilesLimit => maxOpenFilesLimit; } diff --git a/src/Nethermind/Nethermind.Core/BlockHeader.cs b/src/Nethermind/Nethermind.Core/BlockHeader.cs index 3183f88d2d3b..92bcc5d1eabe 100644 --- a/src/Nethermind/Nethermind.Core/BlockHeader.cs +++ b/src/Nethermind/Nethermind.Core/BlockHeader.cs @@ -44,7 +44,8 @@ public BlockHeader( ExcessBlobGas = excessBlobGas; } - public bool IsGenesis => Number == 0L; + public virtual long GenesisBlockNumber => 0; + public bool IsGenesis => Number == GenesisBlockNumber; public Hash256? ParentHash { get; set; } public Hash256? UnclesHash { get; set; } public Address? Author { get; set; } @@ -80,7 +81,6 @@ public BlockHeader( public bool HasTransactions => (TxRoot is not null && TxRoot != Keccak.EmptyTreeHash); - public string SealEngineType { get; set; } = Core.SealEngineType.Ethash; public bool IsPostMerge { get; set; } public string ToString(string indent) diff --git a/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs b/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs index a13c6eaef1fe..9a5148c57653 100644 --- a/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs +++ b/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -58,8 +58,18 @@ public bool Set(TKey key, TValue val) if (_cacheMap.TryGetValue(key, out LruCacheItem ov)) { - // Fast path: atomic update using TryUpdate - if (_cacheMap.TryUpdate(key, new(val, ov.Offset), comparisonValue: ov)) + bool needsUpdate; + if (typeof(TValue).IsValueType) + { + needsUpdate = !EqualityComparer.Default.Equals(ov.Value, val); + } + else + { + needsUpdate = !ReferenceEquals(ov.Value, val); + } + + // Fast path: no update or atomic update using TryUpdate + if (!needsUpdate || _cacheMap.TryUpdate(key, new(val, ov.Offset), comparisonValue: ov)) { MarkAccessed(ov.Offset); return false; @@ -136,9 +146,15 @@ void ThrowInvalidOperationException() } } - public bool Delete(TKey key) + public bool Delete(TKey key) => Delete(key, out _); + + public bool Delete(TKey key, [NotNullWhen(true)] out TValue? value) { - if (MaxCapacity == 0) return false; + if (MaxCapacity == 0) + { + value = default; + return false; + } using var lockRelease = _lock.Acquire(); @@ -148,9 +164,11 @@ public bool Delete(TKey key) KeyToOffset[ov.Offset] = default; ClearAccessed(ov.Offset); FreeOffsets.Enqueue(ov.Offset); - return true; + value = ov.Value; + return ov.Value != null; } + value = default; return false; } diff --git a/src/Nethermind/Nethermind.Core/Collections/ArrayListCore.cs b/src/Nethermind/Nethermind.Core/Collections/ArrayListCore.cs new file mode 100644 index 000000000000..5a017db38d0d --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Collections/ArrayListCore.cs @@ -0,0 +1,280 @@ +using System; +using System.Buffers; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Nethermind.Core.Collections; + +internal static class ArrayPoolListCore +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GuardResize( + ArrayPool pool, + ref T[] array, + ref int capacity, + int count, + int itemsToAdd = 1) + { + int newCount = count + itemsToAdd; + + if (capacity == 0) + { + array = pool.Rent(newCount); + capacity = array.Length; + } + else if (newCount > capacity) + { + int newCapacity = capacity * 2; + if (newCapacity == 0) newCapacity = 1; + while (newCount > newCapacity) + { + newCapacity *= 2; + } + + T[] newArray = pool.Rent(newCapacity); + Array.Copy(array, 0, newArray, 0, count); + T[] oldArray = Interlocked.Exchange(ref array, newArray); + capacity = newArray.Length; + ClearToCount(oldArray, count); + pool.Return(oldArray); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ClearToCount(T[] array, int count) + { + if (count > 0 && RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Array.Clear(array, 0, count); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add( + ArrayPool pool, + ref T[] array, + ref int capacity, + ref int count, + T item) + { + GuardResize(pool, ref array, ref capacity, count); + array[count++] = item; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void AddRange( + ArrayPool pool, + ref T[] array, + ref int capacity, + ref int count, + ReadOnlySpan items) + { + GuardResize(pool, ref array, ref capacity, count, items.Length); + items.CopyTo(array.AsSpan(count, items.Length)); + count += items.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(T[] array, ref int count) + { + ClearToCount(array, count); + count = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReduceCount( + ArrayPool pool, + ref T[] array, + ref int capacity, + ref int count, + int newCount) + { + int oldCount = count; + if (newCount == oldCount) return; + + if (newCount > oldCount) + ThrowOnlyReduce(newCount, oldCount); + + count = newCount; + + if (newCount < capacity / 2) + { + T[] newArray = pool.Rent(newCount); + array.AsSpan(0, newCount).CopyTo(newArray); + T[] oldArray = Interlocked.Exchange(ref array, newArray); + capacity = newArray.Length; + ClearToCount(oldArray, oldCount); + pool.Return(oldArray); + } + else if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Array.Clear(array, newCount, oldCount - newCount); + } + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowOnlyReduce(int newCount, int oldCount) + { + throw new ArgumentException($"Count can only be reduced. {newCount} is larger than {oldCount}", + nameof(count)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Sort(T[] array, int count, Comparison comparison) + { + ArgumentNullException.ThrowIfNull(comparison); + if (count > 1) array.AsSpan(0, count).Sort(comparison); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Reverse(T[] array, int count) + { + array.AsSpan(0, count).Reverse(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Contains(T[] array, T item, int count) => IndexOf(array, count, item) >= 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(T[] array, int count, T item) + { + int indexOf = Array.IndexOf(array, item); + return indexOf < count ? indexOf : -1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(T[] array, int count, T[] destination, int index) + { + array.AsMemory(0, count).CopyTo(destination.AsMemory(index)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool GuardIndex(int index, int count, bool shouldThrow = true, bool allowEqualToCount = false) + { + if ((uint)index > (uint)count || (!allowEqualToCount && index == count)) + { + if (shouldThrow) ThrowArgumentOutOfRangeException(); + return false; + } + + return true; + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowArgumentOutOfRangeException() + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool RemoveAt(T[] array, ref int count, int index, bool shouldThrow) + { + bool isValid = GuardIndex(index, count, shouldThrow); + if (isValid) + { + int start = index + 1; + if (start < count) + { + array.AsMemory(start, count - index).CopyTo(array.AsMemory(index)); + } + + count--; + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + array[count] = default!; + } + } + + return isValid; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(T[] array, ref int count, T item) + { + return RemoveAt(array, ref count, IndexOf(array, count, item), false); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Insert( + ArrayPool pool, + ref T[] array, + ref int capacity, + ref int count, + int index, + T item) + { + GuardResize(pool, ref array, ref capacity, count); + GuardIndex(index, count, shouldThrow: true, allowEqualToCount: true); + array.AsMemory(index, count - index).CopyTo(array.AsMemory(index + 1)); + array[index] = item; + count++; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Truncate(int newLength, T[] array, ref int count) + { + GuardIndex(newLength, count, shouldThrow: true, allowEqualToCount: true); + count = newLength; + } + + // Expose Get/Set and GetRef consistent with the original + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T GetRef(T[] array, int index, int count) + { + GuardIndex(index, count); + return ref array[index]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Get(T[] array, int index, int count) + { + GuardIndex(index, count); + return array[index]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Set(T[] array, int index, int count, T value) + { + GuardIndex(index, count); + array[index] = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dispose( + ArrayPool pool, + ref T[] array, + ref int count, + ref int capacity) + { + T[]? localArray = array; + array = null!; + + if (localArray is not null) + { + ClearToCount(localArray, count); + pool.Return(localArray); + } + + count = 0; + capacity = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dispose( + ArrayPool pool, + ref T[] array, + ref int count, + ref int capacity, + ref bool disposed) + { + if (!disposed && capacity != 0) + { + disposed = true; + Dispose(pool, ref array, ref count, ref capacity); + } + } +} diff --git a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs index 440bb3554c2c..a5969cb72d3e 100644 --- a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs +++ b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs @@ -18,6 +18,7 @@ public sealed class ArrayPoolList : IList, IList, IOwnedReadOnlyList private T[] _array; private int _capacity; private bool _disposed; + private int _count = 0; public ArrayPoolList(int capacity) : this(ArrayPool.Shared, capacity) { } @@ -42,18 +43,17 @@ public ArrayPoolList(ArrayPool arrayPool, int capacity, int startingCount = 0 } _capacity = _array.Length; - Count = startingCount; + _count = startingCount; } - ReadOnlySpan IOwnedReadOnlyList.AsSpan() - { - return AsSpan(); - } + public int Count => _count; + + ReadOnlySpan IOwnedReadOnlyList.AsSpan() => AsSpan(); public PooledArrayEnumerator GetEnumerator() { GuardDispose(); - return new PooledArrayEnumerator(_array, Count); + return new PooledArrayEnumerator(_array, _count); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -65,6 +65,7 @@ private void GuardDispose() } [DoesNotReturn] + [StackTraceHidden] static void ThrowObjectDisposed() { throw new ObjectDisposedException(nameof(ArrayPoolList)); @@ -77,8 +78,8 @@ static void ThrowObjectDisposed() public void Add(T item) { - GuardResize(); - _array[Count++] = item; + GuardDispose(); + ArrayPoolListCore.Add(_arrayPool, ref _array, ref _capacity, ref _count, item); } int IList.Add(object? value) @@ -87,27 +88,25 @@ int IList.Add(object? value) Add((T)value!); - return Count - 1; + return _count - 1; } - public void AddRange(ReadOnlySpan items) + public void AddRange(params ReadOnlySpan items) { - GuardResize(items.Length); - items.CopyTo(_array.AsSpan(Count, items.Length)); - Count += items.Length; + GuardDispose(); + ArrayPoolListCore.AddRange(_arrayPool, ref _array, ref _capacity, ref _count, items); } public void Clear() { - ClearToCount(_array); - Count = 0; + GuardDispose(); + ArrayPoolListCore.Clear(_array, ref _count); } public bool Contains(T item) { GuardDispose(); - int indexOf = Array.IndexOf(_array, item); - return indexOf >= 0 && indexOf < Count; + return ArrayPoolListCore.Contains(_array, item, _count); } bool IList.Contains(object? value) => IsCompatibleObject(value) && Contains((T)value!); @@ -115,73 +114,36 @@ public bool Contains(T item) public void CopyTo(T[] array, int arrayIndex) { GuardDispose(); - _array.AsMemory(0, Count).CopyTo(array.AsMemory(arrayIndex)); + ArrayPoolListCore.CopyTo(_array, _count, array, arrayIndex); } - void ICollection.CopyTo(Array array, int index) + void ICollection.CopyTo(Array? array, int index) { - if ((array is not null) && (array.Rank != 1)) - throw new ArgumentException("Only single dimensional arrays are supported.", nameof(array)); - - GuardDispose(); + if (array is not null && array.Rank != 1) + ThrowMultiDimensionalArray(); - Array.Copy(_array, 0, array!, index, Count); - } - - public int Count { get; private set; } = 0; - public void ReduceCount(int count) - { GuardDispose(); - var oldCount = Count; - if (count == oldCount) return; - if (count > oldCount) - { - ThrowOnlyReduce(count); - } - - Count = count; - if (count < _capacity / 2) - { - // Reduced to less than half of the capacity, resize the array. - T[] newArray = _arrayPool.Rent(count); - _array.AsSpan(0, count).CopyTo(newArray); - T[] oldArray = Interlocked.Exchange(ref _array, newArray); - _capacity = newArray.Length; - ClearToCount(oldArray); - _arrayPool.Return(oldArray); - } - else if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Release any references to the objects in the array that are no longer in use. - Array.Clear(_array, count, oldCount - count); - } + Array.Copy(_array, 0, array!, index, _count); - void ThrowOnlyReduce(int count) + [DoesNotReturn] + [StackTraceHidden] + static void ThrowMultiDimensionalArray() { - throw new ArgumentException($"Count can only be reduced. {count} is larger than {Count}", nameof(count)); + throw new ArgumentException("Only single dimensional arrays are supported.", nameof(array)); } } - private void ClearToCount(T[] array) + public void ReduceCount(int count) { - int count = Count; - // Release any references to the objects in the array so can be GC'd. - if (count > 0 && RuntimeHelpers.IsReferenceOrContainsReferences()) - { - Array.Clear(array, 0, count); - } + GuardDispose(); + ArrayPoolListCore.ReduceCount(_arrayPool, ref _array, ref _capacity, ref _count, count); } public void Sort(Comparison comparison) { - ArgumentNullException.ThrowIfNull(comparison); GuardDispose(); - - if (Count > 1) - { - _array.AsSpan(0, Count).Sort(comparison); - } + ArrayPoolListCore.Sort(_array, _count, comparison); } public int Capacity => _capacity; @@ -199,19 +161,15 @@ public void Sort(Comparison comparison) public int IndexOf(T item) { GuardDispose(); - int indexOf = Array.IndexOf(_array, item); - return indexOf < Count ? indexOf : -1; + return ArrayPoolListCore.IndexOf(_array, _count, item); } int IList.IndexOf(object? value) => IsCompatibleObject(value) ? IndexOf((T)value!) : -1; public void Insert(int index, T item) { - GuardResize(); - GuardIndex(index, allowEqualToCount: true); - _array.AsMemory(index, Count - index).CopyTo(_array.AsMemory(index + 1)); - _array[index] = item; - Count++; + GuardDispose(); + ArrayPoolListCore.Insert(_arrayPool, ref _array, ref _capacity, ref _count, index, item); } void IList.Insert(int index, object? value) @@ -221,87 +179,47 @@ void IList.Insert(int index, object? value) Insert(index, (T)value!); } - private void GuardResize(int itemsToAdd = 1) + public bool Remove(T item) { GuardDispose(); - int newCount = Count + itemsToAdd; - if (_capacity == 0) - { - _array = _arrayPool.Rent(newCount); - _capacity = _array.Length; - } - else if (newCount > _capacity) - { - int newCapacity = _capacity * 2; - if (newCapacity == 0) newCapacity = 1; - while (newCount > newCapacity) - { - newCapacity *= 2; - } - T[] newArray = _arrayPool.Rent(newCapacity); - _array.CopyTo(newArray, 0); - T[] oldArray = Interlocked.Exchange(ref _array, newArray); - _capacity = newArray.Length; - ClearToCount(oldArray); - _arrayPool.Return(oldArray); - } + return ArrayPoolListCore.Remove(_array, ref _count, item); } - public bool Remove(T item) => RemoveAtInternal(IndexOf(item), false); - void IList.Remove(object? value) { if (IsCompatibleObject(value)) Remove((T)value!); } - public void RemoveAt(int index) => RemoveAtInternal(index, true); - - private bool RemoveAtInternal(int index, bool shouldThrow) + public void RemoveAt(int index) { - bool isValid = GuardIndex(index, shouldThrow); - if (isValid) - { - int start = index + 1; - if (start < Count) - { - _array.AsMemory(start, Count - index).CopyTo(_array.AsMemory(index)); - } - - Count--; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - _array[Count] = default!; - } - } - - return isValid; + GuardDispose(); + ArrayPoolListCore.RemoveAt(_array, ref _count, index, true); } public void Truncate(int newLength) { GuardDispose(); - GuardIndex(newLength, allowEqualToCount: true); - Count = newLength; + ArrayPoolListCore.Truncate(newLength, _array, ref _count); } public ref T GetRef(int index) { - GuardIndex(index); - return ref _array[index]; + GuardDispose(); + return ref ArrayPoolListCore.GetRef(_array, index, _count); } public T this[int index] { get { - GuardIndex(index); - return _array[index]; + GuardDispose(); + return ArrayPoolListCore.Get(_array, index, _count); } set { - GuardIndex(index); - _array[index] = value; + GuardDispose(); + ArrayPoolListCore.Set(_array, index, _count, value); } } @@ -311,59 +229,21 @@ public T this[int index] set { ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, nameof(value)); - this[index] = (T)value!; } } - private bool GuardIndex(int index, bool shouldThrow = true, bool allowEqualToCount = false) - { - GuardDispose(); - int count = Count; - if ((uint)index > (uint)count || (!allowEqualToCount && index == count)) - { - if (shouldThrow) - { - ThrowArgumentOutOfRangeException(); - } - return false; - } - - return true; - - [DoesNotReturn] - static void ThrowArgumentOutOfRangeException() - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - } private static bool IsCompatibleObject(object? value) => value is T || value is null && default(T) is null; public static ArrayPoolList Empty() => new(0); - - public void Dispose() { - // Noop for empty array as sometimes this is used as part of an empty shared response. - if (_capacity == 0) return; - - if (!_disposed) - { - _disposed = true; - T[]? array = _array; - _array = null!; - if (array is not null) - { - ClearToCount(array); - _arrayPool.Return(array); - } - Count = 0; - } + ArrayPoolListCore.Dispose(_arrayPool, ref _array, ref _count, ref _capacity, ref _disposed); #if DEBUG - GC.SuppressFinalize(this); + GC.SuppressFinalize(this); #endif } @@ -379,9 +259,29 @@ public void Dispose() } #endif - public Span AsSpan() => _array.AsSpan(0, Count); - public Memory AsMemory() => new(_array, 0, Count); - public ReadOnlyMemory AsReadOnlyMemory() => new(_array, 0, Count); - public T[] UnsafeGetInternalArray() => _array; - public void Reverse() => AsSpan().Reverse(); + public Span AsSpan() + { + GuardDispose(); + return _array.AsSpan(0, _count); + } + public Memory AsMemory() + { + GuardDispose(); + return new(_array, 0, _count); + } + public ReadOnlyMemory AsReadOnlyMemory() + { + GuardDispose(); + return new(_array, 0, _count); + } + public T[] UnsafeGetInternalArray() + { + GuardDispose(); + return _array; + } + public void Reverse() + { + GuardDispose(); + ArrayPoolListCore.Reverse(_array, _count); + } } diff --git a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolListRef.cs b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolListRef.cs new file mode 100644 index 000000000000..7a2d9ad9349b --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolListRef.cs @@ -0,0 +1,98 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nethermind.Core.Collections; + +public ref struct ArrayPoolListRef +{ + private T[] _array; + private int _capacity; + private int _count; + + public ArrayPoolListRef(int capacity, IEnumerable items) : this(capacity) => AddRange(items); + public ArrayPoolListRef(int capacity, params ReadOnlySpan items) : this(capacity) => AddRange(items); + public ArrayPoolListRef(ReadOnlySpan span) : this(span.Length) => AddRange(span); + + public ArrayPoolListRef(int capacity, int startingCount = 0) + { + if (capacity != 0) + { + _array = ArrayPool.Shared.Rent(capacity); + _array.AsSpan(0, startingCount).Clear(); + } + else + { + _array = []; + } + + _capacity = _array.Length; + _count = startingCount; + } + + public readonly int Count => _count; + public readonly int Capacity => _capacity; + public void Add(T item) => ArrayPoolListCore.Add(ArrayPool.Shared, ref _array, ref _capacity, ref _count, item); + public void AddRange(params T[] items) => AddRange(items.AsSpan()); + public void AddRange(params ReadOnlySpan items) => ArrayPoolListCore.AddRange(ArrayPool.Shared, ref _array, ref _capacity, ref _count, items); + + public void AddRange(params IEnumerable items) + { + switch (items) + { + case T[] array: + AddRange((ReadOnlySpan)array); + break; + case List listItems: + AddRange(CollectionsMarshal.AsSpan(listItems)); + break; + default: + { + foreach (T item in items) + { + Add(item); + } + + break; + } + } + } + + public void Insert(int index, T item) => ArrayPoolListCore.Insert(ArrayPool.Shared, ref _array, ref _capacity, ref _count, index, item); + public bool Remove(T item) => ArrayPoolListCore.Remove(_array, ref _count, item); + public void RemoveAt(int index) => ArrayPoolListCore.RemoveAt(_array, ref _count, index, shouldThrow: true); + public void Clear() => ArrayPoolListCore.Clear(_array, ref _count); + public void ReduceCount(int newCount) => ArrayPoolListCore.ReduceCount(ArrayPool.Shared, ref _array, ref _capacity, ref _count, newCount); + public void Truncate(int newLength) => ArrayPoolListCore.Truncate(newLength, _array, ref _count); + public readonly void Sort(Comparison comparison) => ArrayPoolListCore.Sort(_array, _count, comparison); + public readonly void Reverse() => ArrayPoolListCore.Reverse(_array, _count); + public readonly ref T GetRef(int index) => ref ArrayPoolListCore.GetRef(_array, index, _count); + public readonly Span AsSpan() => _array.AsSpan(0, _count); + public readonly Memory AsMemory() => new(_array, 0, _count); + public readonly ReadOnlyMemory AsReadOnlyMemory() => new(_array, 0, _count); + + public readonly T this[int index] + { + get => ArrayPoolListCore.Get(_array, index, _count); + set => ArrayPoolListCore.Set(_array, index, _count, value); + } + + public void Dispose() => ArrayPoolListCore.Dispose(ArrayPool.Shared, ref _array, ref _count, ref _capacity); + public readonly PooledArrayEnumerator GetEnumerator() => new(_array, _count); + public readonly bool Contains(T item) => ArrayPoolListCore.Contains(_array, item, _count); + public readonly int IndexOf(T item) => ArrayPoolListCore.IndexOf(_array, _count, item); + public readonly void CopyTo(T[] array, int arrayIndex) => ArrayPoolListCore.CopyTo(_array, _count, array, arrayIndex); + + public readonly ArrayPoolListRef Select(Func selector) + { + ArrayPoolListRef result = new(_count); + foreach (T item in AsSpan()) result.Add(selector(item)); + return result; + } + + public readonly T[] ToArray() => AsSpan().ToArray(); + public readonly T[] UnsafeGetInternalArray() => _array; +} diff --git a/src/Nethermind/Nethermind.Core/Collections/CollectionExtensions.cs b/src/Nethermind/Nethermind.Core/Collections/CollectionExtensions.cs index 4a5f5f476a39..bf689b2519fd 100644 --- a/src/Nethermind/Nethermind.Core/Collections/CollectionExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Collections/CollectionExtensions.cs @@ -16,24 +16,26 @@ public static class CollectionExtensions public static void AddRange(this ICollection list, IEnumerable items) { - if (items is T[] array) + switch (items) { - list.AddRange(array); - } - else if (items is IList listItems) - { - list.AddRange(listItems); - } - else if (items is IReadOnlyList readOnlyList) - { - list.AddRange(readOnlyList); - } - else - { - foreach (T item in items) - { - list.Add(item); - } + case T[] array: + list.AddRange(array); + break; + case IList listItems: + list.AddRange(listItems); + break; + case IReadOnlyList readOnlyList: + list.AddRange(readOnlyList); + break; + default: + { + foreach (T item in items) + { + list.Add(item); + } + + break; + } } } diff --git a/src/Nethermind/Nethermind.Core/Collections/DictionaryExtensions.cs b/src/Nethermind/Nethermind.Core/Collections/DictionaryExtensions.cs index e036bc568d54..ddb32f7f8bf4 100644 --- a/src/Nethermind/Nethermind.Core/Collections/DictionaryExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Collections/DictionaryExtensions.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -13,4 +14,22 @@ public static void Increment(this Dictionary dictionary, TKey k ref int res = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out bool _); res++; } + + public static TValue GetOrAdd(this Dictionary dictionary, + TKey key, Func factory, + out bool exists) + where TKey : notnull + { + ref TValue? existing = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out exists); + + if (!exists) + existing = factory(key); + + return existing!; + } + + public static TValue GetOrAdd(this Dictionary dictionary, + TKey key, Func factory) + where TKey : notnull => + GetOrAdd(dictionary, key, factory, out _); } diff --git a/src/Nethermind/Nethermind.Core/Crypto/Hash256.cs b/src/Nethermind/Nethermind.Core/Crypto/Hash256.cs index 6a1314f80e2f..892e20e6c102 100644 --- a/src/Nethermind/Nethermind.Core/Crypto/Hash256.cs +++ b/src/Nethermind/Nethermind.Core/Crypto/Hash256.cs @@ -16,7 +16,6 @@ namespace Nethermind.Core.Crypto { [DebuggerStepThrough] [DebuggerDisplay("{ToString()}")] - [JsonConverter(typeof(ValueHash256Converter))] public readonly struct ValueHash256 : IEquatable, IComparable, IEquatable { // Ensure that hashes are different for every run of the node and every node, so if are any hash collisions on @@ -117,7 +116,6 @@ public readonly struct Hash256AsKey(Hash256 key) : IEquatable, ICo public int CompareTo(Hash256AsKey other) => _key.CompareTo(other._key); } - [JsonConverter(typeof(Hash256Converter))] [DebuggerStepThrough] public sealed class Hash256 : IEquatable, IComparable { diff --git a/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequestExtensions.cs b/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequestExtensions.cs index 7b7350b8a04a..738ae7c7ebf8 100644 --- a/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequestExtensions.cs +++ b/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequestExtensions.cs @@ -38,7 +38,7 @@ public static Hash256 CalculateHashFromFlatEncodedRequests(byte[][]? flatEncoded throw new ArgumentException("Flat encoded requests must be an array"); } - using ArrayPoolList concatenatedHashes = new(Hash256.Size * MaxRequestsCount); + using ArrayPoolListRef concatenatedHashes = new(Hash256.Size * MaxRequestsCount); foreach (byte[] requests in flatEncodedRequests) { if (requests.Length <= 1) continue; @@ -57,7 +57,7 @@ public static ArrayPoolList GetFlatEncodedRequests( ExecutionRequest[] consolidationRequests ) { - var result = new ArrayPoolList(MaxRequestsCount); + ArrayPoolList result = new(MaxRequestsCount); if (depositRequests.Length > 0) { @@ -78,7 +78,7 @@ ExecutionRequest[] consolidationRequests static byte[] FlatEncodeRequests(ExecutionRequest[] requests, int bufferSize, byte type) { - using ArrayPoolList buffer = new(bufferSize + 1) { type }; + using ArrayPoolListRef buffer = new(bufferSize + 1, type); foreach (ExecutionRequest request in requests) { diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index 128268d8243b..e6a5f23545bf 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -262,7 +262,7 @@ public static byte[] PadLeft(this ReadOnlySpan bytes, int length, byte pad return result; } - public static byte[] PadRight(this byte[] bytes, int length) + public static byte[] PadRight(this byte[] bytes, int length, byte padding = 0) { if (bytes.Length == length) { @@ -276,6 +276,12 @@ public static byte[] PadRight(this byte[] bytes, int length) byte[] result = new byte[length]; Buffer.BlockCopy(bytes, 0, result, 0, bytes.Length); + + if (padding != 0) + { + result.AsSpan(bytes.Length, length - bytes.Length).Fill(padding); + } + return result; } diff --git a/src/Nethermind/Nethermind.Core/Extensions/EnumerableExtensions.cs b/src/Nethermind/Nethermind.Core/Extensions/EnumerableExtensions.cs index 4686955c4cda..0a0cf4faf049 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/EnumerableExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/EnumerableExtensions.cs @@ -16,6 +16,9 @@ public static ISet AsSet(this IEnumerable enumerable) => public static ArrayPoolList ToPooledList(this IEnumerable enumerable, int count) => new(count, enumerable); public static ArrayPoolList ToPooledList(this IReadOnlyCollection collection) => new(collection.Count, collection); public static ArrayPoolList ToPooledList(this ReadOnlySpan span) => new(span); + public static ArrayPoolListRef ToPooledListRef(this IEnumerable enumerable, int count) => new(count, enumerable); + public static ArrayPoolListRef ToPooledListRef(this IReadOnlyCollection collection) => new(collection.Count, collection); + public static ArrayPoolListRef ToPooledListRef(this ReadOnlySpan span) => new(span); public static IEnumerable Shuffle(this IEnumerable enumerable, Random rng, int maxSize = 100) { diff --git a/src/Nethermind/Nethermind.Core/Extensions/SizeExtensions.cs b/src/Nethermind/Nethermind.Core/Extensions/SizeExtensions.cs index ba773225fb46..2ae4af43e7e8 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/SizeExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/SizeExtensions.cs @@ -67,7 +67,7 @@ public static long KiB(this int @this) return ((long)@this).KiB(); } - public static string SizeToString(this long @this, bool useSi = false, int precision = 1) + public static string SizeToString(this long @this, bool useSi = false, bool addSpace = false, int precision = 1) { string[] suf = useSi ? ["B", "KB", "MB", "GB", "TB"] : ["B", "KiB", "MiB", "GiB", "TiB"]; if (@this == 0) @@ -77,7 +77,7 @@ public static string SizeToString(this long @this, bool useSi = false, int preci long bytes = Math.Abs(@this); int place = Math.Min(suf.Length - 1, Convert.ToInt32(Math.Floor(Math.Log(bytes, useSi ? 1000 : 1024)))); double num = Math.Round(bytes / Math.Pow(useSi ? 1000 : 1024, place), precision); - return (Math.Sign(@this) * num).ToString() + suf[place]; + return string.Concat(Math.Sign(@this) * num, addSpace ? " " : "", suf[place]); } } } diff --git a/src/Nethermind/Nethermind.Core/Extensions/SpanExtensions.cs b/src/Nethermind/Nethermind.Core/Extensions/SpanExtensions.cs index 51ba5ff778a9..14ac04fe0fb9 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/SpanExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/SpanExtensions.cs @@ -185,7 +185,14 @@ public static Span TakeAndMove(this ref Span span, int length) public static ArrayPoolList ToPooledList(this in ReadOnlySpan span) { - ArrayPoolList newList = new ArrayPoolList(span.Length); + ArrayPoolList newList = new(span.Length); + newList.AddRange(span); + return newList; + } + + public static ArrayPoolListRef ToPooledListRef(this in ReadOnlySpan span) + { + ArrayPoolListRef newList = new(span.Length); newList.AddRange(span); return newList; } diff --git a/src/Nethermind/Nethermind.Core/Extensions/WaitHandleExtensions.cs b/src/Nethermind/Nethermind.Core/Extensions/WaitHandleExtensions.cs index fd0616dca645..5090c78856f1 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/WaitHandleExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/WaitHandleExtensions.cs @@ -23,8 +23,12 @@ public static async Task WaitOneAsync(this WaitHandle handle, int millisec millisecondsTimeout, true); tokenRegistration = cancellationToken.Register( - static state => ((TaskCompletionSource)state!).TrySetCanceled(), - tcs); + static state => + { + var (tcs, ct) = ((TaskCompletionSource tcs, CancellationToken ct))state!; + tcs.TrySetCanceled(ct); + }, + (tcs, cancellationToken)); return await tcs.Task; } diff --git a/src/Nethermind/Nethermind.Core/FakeWriteBatch.cs b/src/Nethermind/Nethermind.Core/FakeWriteBatch.cs index ce27831eb399..1fcf153998a1 100644 --- a/src/Nethermind/Nethermind.Core/FakeWriteBatch.cs +++ b/src/Nethermind/Nethermind.Core/FakeWriteBatch.cs @@ -31,5 +31,12 @@ public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteF { _storePretendingToSupportBatches.Set(key, value, flags); } + + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + throw new NotSupportedException("Merging is not supported by this implementation."); + } + + public void Clear() { } } } diff --git a/src/Nethermind/Nethermind.Core/HardwareInfo.cs b/src/Nethermind/Nethermind.Core/HardwareInfo.cs index 280c04c5bb68..1073c917a671 100644 --- a/src/Nethermind/Nethermind.Core/HardwareInfo.cs +++ b/src/Nethermind/Nethermind.Core/HardwareInfo.cs @@ -2,17 +2,64 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.InteropServices; namespace Nethermind.Core; public class HardwareInfo : IHardwareInfo { public long AvailableMemoryBytes { get; } + public int? MaxOpenFilesLimit { get; } public HardwareInfo() { // Note: Not the same as memory capacity. This take into account current system memory pressure such as // other process as well as OS level limit such as rlimit. Eh, its good enough. AvailableMemoryBytes = GC.GetGCMemoryInfo().TotalAvailableMemoryBytes; + MaxOpenFilesLimit = GetMaxOpenFilesLimit(); } + + private static int? GetMaxOpenFilesLimit() + { + try + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Windows doesn't have a per-process file descriptor limit like Unix systems + // The limit is much higher (typically thousands), so we return null to indicate no specific limit + return null; + } + else + { + // Unix-like systems (Linux, macOS, etc.) + const int RLIMIT_NOFILE = 7; // Same value for both macOS and Linux + + RLimit limit = new(); + int result = getrlimit(RLIMIT_NOFILE, ref limit); + + if (result == 0) + { + // Return the soft limit as it's what the process can actually use + // The soft limit can be raised up to the hard limit by the process + return (int)Math.Min(limit.rlim_cur, int.MaxValue); + } + } + } + catch + { + // If we can't detect the limit, return null to indicate unknown + } + + return null; + } + + [StructLayout(LayoutKind.Sequential)] + private struct RLimit + { + public ulong rlim_cur; // Soft limit + public ulong rlim_max; // Hard limit + } + + [DllImport("libc", SetLastError = true)] + private static extern int getrlimit(int resource, ref RLimit rlim); } diff --git a/src/Nethermind/Nethermind.Core/IHardwareInfo.cs b/src/Nethermind/Nethermind.Core/IHardwareInfo.cs index 0b9e98ff9516..87d78540683c 100644 --- a/src/Nethermind/Nethermind.Core/IHardwareInfo.cs +++ b/src/Nethermind/Nethermind.Core/IHardwareInfo.cs @@ -9,4 +9,5 @@ public interface IHardwareInfo { public static readonly long StateDbLargerMemoryThreshold = 20.GiB(); long AvailableMemoryBytes { get; } + int? MaxOpenFilesLimit { get; } } diff --git a/src/Nethermind/Nethermind.Core/IKeyValueStore.cs b/src/Nethermind/Nethermind.Core/IKeyValueStore.cs index e613308267ed..52a6b97b317c 100644 --- a/src/Nethermind/Nethermind.Core/IKeyValueStore.cs +++ b/src/Nethermind/Nethermind.Core/IKeyValueStore.cs @@ -69,6 +69,11 @@ public interface IWriteOnlyKeyValueStore void Remove(ReadOnlySpan key) => Set(key, null); } + public interface IMergeableKeyValueStore : IWriteOnlyKeyValueStore + { + void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None); + } + public interface ISortedKeyValueStore : IKeyValueStore { byte[]? FirstKey { get; } @@ -82,6 +87,7 @@ public interface ISortedKeyValueStore : IKeyValueStore /// public interface ISortedView : IDisposable { + public bool StartBefore(ReadOnlySpan value); public bool MoveNext(); public ReadOnlySpan CurrentKey { get; } public ReadOnlySpan CurrentValue { get; } diff --git a/src/Nethermind/Nethermind.Core/IMessageHandler.cs b/src/Nethermind/Nethermind.Core/IMessageHandler.cs new file mode 100644 index 000000000000..41abb066e010 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/IMessageHandler.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core; + +public interface IMessageHandler +{ + void HandleMessage(TMessage message); +} diff --git a/src/Nethermind/Nethermind.Core/INew.cs b/src/Nethermind/Nethermind.Core/INew.cs new file mode 100644 index 000000000000..76cb860edd8b --- /dev/null +++ b/src/Nethermind/Nethermind.Core/INew.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core; + +public interface INew +{ + public static abstract T New(TArg arg); +} diff --git a/src/Nethermind/Nethermind.Core/IWriteBatch.cs b/src/Nethermind/Nethermind.Core/IWriteBatch.cs index 909f184541c0..852dc132caa9 100644 --- a/src/Nethermind/Nethermind.Core/IWriteBatch.cs +++ b/src/Nethermind/Nethermind.Core/IWriteBatch.cs @@ -5,5 +5,8 @@ namespace Nethermind.Core { - public interface IWriteBatch : IDisposable, IWriteOnlyKeyValueStore; + public interface IWriteBatch : IDisposable, IWriteOnlyKeyValueStore, IMergeableKeyValueStore + { + void Clear(); + } } diff --git a/src/Nethermind/Nethermind.Core/JsonConverters/ByteArrayConverter.cs b/src/Nethermind/Nethermind.Core/JsonConverters/ByteArrayConverter.cs index e67479f53636..27b535d096f2 100644 --- a/src/Nethermind/Nethermind.Core/JsonConverters/ByteArrayConverter.cs +++ b/src/Nethermind/Nethermind.Core/JsonConverters/ByteArrayConverter.cs @@ -25,7 +25,7 @@ public class ByteArrayConverter : JsonConverter return Convert(ref reader); } - public static byte[]? Convert(ref Utf8JsonReader reader) + public static byte[]? Convert(ref Utf8JsonReader reader, bool strictHexFormat = false) { JsonTokenType tokenType = reader.TokenType; if (tokenType == JsonTokenType.None || tokenType == JsonTokenType.Null) @@ -35,7 +35,7 @@ public class ByteArrayConverter : JsonConverter if (reader.HasValueSequence) { - return ConvertValueSequence(ref reader); + return ConvertValueSequence(ref reader, strictHexFormat); } int length = reader.ValueSpan.Length; @@ -43,13 +43,14 @@ public class ByteArrayConverter : JsonConverter if (hex.Length == 0) return null; if (length >= 2 && Unsafe.As(ref MemoryMarshal.GetReference(hex)) == _hexPrefix) hex = hex[2..]; + else if (strictHexFormat) ThrowFormatException(); return Bytes.FromUtf8HexString(hex); } [SkipLocalsInit] [MethodImpl(MethodImplOptions.NoInlining)] - private static byte[]? ConvertValueSequence(ref Utf8JsonReader reader) + private static byte[]? ConvertValueSequence(ref Utf8JsonReader reader, bool strictHexFormat) { ReadOnlySequence valueSequence = reader.ValueSequence; int length = checked((int)valueSequence.Length); @@ -71,8 +72,11 @@ public class ByteArrayConverter : JsonConverter { // rewind if not really a prefix sr.Rewind(1); + if (strictHexFormat) + ThrowFormatException(); } } + else if (strictHexFormat) ThrowFormatException(); } // Compute total hex digit count (after prefix) @@ -246,4 +250,19 @@ public static void Convert( if (array is not null) ArrayPool.Shared.Return(array); } + + public override byte[] ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + byte[]? result = Convert(ref reader); + + if (result is null) + ThrowInvalidOperationException(); + + return result; + } + + public override void WriteAsPropertyName(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) + { + Convert(writer, value, static (w, h) => w.WritePropertyName(h), skipLeadingZeros: false, addQuotations: false, addHexPrefix: true); + } } diff --git a/src/Nethermind/Nethermind.Core/KeyValueStoreExtensions.cs b/src/Nethermind/Nethermind.Core/KeyValueStoreExtensions.cs index 906b39acf45d..bea10d063f48 100644 --- a/src/Nethermind/Nethermind.Core/KeyValueStoreExtensions.cs +++ b/src/Nethermind/Nethermind.Core/KeyValueStoreExtensions.cs @@ -22,8 +22,6 @@ public static IWriteBatch LikeABatch(this IWriteOnlyKeyValueStore keyValueStore, return new FakeWriteBatch(keyValueStore, onDispose); } - #region Getters - public static byte[]? Get(this IReadOnlyKeyValueStore db, Hash256 key) { #if DEBUG @@ -88,12 +86,6 @@ public static bool KeyExists(this IReadOnlyKeyValueStore db, long key) return span.IsNullOrEmpty() ? null : new DbSpanMemoryManager(db, span); } - - #endregion - - - #region Setters - public static void Set(this IWriteOnlyKeyValueStore db, Hash256 key, byte[] value, WriteFlags writeFlags = WriteFlags.None) { if (db.PreferWriteByArray) @@ -115,14 +107,15 @@ public static void Set(this IWriteOnlyKeyValueStore db, Hash256 key, in CappedAr db.PutSpan(key.Bytes, value.AsSpan(), writeFlags); } + [SkipLocalsInit] public static void Set(this IWriteOnlyKeyValueStore db, long blockNumber, Hash256 key, ReadOnlySpan value, WriteFlags writeFlags = WriteFlags.None) { Span blockNumberPrefixedKey = stackalloc byte[40]; - GetBlockNumPrefixedKey(blockNumber, key, blockNumberPrefixedKey); + GetBlockNumPrefixedKey(blockNumber, in key.ValueHash256, blockNumberPrefixedKey); db.PutSpan(blockNumberPrefixedKey, value, writeFlags); } - public static void GetBlockNumPrefixedKey(long blockNumber, ValueHash256 blockHash, Span output) + public static void GetBlockNumPrefixedKey(long blockNumber, in ValueHash256 blockHash, Span output) { blockNumber.WriteBigEndian(output); blockHash!.Bytes.CopyTo(output[8..]); @@ -147,7 +140,7 @@ public static void Delete(this IWriteOnlyKeyValueStore db, long key) public static void Delete(this IWriteOnlyKeyValueStore db, long blockNumber, Hash256 hash) { Span key = stackalloc byte[40]; - GetBlockNumPrefixedKey(blockNumber, hash, key); + GetBlockNumPrefixedKey(blockNumber, in hash.ValueHash256, key); db.Remove(key); } @@ -155,7 +148,5 @@ public static void Set(this IWriteOnlyKeyValueStore db, long key, byte[] value) { db[key.ToBigEndianSpanWithoutLeadingZeros(out _)] = value; } - - #endregion } } diff --git a/src/Nethermind/Nethermind.Core/Messages/BlockErrorMessages.cs b/src/Nethermind/Nethermind.Core/Messages/BlockErrorMessages.cs index b0863c6da056..8cb8db2d83ce 100644 --- a/src/Nethermind/Nethermind.Core/Messages/BlockErrorMessages.cs +++ b/src/Nethermind/Nethermind.Core/Messages/BlockErrorMessages.cs @@ -149,4 +149,7 @@ public static string InvalidDepositEventLayout(string error) => public static string ExceededBlockSizeLimit(int limit) => $"ExceededBlockSizeLimit: Exceeded block size limit of {limit} bytes."; + + public static string ReceiptCountMismatch(int expectedCount, int actualCount) => + $"ReceiptCountMismatch: Expected {expectedCount} receipts to match transaction count, but got {actualCount}."; } diff --git a/src/Nethermind/Nethermind.Core/ProgressLogger.cs b/src/Nethermind/Nethermind.Core/ProgressLogger.cs index f674106dab0d..589bb60f90c4 100644 --- a/src/Nethermind/Nethermind.Core/ProgressLogger.cs +++ b/src/Nethermind/Nethermind.Core/ProgressLogger.cs @@ -47,9 +47,9 @@ public void IncrementSkipped(int skipped = 1) Interlocked.Add(ref _skipped, skipped); } - public void SetMeasuringPoint() + public void SetMeasuringPoint(bool resetCompletion = true) { - UtcEndTime = null; + if (resetCompletion) UtcEndTime = null; if (UtcStartTime is not null) { @@ -169,7 +169,7 @@ public void LogProgress() _lastReportState = reportState; _logger.Info(reportString); } - SetMeasuringPoint(); + SetMeasuringPoint(resetCompletion: false); } private string DefaultFormatter() diff --git a/src/Nethermind/Nethermind.Core/Utils/ConcurrentWriteBatcher.cs b/src/Nethermind/Nethermind.Core/Utils/ConcurrentWriteBatcher.cs index 9ee5829dc20f..a6c80ed33fc7 100644 --- a/src/Nethermind/Nethermind.Core/Utils/ConcurrentWriteBatcher.cs +++ b/src/Nethermind/Nethermind.Core/Utils/ConcurrentWriteBatcher.cs @@ -40,6 +40,18 @@ public void PutSpan(ReadOnlySpan key, ReadOnlySpan value, WriteFlags ReturnWriteBatch(currentBatch); } + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + IWriteBatch currentBatch = RentWriteBatch(); + currentBatch.Merge(key, value, flags); + ReturnWriteBatch(currentBatch); + } + + public void Clear() + { + throw new NotSupportedException($"{nameof(ConcurrentWriteBatcher)} can not be cancelled."); + } + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { IWriteBatch currentBatch = RentWriteBatch(); diff --git a/src/Nethermind/Nethermind.Crypto/BlsSigner.cs b/src/Nethermind/Nethermind.Crypto/BlsSigner.cs index f8a5fcf35d40..3beef7a9e550 100644 --- a/src/Nethermind/Nethermind.Crypto/BlsSigner.cs +++ b/src/Nethermind/Nethermind.Crypto/BlsSigner.cs @@ -44,7 +44,7 @@ public static Signature Sign(Bls.SecretKey sk, ReadOnlySpan message) public static bool Verify(G1Affine publicKey, Signature signature, ReadOnlySpan message) { int len = 2 * GT.Sz; - using ArrayPoolList buf = new(len, len); + using ArrayPoolListRef buf = new(len, len); GT p1 = new(buf.AsSpan()[..GT.Sz]); p1.MillerLoop(signature.Point, G1Affine.Generator(stackalloc long[G1Affine.Sz])); diff --git a/src/Nethermind/Nethermind.Crypto/CryptoRandom.cs b/src/Nethermind/Nethermind.Crypto/CryptoRandom.cs index 0fd8279bc2dc..96dc3fb96de2 100644 --- a/src/Nethermind/Nethermind.Crypto/CryptoRandom.cs +++ b/src/Nethermind/Nethermind.Crypto/CryptoRandom.cs @@ -11,7 +11,6 @@ namespace Nethermind.Crypto public class CryptoRandom : ICryptoRandom { private readonly RandomNumberGenerator _secureRandom = RandomNumberGenerator.Create(); - private readonly Random _random = new(); public void GenerateRandomBytes(Span bytes) { @@ -25,10 +24,11 @@ public byte[] GenerateRandomBytes(int length) return bytes; } - [RequiresSecurityReview("There should be no unsecured method in a class that suggests security")] public int NextInt(int max) { - return _random.Next(max); + // Use cryptographically secure RNG; preserve Random.Next behavior for non-positive max + // (Random.Next(0) returns 0; negatives throw). This keeps compatibility. + return max <= 0 ? 0 : RandomNumberGenerator.GetInt32(max); } public void Dispose() diff --git a/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs b/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs index ece6bb682979..f1271ebe84e2 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs @@ -11,7 +11,7 @@ namespace Nethermind.Db.Rocks; -public class ColumnDb : IDb, ISortedKeyValueStore +public class ColumnDb : IDb, ISortedKeyValueStore, IMergeableKeyValueStore { private readonly RocksDb _rocksDb; internal readonly DbOnTheRocks _mainDb; @@ -57,6 +57,11 @@ public void PutSpan(ReadOnlySpan key, ReadOnlySpan value, WriteFlags _mainDb.SetWithColumnFamily(key, _columnFamily, value, writeFlags); } + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags writeFlags = WriteFlags.None) + { + _mainDb.MergeWithColumnFamily(key, _columnFamily, value, writeFlags); + } + public KeyValuePair[] this[byte[][] keys] => _rocksDb.MultiGet(keys, keys.Select(k => _columnFamily).ToArray()); @@ -99,6 +104,11 @@ public void Dispose() _underlyingWriteBatch.Dispose(); } + public void Clear() + { + _underlyingWriteBatch.Clear(); + } + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { if (value is null) @@ -115,6 +125,11 @@ public void PutSpan(ReadOnlySpan key, ReadOnlySpan value, WriteFlags { _underlyingWriteBatch.Set(key, value, _columnDb._columnFamily, flags); } + + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + _underlyingWriteBatch.Merge(key, value, _columnDb._columnFamily, flags); + } } public void Remove(ReadOnlySpan key) @@ -130,7 +145,7 @@ public bool KeyExists(ReadOnlySpan key) public void Flush(bool onlyWal) { - _mainDb.Flush(onlyWal); + _mainDb.FlushWithColumnFamily(_columnFamily); } public void Compact() diff --git a/src/Nethermind/Nethermind.Db.Rocks/ColumnsDb.cs b/src/Nethermind/Nethermind.Db.Rocks/ColumnsDb.cs index 02300a2d7e62..d5a240d1e968 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/ColumnsDb.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/ColumnsDb.cs @@ -61,9 +61,9 @@ private static IReadOnlyList GetEnumKeys(IReadOnlyList keys) return keys; } - protected override void BuildOptions(IRocksDbConfig dbConfig, Options options, IntPtr? sharedCache) + protected override void BuildOptions(IRocksDbConfig dbConfig, Options options, IntPtr? sharedCache, IMergeOperator? mergeOperator) { - base.BuildOptions(dbConfig, options, sharedCache); + base.BuildOptions(dbConfig, options, sharedCache, mergeOperator); options.SetCreateMissingColumnFamilies(); } @@ -130,9 +130,19 @@ public void Dispose() _writeBatch.Dispose(); } + public void Clear() + { + _writeBatch._writeBatch.Clear(); + } + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { _writeBatch._writeBatch.Set(key, value, _column._columnFamily, flags); } + + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + _writeBatch._writeBatch.Merge(key, value, _column._columnFamily, flags); + } } } diff --git a/src/Nethermind/Nethermind.Db.Rocks/Config/PerTableDbConfig.cs b/src/Nethermind/Nethermind.Db.Rocks/Config/PerTableDbConfig.cs index 6524901e3578..0efa1fe7553d 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/Config/PerTableDbConfig.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/Config/PerTableDbConfig.cs @@ -38,7 +38,7 @@ private void EnsureConfigIsAvailable(string propertyName) foreach (var prefix in _prefixes) { string prefixed = string.Concat(prefix, propertyName); - if (type.GetProperty(prefixed, BindingFlags.Public | BindingFlags.Instance) is null) + if (GetProperty(type, prefixed) is null) { throw new InvalidConfigurationException($"Configuration {propertyName} not available with prefix {prefix}. Add {prefix}{propertyName} to {nameof(IDbConfig)}.", -1); } @@ -90,13 +90,13 @@ private static string ReadRocksdbOptions(IDbConfig dbConfig, string propertyName Type type = dbConfig.GetType(); PropertyInfo? propertyInfo; - string val = (string)type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance)!.GetValue(dbConfig)!; + string val = (string)GetProperty(type, propertyName)!.GetValue(dbConfig)!; foreach (var prefix in prefixes) { string prefixed = string.Concat(prefix, propertyName); - propertyInfo = type.GetProperty(prefixed, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + propertyInfo = GetProperty(type, prefixed); if (propertyInfo is not null) { string? valObj = (string?)propertyInfo.GetValue(dbConfig); @@ -122,7 +122,7 @@ private static string ReadRocksdbOptions(IDbConfig dbConfig, string propertyName { string prefixed = string.Concat(prefix, propertyName); - propertyInfo = type.GetProperty(prefixed, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + propertyInfo = GetProperty(type, prefixed); if (propertyInfo is not null) { if (propertyInfo.PropertyType.CanBeAssignedNull()) @@ -147,7 +147,7 @@ private static string ReadRocksdbOptions(IDbConfig dbConfig, string propertyName } // Use generic one even if its available - propertyInfo = type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + propertyInfo = GetProperty(type, propertyName); return (T?)propertyInfo?.GetValue(dbConfig); } catch (Exception e) @@ -156,4 +156,6 @@ private static string ReadRocksdbOptions(IDbConfig dbConfig, string propertyName } } + private static PropertyInfo? GetProperty(Type type, string name) => + type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); } diff --git a/src/Nethermind/Nethermind.Db.Rocks/Config/RocksDbConfigFactory.cs b/src/Nethermind/Nethermind.Db.Rocks/Config/RocksDbConfigFactory.cs index aa62daff031d..add67c9965df 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/Config/RocksDbConfigFactory.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/Config/RocksDbConfigFactory.cs @@ -11,9 +11,33 @@ namespace Nethermind.Db.Rocks.Config; public class RocksDbConfigFactory(IDbConfig dbConfig, IPruningConfig pruningConfig, IHardwareInfo hardwareInfo, ILogManager logManager) : IRocksDbConfigFactory { private readonly ILogger _logger = logManager.GetClassLogger(); + private bool _maxOpenFilesInitialized; public IRocksDbConfig GetForDatabase(string databaseName, string? columnName) { + // Automatically adjust MaxOpenFiles if not configured (only once) + if (!_maxOpenFilesInitialized) + { + _maxOpenFilesInitialized = true; + + if (dbConfig.MaxOpenFiles is null && hardwareInfo.MaxOpenFilesLimit.HasValue) + { + int systemLimit = hardwareInfo.MaxOpenFilesLimit.Value; + // Apply 80% of system limit as safety margin to account for: + // - Multiple databases (~15) each using this limit + // - System operations and network sockets + // - Other file descriptors needed by the application + int perDbLimit = Math.Max(256, (int)(systemLimit * 0.8)); + + if (_logger.IsInfo) + { + _logger.Info($"Detected system open files limit of {systemLimit}. Setting MaxOpenFiles to {perDbLimit} per database."); + } + + dbConfig.MaxOpenFiles = perDbLimit; + } + } + IRocksDbConfig rocksDbConfig = new PerTableDbConfig(dbConfig, databaseName, columnName); if (databaseName.StartsWith("State")) { diff --git a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs index 474ae0d50da1..a69d62475f0c 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs @@ -29,7 +29,7 @@ namespace Nethermind.Db.Rocks; -public partial class DbOnTheRocks : IDb, ITunableDb, IReadOnlyNativeKeyValueStore, ISortedKeyValueStore +public partial class DbOnTheRocks : IDb, ITunableDb, IReadOnlyNativeKeyValueStore, ISortedKeyValueStore, IMergeableKeyValueStore { protected ILogger _logger; @@ -66,6 +66,10 @@ public partial class DbOnTheRocks : IDb, ITunableDb, IReadOnlyNativeKeyValueStor private readonly DbSettings _settings; + // Need to keep options from GC in case of merge operator applied, as they are used in callback + // ReSharper disable once CollectionNeverQueried.Local + private readonly List _doNotGcOptions = []; + private readonly IRocksDbConfig _perTableDbConfig; private ulong _maxBytesForLevelBase; private ulong _targetFileSizeBase; @@ -143,7 +147,7 @@ private RocksDb Init(string basePath, string dbPath, IDbConfig dbConfig, ILogMan // ReSharper disable once VirtualMemberCallInConstructor if (_logger.IsDebug) _logger.Debug($"Building options for {Name} DB"); DbOptions = new DbOptions(); - BuildOptions(_perTableDbConfig, DbOptions, sharedCache); + BuildOptions(_perTableDbConfig, DbOptions, sharedCache, _settings.MergeOperator); ColumnFamilies? columnFamilies = null; if (columnNames is not null) @@ -155,7 +159,8 @@ private RocksDb Init(string basePath, string dbPath, IDbConfig dbConfig, ILogMan ColumnFamilyOptions options = new(); IRocksDbConfig columnConfig = _rocksDbConfigFactory.GetForDatabase(Name, columnFamily); - BuildOptions(columnConfig, options, sharedCache); + IMergeOperator? mergeOperator = _settings.ColumnsMergeOperators?.GetValueOrDefault(enumColumnName); + BuildOptions(columnConfig, options, sharedCache, mergeOperator); // "default" is a special column name with rocksdb, which is what previously not specifying column goes to if (columnFamily == "Default") columnFamily = "default"; @@ -447,7 +452,7 @@ public static IDictionary ExtractOptions(string dbOptions) return asDict; } - protected virtual void BuildOptions(IRocksDbConfig dbConfig, Options options, IntPtr? sharedCache) where T : Options + protected virtual void BuildOptions(IRocksDbConfig dbConfig, Options options, IntPtr? sharedCache, IMergeOperator? mergeOperator) where T : Options { // This section is about the table factory.. and block cache apparently. // This effect the format of the SST files and usually require resync to take effect. @@ -577,6 +582,13 @@ protected virtual void BuildOptions(IRocksDbConfig dbConfig, Options optio } } + if (mergeOperator is not null) + { + options.SetMergeOperator(new MergeOperatorAdapter(mergeOperator)); + lock (_doNotGcOptions) + _doNotGcOptions.Add(options); + } + #endregion #region read-write options @@ -893,6 +905,40 @@ public void PutSpan(ReadOnlySpan key, ReadOnlySpan value, WriteFlags SetWithColumnFamily(key, null, value, writeFlags); } + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + ObjectDisposedException.ThrowIf(_isDisposing, this); + + UpdateWriteMetrics(); + + try + { + _db.Merge(key, value, null, WriteFlagsToWriteOptions(flags)); + } + catch (RocksDbSharpException e) + { + CreateMarkerIfCorrupt(e); + throw; + } + } + + internal void MergeWithColumnFamily(ReadOnlySpan key, ColumnFamilyHandle? cf, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + ObjectDisposedException.ThrowIf(_isDisposing, this); + + UpdateWriteMetrics(); + + try + { + _db.Merge(key, value, cf, WriteFlagsToWriteOptions(flags)); + } + catch (RocksDbSharpException e) + { + CreateMarkerIfCorrupt(e); + throw; + } + } + public void DangerousReleaseMemory(in ReadOnlySpan span) { if (!span.IsNullOrEmpty()) @@ -1222,6 +1268,13 @@ private static void ReturnWriteBatch(WriteBatch batch) _reusableWriteBatch = batch; } + public void Clear() + { + ObjectDisposedException.ThrowIf(_dbOnTheRocks._isDisposed, _dbOnTheRocks); + + _rocksBatch.Clear(); + } + public void Dispose() { ObjectDisposedException.ThrowIf(_dbOnTheRocks._isDisposed, _dbOnTheRocks); @@ -1280,6 +1333,21 @@ public void PutSpan(ReadOnlySpan key, ReadOnlySpan value, WriteFlags Set(key, value, null, flags); } + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + Merge(key, value, null, flags); + } + + public void Merge(ReadOnlySpan key, ReadOnlySpan value, ColumnFamilyHandle? cf = null, WriteFlags flags = WriteFlags.None) + { + ObjectDisposedException.ThrowIf(_isDisposed, this); + + _rocksBatch.Merge(key, value, cf); + _writeFlags = flags; + + if ((flags & WriteFlags.DisableWAL) != 0) FlushOnTooManyWrites(); + } + private void FlushOnTooManyWrites() { if (Interlocked.Increment(ref _writeCount) % MaxWritesOnNoWal != 0) return; @@ -1306,6 +1374,13 @@ public void Flush(bool onlyWal = false) InnerFlush(onlyWal); } + public void FlushWithColumnFamily(ColumnFamilyHandle familyHandle) + { + ObjectDisposedException.ThrowIf(_isDisposing, this); + + InnerFlush(familyHandle); + } + public virtual void Compact() { _db.CompactRange(Keccak.Zero.BytesToArray(), Keccak.MaxValue.BytesToArray()); @@ -1328,6 +1403,18 @@ private void InnerFlush(bool onlyWal) } } + private void InnerFlush(ColumnFamilyHandle columnFamilyHandle) + { + try + { + _rocksDbNative.rocksdb_flush_cf(_db.Handle, FlushOptions.DefaultFlushOptions.Handle, columnFamilyHandle.Handle); + } + catch (RocksDbSharpException e) + { + CreateMarkerIfCorrupt(e); + } + } + public void Clear() { Dispose(); @@ -1776,10 +1863,10 @@ private sealed class ManagedIterators : ThreadLocal public void ClearIterators() { if (_disposed) return; - if (Values is null) return; - foreach (IteratorHolder iterator in Values) + if (Values is not { } values) return; + foreach (IteratorHolder iterator in values) { - iterator.Dispose(); + iterator?.Dispose(); } } diff --git a/src/Nethermind/Nethermind.Db.Rocks/MergeOperatorAdapter.cs b/src/Nethermind/Nethermind.Db.Rocks/MergeOperatorAdapter.cs new file mode 100644 index 000000000000..14bf9efc98f8 --- /dev/null +++ b/src/Nethermind/Nethermind.Db.Rocks/MergeOperatorAdapter.cs @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Nethermind.Core.Collections; +using RocksDbSharp; + +namespace Nethermind.Db.Rocks; + +// Also see RocksDbSharp.MergeOperatorImpl +internal class MergeOperatorAdapter(IMergeOperator inner) : MergeOperator +{ + public string Name => inner.Name; + + // TODO: fix and return array ptr instead of copying to unmanaged memory? + private static unsafe IntPtr GetResult(ArrayPoolList? data, out IntPtr resultLength, out IntPtr success) + { + if (data is null) + { + success = Convert.ToInt32(false); + resultLength = IntPtr.Zero; + return IntPtr.Zero; + } + + using (data) + { + void* resultPtr = NativeMemory.Alloc((uint)data.Count); + var result = new Span(resultPtr, data.Count); + data.AsSpan().CopyTo(result); + + resultLength = result.Length; + + // Fixing RocksDbSharp invalid callback signature, TODO: submit an issue/PR + Unsafe.SkipInit(out success); + Unsafe.As(ref success) = 1; + + return (IntPtr)resultPtr; + } + } + + unsafe IntPtr MergeOperator.PartialMerge( + IntPtr keyPtr, + UIntPtr keyLength, + IntPtr operandsList, + IntPtr operandsListLength, + int numOperands, + out IntPtr successPtr, + out IntPtr resultLength) + { + var key = new Span((void*)keyPtr, (int)keyLength); + var enumerator = new RocksDbMergeEnumerator(new((void*)operandsList, numOperands), new((void*)operandsListLength, numOperands)); + + ArrayPoolList? result = inner.PartialMerge(key, enumerator); + return GetResult(result, out resultLength, out successPtr); + } + + unsafe IntPtr MergeOperator.FullMerge( + IntPtr keyPtr, + UIntPtr keyLength, + IntPtr existingValuePtr, + UIntPtr existingValueLength, + IntPtr operandsList, + IntPtr operandsListLength, + int numOperands, + out IntPtr successPtr, + out IntPtr resultLength) + { + var key = new ReadOnlySpan((void*)keyPtr, (int)keyLength); + bool hasExistingValue = existingValuePtr != IntPtr.Zero; + Span existingValue = hasExistingValue ? new((void*)existingValuePtr, (int)existingValueLength) : Span.Empty; + var enumerator = new RocksDbMergeEnumerator(existingValue, hasExistingValue, new((void*)operandsList, numOperands), new((void*)operandsListLength, numOperands)); + + ArrayPoolList? result = inner.FullMerge(key, enumerator); + return GetResult(result, out resultLength, out successPtr); + } + + unsafe void MergeOperator.DeleteValue(IntPtr value, UIntPtr valueLength) => NativeMemory.Free((void*)value); +} diff --git a/src/Nethermind/Nethermind.Db.Rocks/RocksdbSortedView.cs b/src/Nethermind/Nethermind.Db.Rocks/RocksdbSortedView.cs index e617000d5320..a64fe85d5b49 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/RocksdbSortedView.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/RocksdbSortedView.cs @@ -22,6 +22,15 @@ public void Dispose() _iterator.Dispose(); } + public bool StartBefore(ReadOnlySpan value) + { + if (_started) + throw new InvalidOperationException($"{nameof(StartBefore)} can only be called before starting iteration."); + + _iterator.SeekForPrev(value); + return _started = _iterator.Valid(); + } + public bool MoveNext() { if (!_started) diff --git a/src/Nethermind/Nethermind.Db.Test/RocksDbConfigFactoryTests.cs b/src/Nethermind/Nethermind.Db.Test/RocksDbConfigFactoryTests.cs index 61222f9c4046..ad1d4d04c314 100644 --- a/src/Nethermind/Nethermind.Db.Test/RocksDbConfigFactoryTests.cs +++ b/src/Nethermind/Nethermind.Db.Test/RocksDbConfigFactoryTests.cs @@ -58,4 +58,46 @@ public void WillOverrideStateConfigWhenDirtyCachesTooHigh() IRocksDbConfig config = factory.GetForDatabase("State0", null); config.WriteBufferSize.Should().Be((ulong)500.MB()); } + + [Test] + public void WillAutomaticallySetMaxOpenFilesWhenNotConfigured() + { + var dbConfig = new DbConfig(); + var factory = new RocksDbConfigFactory(dbConfig, new PruningConfig(), new TestHardwareInfo(0, 10000), LimboLogs.Instance); + IRocksDbConfig config = factory.GetForDatabase("State0", null); + // With system limit of 10000, should set per-db limit to 10000 * 0.8 = 8000 + config.MaxOpenFiles.Should().Be(8000); + } + + [Test] + public void WillNotOverrideUserConfiguredMaxOpenFiles() + { + var dbConfig = new DbConfig(); + dbConfig.MaxOpenFiles = 3000; + var factory = new RocksDbConfigFactory(dbConfig, new PruningConfig(), new TestHardwareInfo(0, 10000), LimboLogs.Instance); + IRocksDbConfig config = factory.GetForDatabase("State0", null); + // Should keep user-configured value + config.MaxOpenFiles.Should().Be(3000); + } + + [Test] + public void WillNotSetMaxOpenFilesWhenSystemLimitUnknown() + { + var dbConfig = new DbConfig(); + var factory = new RocksDbConfigFactory(dbConfig, new PruningConfig(), new TestHardwareInfo(0, null), LimboLogs.Instance); + IRocksDbConfig config = factory.GetForDatabase("State0", null); + // Should be null when system limit is unknown + config.MaxOpenFiles.Should().BeNull(); + } + + [Test] + public void WillEnforceMinimumMaxOpenFiles() + { + var dbConfig = new DbConfig(); + // Very low system limit (e.g., 1000) should still give minimum of 256 + var factory = new RocksDbConfigFactory(dbConfig, new PruningConfig(), new TestHardwareInfo(0, 1000), LimboLogs.Instance); + IRocksDbConfig config = factory.GetForDatabase("State0", null); + // With system limit of 1000, would be 1000 * 0.8 = 800 + config.MaxOpenFiles.Should().Be(800); + } } diff --git a/src/Nethermind/Nethermind.Db/CompressingDb.cs b/src/Nethermind/Nethermind.Db/CompressingDb.cs index d8284f2049e9..a03e69ff1036 100644 --- a/src/Nethermind/Nethermind.Db/CompressingDb.cs +++ b/src/Nethermind/Nethermind.Db/CompressingDb.cs @@ -44,6 +44,8 @@ private class WriteBatch : IWriteBatch public void Dispose() => _wrapped.Dispose(); + public void Clear() => _wrapped.Clear(); + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) => _wrapped.Set(key, Compress(value), flags); @@ -52,6 +54,11 @@ public void PutSpan(ReadOnlySpan key, ReadOnlySpan value, WriteFlags _wrapped.PutSpan(key, Compress(value, stackalloc byte[value.Length]), flags); } + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + throw new InvalidOperationException("EOA compressing DB does not support merging"); + } + public bool PreferWriteByArray => _wrapped.PreferWriteByArray; public byte[]? this[ReadOnlySpan key] diff --git a/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs b/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs index fb2c98942716..f79c010c9889 100755 --- a/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs +++ b/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs @@ -103,6 +103,12 @@ private void Duplicate(IWriteOnlyKeyValueStore db, ReadOnlySpan key, ReadO _updateDuplicateWriteMetrics?.Invoke(); } + private void DuplicateMerge(IMergeableKeyValueStore db, ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags) + { + db.Merge(key, value, flags); + _updateDuplicateWriteMetrics?.Invoke(); + } + // we also need to duplicate writes that are in batches public IWriteBatch StartWriteBatch() => _pruningContext is null @@ -317,11 +323,23 @@ public void Dispose() _clonedWriteBatch.Dispose(); } + public void Clear() + { + _writeBatch.Clear(); + _clonedWriteBatch.Clear(); + } + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { _writeBatch.Set(key, value, flags); _db.Duplicate(_clonedWriteBatch, key, value, flags); } + + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + _writeBatch.Merge(key, value, flags); + _db.DuplicateMerge(_clonedWriteBatch, key, value, flags); + } } public void Tune(ITunableDb.TuneType type) diff --git a/src/Nethermind/Nethermind.Db/IMergeOperator.cs b/src/Nethermind/Nethermind.Db/IMergeOperator.cs new file mode 100644 index 000000000000..7df2946bd71f --- /dev/null +++ b/src/Nethermind/Nethermind.Db/IMergeOperator.cs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.Collections; + +namespace Nethermind.Db; + +public interface IMergeOperator +{ + string Name { get; } + ArrayPoolList? FullMerge(ReadOnlySpan key, RocksDbMergeEnumerator enumerator); + ArrayPoolList? PartialMerge(ReadOnlySpan key, RocksDbMergeEnumerator enumerator); +} diff --git a/src/Nethermind/Nethermind.Db/InMemoryWriteBatch.cs b/src/Nethermind/Nethermind.Db/InMemoryWriteBatch.cs index 7c43ec39039c..c8054ac8b6d2 100644 --- a/src/Nethermind/Nethermind.Db/InMemoryWriteBatch.cs +++ b/src/Nethermind/Nethermind.Db/InMemoryWriteBatch.cs @@ -29,10 +29,20 @@ public void Dispose() GC.SuppressFinalize(this); } + public void Clear() + { + _currentItems.Clear(); + } + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { _currentItems[key.ToArray()] = value; _writeFlags = flags; } + + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + throw new NotSupportedException("Merging is not supported by this implementation."); + } } } diff --git a/src/Nethermind/Nethermind.Db/RocksDbMergeEnumerator.cs b/src/Nethermind/Nethermind.Db/RocksDbMergeEnumerator.cs new file mode 100644 index 000000000000..fa2da2060b30 --- /dev/null +++ b/src/Nethermind/Nethermind.Db/RocksDbMergeEnumerator.cs @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; + +namespace Nethermind.Db; + +/// +/// RocksDB enumerator for values of merge operation. +/// +// Interface was not used because of ref struct limitations. +public readonly ref struct RocksDbMergeEnumerator(ReadOnlySpan operandsList, ReadOnlySpan operandsListLength) +{ + private readonly ReadOnlySpan _operandsList = operandsList; + private readonly ReadOnlySpan _operandsListLength = operandsListLength; + + public Span ExistingValue { get; } + public bool HasExistingValue { get; } + public int OperandsCount => _operandsList.Length; + public int TotalCount => OperandsCount + (HasExistingValue ? 1 : 0); + + public RocksDbMergeEnumerator( + Span existingValue, bool hasExistingValue, + ReadOnlySpan operandsList, ReadOnlySpan operandsListLength + ) : this(operandsList, operandsListLength) + { + ExistingValue = existingValue; + HasExistingValue = hasExistingValue; + } + + public Span GetExistingValue() + { + return HasExistingValue ? ExistingValue : default; + } + + public unsafe Span GetOperand(int index) + { + return new((void*)_operandsList[index], (int)_operandsListLength[index]); + } + + public Span Get(int index) + { + if (index == 0 && HasExistingValue) + return ExistingValue; + + if (HasExistingValue) + index -= 1; + + return GetOperand(index); + } +} diff --git a/src/Nethermind/Nethermind.Db/RocksDbSettings.cs b/src/Nethermind/Nethermind.Db/RocksDbSettings.cs index f3d9eebd4e67..d3ee2b073f39 100644 --- a/src/Nethermind/Nethermind.Db/RocksDbSettings.cs +++ b/src/Nethermind/Nethermind.Db/RocksDbSettings.cs @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; +using System.Collections.Generic; + namespace Nethermind.Db { public class DbSettings @@ -17,6 +20,9 @@ public DbSettings(string name, string path) public bool DeleteOnStart { get; set; } public bool CanDeleteFolder { get; set; } = true; + public IMergeOperator? MergeOperator { get; set; } + public Dictionary? ColumnsMergeOperators { get; set; } + public DbSettings Clone(string name, string path) { DbSettings settings = (DbSettings)MemberwiseClone(); diff --git a/src/Nethermind/Nethermind.Era1.Test/Era1ModuleTests.cs b/src/Nethermind/Nethermind.Era1.Test/Era1ModuleTests.cs index 49c22c3ba380..f43210b385e4 100644 --- a/src/Nethermind/Nethermind.Era1.Test/Era1ModuleTests.cs +++ b/src/Nethermind/Nethermind.Era1.Test/Era1ModuleTests.cs @@ -73,7 +73,6 @@ public async Task ExportAndImportTwoBlocksAndReceipts() Assert.That(importedBlock1.TotalDifficulty, Is.EqualTo(BlockHeaderBuilder.DefaultDifficulty)); } - [TestCase("holesky")] [TestCase("mainnet")] public async Task ImportAndExportGethFiles(string network) { diff --git a/src/Nethermind/Nethermind.Era1.Test/Nethermind.Era1.Test.csproj b/src/Nethermind/Nethermind.Era1.Test/Nethermind.Era1.Test.csproj index ff61ad75ccab..d8eb03e51131 100644 --- a/src/Nethermind/Nethermind.Era1.Test/Nethermind.Era1.Test.csproj +++ b/src/Nethermind/Nethermind.Era1.Test/Nethermind.Era1.Test.csproj @@ -20,15 +20,6 @@ - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - PreserveNewest diff --git a/src/Nethermind/Nethermind.Era1.Test/testdata/holesky/holesky-00000-a3bfd81f.era1 b/src/Nethermind/Nethermind.Era1.Test/testdata/holesky/holesky-00000-a3bfd81f.era1 deleted file mode 100644 index b0bff516105a..000000000000 Binary files a/src/Nethermind/Nethermind.Era1.Test/testdata/holesky/holesky-00000-a3bfd81f.era1 and /dev/null differ diff --git a/src/Nethermind/Nethermind.Era1.Test/testdata/holesky/holesky-00001-e343db12.era1 b/src/Nethermind/Nethermind.Era1.Test/testdata/holesky/holesky-00001-e343db12.era1 deleted file mode 100644 index 45d4919e23b3..000000000000 Binary files a/src/Nethermind/Nethermind.Era1.Test/testdata/holesky/holesky-00001-e343db12.era1 and /dev/null differ diff --git a/src/Nethermind/Nethermind.Era1.Test/testdata/holesky/holesky-00002-5a3ecb1a.era1 b/src/Nethermind/Nethermind.Era1.Test/testdata/holesky/holesky-00002-5a3ecb1a.era1 deleted file mode 100644 index f38c19c7cc74..000000000000 Binary files a/src/Nethermind/Nethermind.Era1.Test/testdata/holesky/holesky-00002-5a3ecb1a.era1 and /dev/null differ diff --git a/src/Nethermind/Nethermind.Era1/AccumulatorCalculator.cs b/src/Nethermind/Nethermind.Era1/AccumulatorCalculator.cs index c97fd19173f3..8be42473da51 100644 --- a/src/Nethermind/Nethermind.Era1/AccumulatorCalculator.cs +++ b/src/Nethermind/Nethermind.Era1/AccumulatorCalculator.cs @@ -12,16 +12,11 @@ namespace Nethermind.Era1; // https://github.com/ethereum/portal-network-specs/blob/master/history/history-network.md#algorithms internal class AccumulatorCalculator : IDisposable { - ArrayPoolList> _roots; - - public AccumulatorCalculator() - { - _roots = new(EraWriter.MaxEra1Size); - } + private readonly ArrayPoolList> _roots = new(EraWriter.MaxEra1Size); public void Add(Hash256 headerHash, UInt256 td) { - Merkleizer merkleizer = new Merkleizer((int)Merkle.NextPowerOfTwoExponent(2)); + Merkleizer merkleizer = new(Merkle.NextPowerOfTwoExponent(2)); merkleizer.Feed(headerHash.Bytes); merkleizer.Feed(td); _roots.Add(merkleizer.CalculateRoot().ToLittleEndian()); @@ -29,7 +24,7 @@ public void Add(Hash256 headerHash, UInt256 td) public ValueHash256 ComputeRoot() { - Merkleizer merkleizer = new Merkleizer(0); + Merkleizer merkleizer = new(0); merkleizer.Feed(_roots, EraWriter.MaxEra1Size); UInt256 root = merkleizer.CalculateRoot(); return new ValueHash256(MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1))); diff --git a/src/Nethermind/Nethermind.Era1/E2StoreReader.cs b/src/Nethermind/Nethermind.Era1/E2StoreReader.cs index 4031526717eb..66012cde3dbe 100644 --- a/src/Nethermind/Nethermind.Era1/E2StoreReader.cs +++ b/src/Nethermind/Nethermind.Era1/E2StoreReader.cs @@ -43,7 +43,7 @@ public long ReadEntryAndDecode(long position, Func, T> decoder, Entry entry = ReadEntry(position, expectedType); int length = (int)entry.Length; - using ArrayPoolList buffer = new ArrayPoolList(length, length); + using ArrayPoolListRef buffer = new(length, length); RandomAccess.Read(_file, buffer.AsSpan(), position + HeaderSize); value = decoder(buffer.AsMemory()); return (long)(entry.Length + HeaderSize); @@ -75,7 +75,7 @@ public Entry ReadEntry(long position, ushort? expectedType, CancellationToken to private async Task ReadEntryValueAsSnappy(long offset, ulong length, Func, T> decoder, CancellationToken cancellation = default) { - using ArrayPoolList inputBuffer = new ArrayPoolList((int)length, (int)length); + using ArrayPoolList inputBuffer = new((int)length, (int)length); RandomAccess.Read(_file, inputBuffer.AsSpan(), offset); Stream inputStream = inputBuffer.AsMemory().AsStream(); diff --git a/src/Nethermind/Nethermind.Era1/EraExporter.cs b/src/Nethermind/Nethermind.Era1/EraExporter.cs index 1f304770205a..493fffc15112 100644 --- a/src/Nethermind/Nethermind.Era1/EraExporter.cs +++ b/src/Nethermind/Nethermind.Era1/EraExporter.cs @@ -62,7 +62,7 @@ private async Task DoExport( fileSystem.Directory.CreateDirectory(destinationPath); } - ProgressLogger progress = new ProgressLogger("Era export", logManager); + ProgressLogger progress = new("Era export", logManager); progress.Reset(0, to - from + 1); int totalProcessed = 0; diff --git a/src/Nethermind/Nethermind.Era1/EraReader.cs b/src/Nethermind/Nethermind.Era1/EraReader.cs index 93f301267724..cb6d483355a5 100644 --- a/src/Nethermind/Nethermind.Era1/EraReader.cs +++ b/src/Nethermind/Nethermind.Era1/EraReader.cs @@ -77,7 +77,7 @@ public async Task VerifyContent(ISpecProvider specProvider, IBlock int blockCount = (int)_fileReader.BlockCount; using ArrayPoolList<(Hash256, UInt256)> blockHashes = new(blockCount, blockCount); - ConcurrentQueue blockNumbers = new ConcurrentQueue(EnumerateBlockNumber()); + ConcurrentQueue blockNumbers = new(EnumerateBlockNumber()); using ArrayPoolList workers = Enumerable.Range(0, verifyConcurrency).Select((_) => Task.Run(async () => { @@ -110,7 +110,7 @@ public async Task VerifyContent(ISpecProvider specProvider, IBlock await Task.WhenAll(workers.AsSpan()); using AccumulatorCalculator calculator = new(); - foreach (var valueTuple in blockHashes.AsSpan()) + foreach ((Hash256, UInt256) valueTuple in blockHashes.AsSpan()) { calculator.Add(valueTuple.Item1, valueTuple.Item2); } diff --git a/src/Nethermind/Nethermind.Era1/EraWriter.cs b/src/Nethermind/Nethermind.Era1/EraWriter.cs index 2574736d5f98..d30615230fb5 100644 --- a/src/Nethermind/Nethermind.Era1/EraWriter.cs +++ b/src/Nethermind/Nethermind.Era1/EraWriter.cs @@ -105,7 +105,7 @@ public async Task Add(Block block, TxReceipt[] receipts, CancellationToken cance //Index is 64 bits segments in the format => start | index | index | ... | count //16 bytes is for the start and count plus every entry int length = 16 + _entryIndexes.Count * 8; - using ArrayPoolList blockIndex = new ArrayPoolList(length, length); + using ArrayPoolList blockIndex = new(length, length); Span blockIndexSpan = blockIndex.AsSpan(); WriteInt64(blockIndexSpan, 0, _startNumber); diff --git a/src/Nethermind/Nethermind.Evm.Precompiles/Bls/PairingCheckPrecompile.cs b/src/Nethermind/Nethermind.Evm.Precompiles/Bls/PairingCheckPrecompile.cs index de45b8838a92..56fe9c944b14 100644 --- a/src/Nethermind/Nethermind.Evm.Precompiles/Bls/PairingCheckPrecompile.cs +++ b/src/Nethermind/Nethermind.Evm.Precompiles/Bls/PairingCheckPrecompile.cs @@ -44,7 +44,7 @@ private PairingCheckPrecompile() { } G1 x = new(stackalloc long[G1.Sz]); G2 y = new(stackalloc long[G2.Sz]); - using ArrayPoolList buf = new(GT.Sz * 2, GT.Sz * 2); + using ArrayPoolListRef buf = new(GT.Sz * 2, GT.Sz * 2); var acc = GT.One(buf.AsSpan()); GT p = new(buf.AsSpan()[GT.Sz..]); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs index 7069f9624653..f5fb43269de6 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs @@ -217,7 +217,7 @@ internal static long[] PopulateJumpDestinationBitmap_Vector512(long[] bitmap, Re int overflow = size - inThis; // Clear bits [lane .. lane+inThis-1] from both masks - ulong clearThis = (Bmi1.X64.IsSupported ? + ulong clearThis = (Bmi2.X64.IsSupported ? Bmi2.X64.ZeroHighBits(ulong.MaxValue, (uint)inThis) : ((1UL << inThis) - 1UL)) << lane; @@ -228,7 +228,7 @@ internal static long[] PopulateJumpDestinationBitmap_Vector512(long[] bitmap, Re // If it spilled, mark those lanes for the next chunk if (overflow > 0) { - carryMask = (Bmi1.X64.IsSupported ? + carryMask = (Bmi2.X64.IsSupported ? Bmi2.X64.ZeroHighBits(ulong.MaxValue, (uint)overflow) : ((1UL << overflow) - 1UL)); break; diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 163029357db7..959ed46ff4c3 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -38,7 +38,7 @@ public ICodeInfo GetCachedCodeInfo(Address codeSource, bool followDelegation, IR ICodeInfo cachedCodeInfo = InternalGetCachedCode(_worldState, codeSource, vmSpec); - if (!cachedCodeInfo.IsEmpty && TryGetDelegatedAddress(cachedCodeInfo.CodeSpan, out delegationAddress)) + if (!cachedCodeInfo.IsEmpty && ICodeInfoRepository.TryGetDelegatedAddress(cachedCodeInfo.CodeSpan, out delegationAddress)) { if (followDelegation) cachedCodeInfo = InternalGetCachedCode(_worldState, delegationAddress, vmSpec); @@ -144,27 +144,11 @@ public ValueHash256 GetExecutableCodeHash(Address address, IReleaseSpec spec) : codeHash; } - /// - /// Parses delegation code to extract the contained address. - /// Assumes is delegation code! - /// - private static bool TryGetDelegatedAddress(ReadOnlySpan code, [NotNullWhen(true)] out Address? address) - { - if (Eip7702Constants.IsDelegatedCode(code)) - { - address = new Address(code[Eip7702Constants.DelegationHeader.Length..].ToArray()); - return true; - } - - address = null; - return false; - } - public bool TryGetDelegation(Address address, IReleaseSpec spec, [NotNullWhen(true)] out Address? delegatedAddress) => - TryGetDelegatedAddress(InternalGetCachedCode(_worldState, address, spec).CodeSpan, out delegatedAddress); + ICodeInfoRepository.TryGetDelegatedAddress(InternalGetCachedCode(_worldState, address, spec).CodeSpan, out delegatedAddress); public bool TryGetDelegation(in ValueHash256 codeHash, IReleaseSpec spec, [NotNullWhen(true)] out Address? delegatedAddress) => - TryGetDelegatedAddress(InternalGetCachedCode(_worldState, in codeHash, spec).CodeSpan, out delegatedAddress); + ICodeInfoRepository.TryGetDelegatedAddress(InternalGetCachedCode(_worldState, in codeHash, spec).CodeSpan, out delegatedAddress); private sealed class CodeLruCache { diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs index 84bd43c0faa6..17e568d007e0 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs @@ -16,6 +16,23 @@ public interface ICodeInfoRepository void InsertCode(ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec); void SetDelegation(Address codeSource, Address authority, IReleaseSpec spec); bool TryGetDelegation(Address address, IReleaseSpec spec, [NotNullWhen(true)] out Address? delegatedAddress); + + /// + /// Parses delegation code to extract the contained address. + /// Assumes is delegation code! + /// + static bool TryGetDelegatedAddress(ReadOnlySpan code, [NotNullWhen(true)] out Address? address) + { + if (Eip7702Constants.IsDelegatedCode(code)) + { + address = new Address(code[Eip7702Constants.DelegationHeader.Length..].ToArray()); + return true; + } + + address = null; + return false; + } + } public static class CodeInfoRepositoryExtensions diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs index c99d7f27a0b0..0a1d8100b023 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs @@ -37,6 +37,7 @@ public CompositeTxTracer(IList txTracers) IsTracingStorage |= t.IsTracingStorage; IsTracingAccess |= t.IsTracingAccess; IsTracingFees |= t.IsTracingFees; + IsTracingLogs |= t.IsTracingLogs; } } @@ -187,24 +188,12 @@ public void ReportOperationRemainingGas(long gas) } } - public void ReportOperationLogs(LogEntry log) - { - for (int index = 0; index < _txTracers.Count; index++) - { - ITxTracer innerTracer = _txTracers[index]; - if (innerTracer.IsTracingInstructions && innerTracer.IsTracingLogs) - { - innerTracer.ReportLog(log); - } - } - } - public void ReportLog(LogEntry log) { for (int index = 0; index < _txTracers.Count; index++) { ITxTracer innerTracer = _txTracers[index]; - if (innerTracer.IsTracingInstructions) + if (innerTracer.IsTracingLogs) { innerTracer.ReportLog(log); } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index f43a7eb33dbd..438dd62c6cd6 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -554,13 +554,17 @@ protected virtual TransactionResult BuyGas(Transaction tx, IReleaseSpec spec, IT protected virtual TransactionResult IncrementNonce(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, ExecutionOptions opts) { - if (tx.Nonce != WorldState.GetNonce(tx.SenderAddress!)) + bool validate = !opts.HasFlag(ExecutionOptions.SkipValidation); + UInt256 nonce = WorldState.GetNonce(tx.SenderAddress); + if (validate && tx.Nonce != nonce) { TraceLogInvalidTx(tx, $"WRONG_TRANSACTION_NONCE: {tx.Nonce} (expected {WorldState.GetNonce(tx.SenderAddress)})"); return TransactionResult.WrongTransactionNonce; } - WorldState.IncrementNonce(tx.SenderAddress); + UInt256 newNonce = validate || nonce < ulong.MaxValue ? nonce + 1 : 0; + WorldState.SetNonce(tx.SenderAddress, newNonce); + return TransactionResult.Ok; } @@ -918,14 +922,33 @@ protected virtual long CalculateClaimableRefund(long spentGas, long totalRefund, private static void ThrowInvalidDataException(string message) => throw new InvalidDataException(message); } - public readonly struct TransactionResult(string? error, EvmExceptionType evmException = EvmExceptionType.None) : IEquatable + public readonly struct TransactionResult : IEquatable { - [MemberNotNullWhen(false, nameof(TransactionExecuted))] - public string? Error { get; } = error; - public bool TransactionExecuted => Error is null; - public EvmExceptionType EvmExceptionType { get; } = evmException; + private TransactionResult(ErrorType error = ErrorType.None, EvmExceptionType evmException = EvmExceptionType.None) + { + Error = error; + EvmExceptionType = evmException; + } + public ErrorType Error { get; } + public bool TransactionExecuted => Error is ErrorType.None; + public EvmExceptionType EvmExceptionType { get; } - public static implicit operator TransactionResult(string? error) => new(error); + public string ErrorDescription => Error switch + { + ErrorType.BlockGasLimitExceeded => "Block gas limit exceeded", + ErrorType.GasLimitBelowIntrinsicGas => "gas limit below intrinsic gas", + ErrorType.InsufficientMaxFeePerGasForSenderBalance => "insufficient MaxFeePerGas for sender balance", + ErrorType.InsufficientSenderBalance => "insufficient sender balance", + ErrorType.MalformedTransaction => "malformed", + ErrorType.MinerPremiumNegative => "miner premium is negative", + ErrorType.NonceOverflow => "nonce overflow", + ErrorType.SenderHasDeployedCode => "sender has deployed code", + ErrorType.SenderNotSpecified => "sender not specified", + ErrorType.TransactionSizeOverMaxInitCodeSize => "EIP-3860 - transaction size over max init code size", + ErrorType.WrongTransactionNonce => "wrong transaction nonce", + _ => "" + }; + public static implicit operator TransactionResult(ErrorType error) => new(error); public static implicit operator bool(TransactionResult result) => result.TransactionExecuted; public bool Equals(TransactionResult other) => (TransactionExecuted && other.TransactionExecuted) || (Error == other.Error); public static bool operator ==(TransactionResult obj1, TransactionResult obj2) => obj1.Equals(obj2); @@ -933,25 +956,41 @@ public readonly struct TransactionResult(string? error, EvmExceptionType evmExce public override bool Equals(object? obj) => obj is TransactionResult result && Equals(result); public override int GetHashCode() => TransactionExecuted ? 1 : Error.GetHashCode(); - public override string ToString() => Error is not null ? $"Fail : {Error}" : "Success"; + public override string ToString() => Error is not ErrorType.None ? $"Fail : {ErrorDescription}" : "Success"; public static TransactionResult EvmException(EvmExceptionType evmExceptionType) { - return new TransactionResult(null, evmExceptionType); + return new TransactionResult(ErrorType.None, evmExceptionType); } public static readonly TransactionResult Ok = new(); - public static readonly TransactionResult BlockGasLimitExceeded = "Block gas limit exceeded"; - public static readonly TransactionResult GasLimitBelowIntrinsicGas = "gas limit below intrinsic gas"; - public static readonly TransactionResult InsufficientMaxFeePerGasForSenderBalance = "insufficient MaxFeePerGas for sender balance"; - public static readonly TransactionResult InsufficientSenderBalance = "insufficient sender balance"; - public static readonly TransactionResult MalformedTransaction = "malformed"; - public static readonly TransactionResult MinerPremiumNegative = "miner premium is negative"; - public static readonly TransactionResult NonceOverflow = "nonce overflow"; - public static readonly TransactionResult SenderHasDeployedCode = "sender has deployed code"; - public static readonly TransactionResult SenderNotSpecified = "sender not specified"; - public static readonly TransactionResult TransactionSizeOverMaxInitCodeSize = "EIP-3860 - transaction size over max init code size"; - public static readonly TransactionResult WrongTransactionNonce = "wrong transaction nonce"; + public static readonly TransactionResult BlockGasLimitExceeded = ErrorType.BlockGasLimitExceeded; + public static readonly TransactionResult GasLimitBelowIntrinsicGas = ErrorType.GasLimitBelowIntrinsicGas; + public static readonly TransactionResult InsufficientMaxFeePerGasForSenderBalance = ErrorType.InsufficientMaxFeePerGasForSenderBalance; + public static readonly TransactionResult InsufficientSenderBalance = ErrorType.InsufficientSenderBalance; + public static readonly TransactionResult MalformedTransaction = ErrorType.MalformedTransaction; + public static readonly TransactionResult MinerPremiumNegative = ErrorType.MinerPremiumNegative; + public static readonly TransactionResult NonceOverflow = ErrorType.NonceOverflow; + public static readonly TransactionResult SenderHasDeployedCode = ErrorType.SenderHasDeployedCode; + public static readonly TransactionResult SenderNotSpecified = ErrorType.SenderNotSpecified; + public static readonly TransactionResult TransactionSizeOverMaxInitCodeSize = ErrorType.TransactionSizeOverMaxInitCodeSize; + public static readonly TransactionResult WrongTransactionNonce = ErrorType.WrongTransactionNonce; + + public enum ErrorType + { + None, + BlockGasLimitExceeded, + GasLimitBelowIntrinsicGas, + InsufficientMaxFeePerGasForSenderBalance, + InsufficientSenderBalance, + MalformedTransaction, + MinerPremiumNegative, + NonceOverflow, + SenderHasDeployedCode, + SenderNotSpecified, + TransactionSizeOverMaxInitCodeSize, + WrongTransactionNonce, + } } } diff --git a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs index 0a79495ff345..f9f2d10556f1 100644 --- a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs +++ b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs @@ -214,9 +214,9 @@ private TransactionResult TryCallAndRestore( { return CallAndRestore(blockHeader, transaction, treatBlockHeaderAsParentBlock, tracer, components); } - catch (InsufficientBalanceException ex) + catch (InsufficientBalanceException) { - return new TransactionResult(ex.Message); + return TransactionResult.InsufficientSenderBalance; } } @@ -404,7 +404,7 @@ public IEnumerable FindLogs(LogFilter filter, CancellationToken cance { { TransactionExecuted: true } when txResult.EvmExceptionType is not (EvmExceptionType.None or EvmExceptionType.Revert) => txResult.EvmExceptionType.GetEvmExceptionDescription(), { TransactionExecuted: true } when tracerError is not null => tracerError, - { TransactionExecuted: false, Error: not null } => txResult.Error, + { TransactionExecuted: false, Error: not TransactionResult.ErrorType.None } => txResult.ErrorDescription, _ => null }; diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs index 275e9a001f6d..e38418c446da 100644 --- a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs @@ -79,7 +79,7 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } - private class JsonConverter : JsonConverter + public class JsonConverter : JsonConverter { public override AuthorizationListForRpc? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateBlockValidationTransactionsExecutor.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateBlockValidationTransactionsExecutor.cs index afea6b3c5d09..38eaf8b8ada3 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateBlockValidationTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateBlockValidationTransactionsExecutor.cs @@ -37,7 +37,7 @@ public TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processing long startingGasLeft = simulateState.TotalGasLeft; if (!simulateState.Validate) { - processingOptions |= ProcessingOptions.ForceProcessing | ProcessingOptions.DoNotVerifyNonce | ProcessingOptions.NoValidation; + processingOptions |= ProcessingOptions.ForceProcessing | ProcessingOptions.NoValidation; } var result = baseTransactionExecutor.ProcessTransactions(block, processingOptions, receiptsTracer, token); diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs index 3b5cadf7c264..feb2722394d3 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs @@ -19,9 +19,8 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading; -using Nethermind.Consensus; using Nethermind.Evm.State; -using Nethermind.Logging; +using Nethermind.Evm.TransactionProcessing; using Transaction = Nethermind.Core.Transaction; namespace Nethermind.Facade.Simulate; @@ -38,6 +37,7 @@ private void PrepareState( BlockStateCall blockStateCall, IWorldState stateProvider, IOverridableCodeInfoRepository codeInfoRepository, + long blockNumber, IReleaseSpec releaseSpec) { stateProvider.ApplyStateOverridesNoCommit(codeInfoRepository, blockStateCall.StateOverrides, releaseSpec); @@ -49,7 +49,8 @@ private void PrepareState( stateProvider.CreateAccountIfNotExists(address, 0, 0); } - stateProvider.Commit(releaseSpec, commitRoots: false); + stateProvider.Commit(releaseSpec, commitRoots: true); + stateProvider.CommitTree(blockNumber); } public SimulateOutput TrySimulate( @@ -68,31 +69,38 @@ public SimulateOutput TrySimulate( try { - if (!TrySimulate(parent, payload, tracer, env, list, gasCapLimit, cancellationToken, out string? error)) - { - result.Error = error; - } + Simulate(parent, payload, tracer, env, list, gasCapLimit, cancellationToken); + } + catch (ArgumentException ex) + { + result.Error = ex.Message; + result.IsInvalidOutput = true; + } + catch (InvalidTransactionException ex) + { + result.Error = ex.Reason.ErrorDescription; + result.TransactionResult = ex.Reason; } catch (InsufficientBalanceException ex) { result.Error = ex.Message; + result.TransactionResult = TransactionResult.InsufficientSenderBalance; } catch (Exception ex) { - result.Error = ex.ToString(); + result.Error = ex.Message; } return result; } - private bool TrySimulate(BlockHeader parent, + private void Simulate(BlockHeader parent, SimulatePayload payload, IBlockTracer tracer, SimulateReadOnlyBlocksProcessingScope env, List> output, long gasCapLimit, - CancellationToken cancellationToken, - [NotNullWhen(false)] out string? error) + CancellationToken cancellationToken) { IBlockTree blockTree = env.BlockTree; IWorldState stateProvider = env.WorldState; @@ -102,7 +110,7 @@ private bool TrySimulate(BlockHeader parent, if (payload.BlockStateCalls is not null) { - Dictionary nonceCache = new(); + Dictionary nonceCache = new(); IBlockTracer cancellationBlockTracer = tracer.WithCancellation(cancellationToken); foreach (BlockStateCall blockCall in payload.BlockStateCalls) @@ -118,11 +126,11 @@ private bool TrySimulate(BlockHeader parent, .Select((c) => c.HadGasLimitInRequest) .ToArray(); + PrepareState(blockCall, env.WorldState, env.CodeInfoRepository, callHeader.Number, spec); + BlockBody body = AssembleBody(calls, stateProvider, nonceCache, spec); Block callBlock = new Block(callHeader, body); - PrepareState(blockCall, env.WorldState, env.CodeInfoRepository, spec); - ProcessingOptions processingFlags = payload.Validation ? SimulateProcessingOptions : SimulateProcessingOptions | ProcessingOptions.NoValidation; @@ -155,15 +163,12 @@ private bool TrySimulate(BlockHeader parent, parent = processedBlock.Header; } } - - error = null; - return true; } private BlockBody AssembleBody( TransactionWithSourceDetails[] calls, IWorldState stateProvider, - Dictionary nonceCache, + Dictionary nonceCache, IReleaseSpec spec) { Transaction[] transactions = calls @@ -208,19 +213,19 @@ private static BlockHeader GetParent(BlockHeader parent, SimulatePayload nonceCache) + Dictionary nonceCache) { Transaction? transaction = transactionDetails.Transaction; transaction.SenderAddress ??= Address.Zero; if (!transactionDetails.HadNonceInRequest) { - ref UInt256 cachedNonce = ref CollectionsMarshal.GetValueRefOrAddDefault(nonceCache, transaction.SenderAddress, out bool exist); + ref ulong cachedNonce = ref CollectionsMarshal.GetValueRefOrAddDefault(nonceCache, transaction.SenderAddress, out bool exist); if (!exist) { if (stateProvider.TryGetAccount(transaction.SenderAddress, out AccountStruct test)) { - cachedNonce = test.Nonce; + cachedNonce = test.Nonce.ToUInt64(null); } // else // Todo think if we shall create account here } @@ -256,7 +261,7 @@ private Transaction CreateTransaction( [], requestsHash: parent.RequestsHash) { - MixHash = parent.MixHash, + MixHash = Hash256.Zero, RequestsHash = parent.RequestsHash, }; diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs index ea2f6f219b19..0c9b6e98f56b 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs @@ -28,13 +28,15 @@ public void Delete(long blockNumber, Hash256 blockHash) _blockNumDict.Remove(blockNumber); } - public Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true) + public Block? Get(long blockNumber, Hash256 blockHash, out bool fromCache, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true) { if (_blockNumDict.TryGetValue(blockNumber, out Block block)) { + fromCache = true; return block; } + fromCache = false; block = readonlyBaseBlockStore.Get(blockNumber, blockHash, rlpBehaviors, false); if (block is not null && shouldCache) { @@ -59,7 +61,7 @@ public void Delete(long blockNumber, Hash256 blockHash) { using NettyRlpStream newRlp = _blockDecoder.EncodeToNewNettyStream(block); using var memoryManager = new CappedArrayMemoryManager(newRlp.Data); - return BlockDecoder.DecodeToReceiptRecoveryBlock(memoryManager, memoryManager.Memory, RlpBehaviors.None); + return _blockDecoder.DecodeToReceiptRecoveryBlock(memoryManager, memoryManager.Memory, RlpBehaviors.None); } return readonlyBaseBlockStore.GetReceiptRecoveryBlock(blockNumber, blockHash); } diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryHeaderStore.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryHeaderStore.cs index bcc2487ed0a1..ea9d0a80bc42 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryHeaderStore.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryHeaderStore.cs @@ -34,19 +34,21 @@ public void BulkInsert(IReadOnlyList headers) } } - public BlockHeader? Get(Hash256 blockHash, bool shouldCache = false, long? blockNumber = null) + public BlockHeader? Get(Hash256 blockHash, out bool fromCache, bool shouldCache = false, long? blockNumber = null) { blockNumber ??= GetBlockNumber(blockHash); if (blockNumber.HasValue && _headerDict.TryGetValue(blockHash, out BlockHeader? header)) { + fromCache = true; if (shouldCache) { - Cache(header); + InsertBlockNumber(header.Hash, header.Number); } return header; } + fromCache = false; header = readonlyBaseHeaderStore.Get(blockHash, false, blockNumber); if (header is not null && shouldCache) { diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateOutput.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateOutput.cs index 04cd2d539674..98f5f5d6449a 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateOutput.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateOutput.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Facade.Proxy.Models.Simulate; namespace Nethermind.Facade.Simulate; @@ -9,7 +10,8 @@ namespace Nethermind.Facade.Simulate; public class SimulateOutput { public string? Error { get; set; } - public int? ErrorCode { get; set; } + public bool IsInvalidOutput { get; set; } + public TransactionResult TransactionResult { get; set; } public IReadOnlyList> Items { get; init; } } diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateTxTracer.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateTxTracer.cs index b2816f4040b0..63504a61e8e9 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateTxTracer.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateTxTracer.cs @@ -51,6 +51,7 @@ public SimulateTxTracer(bool isTracingTransfers, Transaction tx, ulong currentBl public override void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) { base.ReportAction(gas, value, from, to, input, callType, isPrecompileCall); + if (callType == ExecutionType.DELEGATECALL) return; if (value > UInt256.Zero) { var data = AbiEncoder.Instance.Encode(AbiEncodingStyle.Packed, AbiTransferSignature, value); diff --git a/src/Nethermind/Nethermind.Flashbots/Handlers/ValidateBuilderSubmissionHandler.cs b/src/Nethermind/Nethermind.Flashbots/Handlers/ValidateBuilderSubmissionHandler.cs index 78f121aee3b6..513959931054 100644 --- a/src/Nethermind/Nethermind.Flashbots/Handlers/ValidateBuilderSubmissionHandler.cs +++ b/src/Nethermind/Nethermind.Flashbots/Handlers/ValidateBuilderSubmissionHandler.cs @@ -152,7 +152,7 @@ private bool ValidateBlobsBundle(Transaction[] transactions, BlobsBundleV1 blobs IBlobProofsVerifier verifier = IBlobProofsManager.For(releaseSpec.BlobProofVersion); - using ArrayPoolList hashes = new(blobs.Blobs.Length); + using ArrayPoolListRef hashes = new(blobs.Blobs.Length); foreach (Transaction tx in transactions) { diff --git a/src/Nethermind/Nethermind.Init/Modules/BlockProcessingModule.cs b/src/Nethermind/Nethermind.Init/Modules/BlockProcessingModule.cs index 0238c1ae1fe9..83e2614a4e0b 100644 --- a/src/Nethermind/Nethermind.Init/Modules/BlockProcessingModule.cs +++ b/src/Nethermind/Nethermind.Init/Modules/BlockProcessingModule.cs @@ -32,7 +32,7 @@ namespace Nethermind.Init.Modules; -public class BlockProcessingModule(IInitConfig initConfig) : Module +public class BlockProcessingModule(IInitConfig initConfig, IBlocksConfig blocksConfig) : Module { protected override void Load(ContainerBuilder builder) { @@ -84,8 +84,6 @@ protected override void Load(ContainerBuilder builder) .AddSingleton(NullSealEngine.Instance) .AddSingleton(NullSealEngine.Instance) .AddSingleton() - - .AddSingleton() .AddSingleton() .AddSingleton((blockTree, specProvider, logManager, blocksConfig) => @@ -104,6 +102,17 @@ protected override void Load(ContainerBuilder builder) .AddScoped() ; + if (blocksConfig.BuildBlocksOnMainState) + { + builder.AddSingleton() + .AddScoped(); + } + else + { + builder.AddSingleton() + .AddScoped(); + } + if (initConfig.ExitOnInvalidBlock) builder.AddStep(typeof(ExitOnInvalidBlock)); } diff --git a/src/Nethermind/Nethermind.Init/Modules/DiscoveryModule.cs b/src/Nethermind/Nethermind.Init/Modules/DiscoveryModule.cs index 686b85396432..a8088abdae06 100644 --- a/src/Nethermind/Nethermind.Init/Modules/DiscoveryModule.cs +++ b/src/Nethermind/Nethermind.Init/Modules/DiscoveryModule.cs @@ -43,12 +43,13 @@ protected override void Load(ContainerBuilder builder) .AddKeyedSingleton(NodeSourceToDiscV4Feeder.SourceKey, ctx => ctx.Resolve()) // Uses by RPC also. - .AddSingleton((logManager) => new StaticNodesManager(initConfig.StaticNodesPath, logManager)) + .AddSingleton(logManager => + new StaticNodesManager(initConfig.StaticNodesPath.GetApplicationResourcePath(initConfig.DataDir), logManager)) // This load from file. .AddSingleton() - .AddSingleton((logManager) => - new TrustedNodesManager(initConfig.TrustedNodesPath, logManager)) + .AddSingleton(logManager => + new TrustedNodesManager(initConfig.TrustedNodesPath.GetApplicationResourcePath(initConfig.DataDir), logManager)) .Bind() diff --git a/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs b/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs index 9bdfb7ee49f7..19dbd2105bba 100644 --- a/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs +++ b/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs @@ -53,7 +53,7 @@ protected override void Load(ContainerBuilder builder) .AddModule(new RpcModules(configProvider.GetConfig())) .AddModule(new EraModule()) .AddSource(new ConfigRegistrationSource()) - .AddModule(new BlockProcessingModule(configProvider.GetConfig())) + .AddModule(new BlockProcessingModule(configProvider.GetConfig(), configProvider.GetConfig())) .AddModule(new BlockTreeModule(configProvider.GetConfig())) .AddSingleton() diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs index d5bd3ef2df07..32f65f11c8f7 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -16,7 +16,6 @@ using Nethermind.Network.Config; using Nethermind.Network.Contract.P2P; using Nethermind.Network.Discovery; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Stats; using Nethermind.Stats.Model; using Nethermind.Synchronization; @@ -275,14 +274,12 @@ private async Task InitPeer() _api.PeerManager!, _networkConfig, _api.LogManager); - PooledTxsRequestor pooledTxsRequestor = new(_api.TxPool!, _api.Config(), _api.SpecProvider); _api.ProtocolsManager = new ProtocolsManager( _api.SyncPeerPool!, syncServer, _api.BackgroundTaskScheduler, _api.TxPool, - pooledTxsRequestor, _discoveryApp, _api.MessageSerializationService, _api.RlpxPeer, @@ -292,8 +289,9 @@ private async Task InitPeer() _forkInfo, _api.GossipPolicy, _api.WorldStateManager!, - _api.BlockTree, _api.LogManager, + _api.Config(), + _api.SpecProvider, _api.TxGossipPolicy); if (_syncConfig.SnapServingEnabled == true) diff --git a/src/Nethermind/Nethermind.Init/Steps/StartBlockProducer.cs b/src/Nethermind/Nethermind.Init/Steps/StartBlockProducer.cs index 4dfbe0dd5aa6..b07ed09a9244 100644 --- a/src/Nethermind/Nethermind.Init/Steps/StartBlockProducer.cs +++ b/src/Nethermind/Nethermind.Init/Steps/StartBlockProducer.cs @@ -1,12 +1,13 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Threading; -using System.Threading.Tasks; +using Autofac; using Nethermind.Api; using Nethermind.Api.Steps; using Nethermind.Consensus.Producers; using Nethermind.Logging; +using System.Threading; +using System.Threading.Tasks; namespace Nethermind.Init.Steps { @@ -30,8 +31,7 @@ public Task Execute(CancellationToken _) ILogger logger = _api.LogManager.GetClassLogger(); if (logger.IsInfo) logger.Info($"Starting {_api.SealEngineType} block producer & sealer"); - ProducedBlockSuggester suggester = new(_api.BlockTree, _api.BlockProducerRunner); - _api.DisposeStack.Push(suggester); + _api.Context.ResolveOptional(); _api.BlockProducerRunner.Start(); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Data/BlockParameterConverterTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Data/BlockParameterConverterTests.cs index 7629c2faa71b..456a057be7be 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Data/BlockParameterConverterTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Data/BlockParameterConverterTests.cs @@ -1,10 +1,12 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; +using FluentAssertions; using Nethermind.Blockchain.Find; using Nethermind.Core.Test.Builders; using Nethermind.Serialization.Json; - +using NSubstitute.ExceptionExtensions; using NUnit.Framework; namespace Nethermind.JsonRpc.Test.Data @@ -29,6 +31,27 @@ public void Can_read_block_number(string input, long output) Assert.That(blockParameter.BlockNumber, Is.EqualTo(output)); } + [TestCase("0", true)] + [TestCase("100", true)] + [TestCase("\"0x\"", false)] + [TestCase("\"0x0\"", false)] + [TestCase("\"0xA\"", false)] + [TestCase("\"0xa\"", false)] + [TestCase("\"0\"", true)] + [TestCase("\"100\"", true)] + public void Cant_read_block_number_when_strict_hex_format_is_enabled(string input, bool throws) + { + EthereumJsonSerializer.StrictHexFormat = true; + IJsonSerializer serializer = new EthereumJsonSerializer(); + + Func action = () => serializer.Deserialize(input); + + if (throws) + action.Should().Throw(); + else + action.Should().NotThrow(); + } + [TestCase("null", BlockParameterType.Latest)] [TestCase("\"\"", BlockParameterType.Latest)] [TestCase("\"latest\"", BlockParameterType.Latest)] diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Eip1186/ProofConverterTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Eip1186/ProofConverterTests.cs index b720c8ab7bbd..770b55b4462e 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Eip1186/ProofConverterTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Eip1186/ProofConverterTests.cs @@ -50,7 +50,7 @@ public void Storage_proofs_have_values_set_complex_3_setup() tree.Accept(accountProofCollector, tree.RootHash); AccountProof proof = accountProofCollector.BuildResult(); - TestToJson(proof, "{\"accountProof\":[\"0xe215a08c9a7cdf08d4425c138ef5ba3d1f6c2ae18786fe88d9a56230a00b3e83367b25\",\"0xf8518080808080a017934c1c90ce30ca48f32b4f449dab308cfba803fd6d142cab01eb1fad1b70038080808080808080a02352504a0cd6095829b18bae394d0c882d84eead7be5b6ad0a87daaff9d2fb4a8080\",\"0xf869a020227dead52ea912e013e7641ccd6b3b174498e55066b0c174a09c8c3cc4bf5eb846f8448001a0b2375a34ff2c8037d9ff04ebc16367b51d156d9a905ca54cef50bfad1a4c0711a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"],\"address\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"balance\":\"0x1\",\"codeHash\":\"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\",\"nonce\":\"0x0\",\"storageHash\":\"0xb2375a34ff2c8037d9ff04ebc16367b51d156d9a905ca54cef50bfad1a4c0711\",\"storageProof\":[{\"key\":\"0x000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaaaa\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf85180a0aec9a5fc3ba2ebedf137fbcf6987b303c9d8718f75253e6e2444b81a4049e5b980808080808080808080a0397f22cb0ad24543caffaad031e3a0a538e5d8ac106d8f6858a703d442c0e4d380808080\",\"0xf84ca02046d62176084b9d1eace1c8bcc2353228d10569a500ccfd1bdbd8c093f4b4e9aaa9ab12000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab12000000000000000000000000000000000000000000000000000000000000000000000000000000\"},{\"key\":\"0x0000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf85180a0aec9a5fc3ba2ebedf137fbcf6987b303c9d8718f75253e6e2444b81a4049e5b980808080808080808080a0397f22cb0ad24543caffaad031e3a0a538e5d8ac106d8f6858a703d442c0e4d380808080\",\"0xf84ca020cc5921315fe8a051acdea77b782ecb469470d4e94248cb4ff49a1ff26fe982aaa9ab34000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab34000000000000000000000000000000000000000000000000000000000000000000000000000000\"},{\"key\":\"0x00000000001ccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf84ca0383dacaf928fae678bb3ccca17c746816e9bcfc5628853ae37c67b2d3bbaad32aaa9ab56000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab56000000000000000000000000000000000000000000000000000000000000000000000000000000\"},{\"key\":\"0x00000000001ddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf84ca031c208aebac39ba1b4503fa46a491619019fc1a910b3bfe3c78dc3da4abdb097aaa9ab78000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab78000000000000000000000000000000000000000000000000000000000000000000000000000000\"},{\"key\":\"0x00000000001eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf84ca03ca9062cf1266ae3ad77045eb67b33d4c9a4f5e2be44c79cad975ace0bc6ed22aaa9ab9a000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab9a000000000000000000000000000000000000000000000000000000000000000000000000000000\"}]}"); + TestToJson(proof, "{\"accountProof\":[\"0xe215a08c9a7cdf08d4425c138ef5ba3d1f6c2ae18786fe88d9a56230a00b3e83367b25\",\"0xf8518080808080a017934c1c90ce30ca48f32b4f449dab308cfba803fd6d142cab01eb1fad1b70038080808080808080a02352504a0cd6095829b18bae394d0c882d84eead7be5b6ad0a87daaff9d2fb4a8080\",\"0xf869a020227dead52ea912e013e7641ccd6b3b174498e55066b0c174a09c8c3cc4bf5eb846f8448001a0b2375a34ff2c8037d9ff04ebc16367b51d156d9a905ca54cef50bfad1a4c0711a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"],\"address\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"balance\":\"0x1\",\"codeHash\":\"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\",\"nonce\":\"0x0\",\"storageHash\":\"0xb2375a34ff2c8037d9ff04ebc16367b51d156d9a905ca54cef50bfad1a4c0711\",\"storageProof\":[{\"key\":\"0xaaaaaaaaaaaaaaaaaaaaaa\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf85180a0aec9a5fc3ba2ebedf137fbcf6987b303c9d8718f75253e6e2444b81a4049e5b980808080808080808080a0397f22cb0ad24543caffaad031e3a0a538e5d8ac106d8f6858a703d442c0e4d380808080\",\"0xf84ca02046d62176084b9d1eace1c8bcc2353228d10569a500ccfd1bdbd8c093f4b4e9aaa9ab12000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab12000000000000000000000000000000000000000000000000000000000000000000000000000000\"},{\"key\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf85180a0aec9a5fc3ba2ebedf137fbcf6987b303c9d8718f75253e6e2444b81a4049e5b980808080808080808080a0397f22cb0ad24543caffaad031e3a0a538e5d8ac106d8f6858a703d442c0e4d380808080\",\"0xf84ca020cc5921315fe8a051acdea77b782ecb469470d4e94248cb4ff49a1ff26fe982aaa9ab34000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab34000000000000000000000000000000000000000000000000000000000000000000000000000000\"},{\"key\":\"0x1ccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf84ca0383dacaf928fae678bb3ccca17c746816e9bcfc5628853ae37c67b2d3bbaad32aaa9ab56000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab56000000000000000000000000000000000000000000000000000000000000000000000000000000\"},{\"key\":\"0x1ddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf84ca031c208aebac39ba1b4503fa46a491619019fc1a910b3bfe3c78dc3da4abdb097aaa9ab78000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab78000000000000000000000000000000000000000000000000000000000000000000000000000000\"},{\"key\":\"0x1eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf84ca03ca9062cf1266ae3ad77045eb67b33d4c9a4f5e2be44c79cad975ace0bc6ed22aaa9ab9a000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab9a000000000000000000000000000000000000000000000000000000000000000000000000000000\"}]}"); } [Test] @@ -83,7 +83,7 @@ public void Does_not_fail_when_proofs_are_longer_than_number_of_proofs_regressio tree.Accept(accountProofCollector, tree.RootHash); AccountProof proof = accountProofCollector.BuildResult(); - TestToJson(proof, "{\"accountProof\":[\"0xe215a08c9a7cdf08d4425c138ef5ba3d1f6c2ae18786fe88d9a56230a00b3e83367b25\",\"0xf8518080808080a017934c1c90ce30ca48f32b4f449dab308cfba803fd6d142cab01eb1fad1b70038080808080808080a02352504a0cd6095829b18bae394d0c882d84eead7be5b6ad0a87daaff9d2fb4a8080\",\"0xf869a020227dead52ea912e013e7641ccd6b3b174498e55066b0c174a09c8c3cc4bf5eb846f8448001a0b2375a34ff2c8037d9ff04ebc16367b51d156d9a905ca54cef50bfad1a4c0711a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"],\"address\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"balance\":\"0x1\",\"codeHash\":\"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\",\"nonce\":\"0x0\",\"storageHash\":\"0xb2375a34ff2c8037d9ff04ebc16367b51d156d9a905ca54cef50bfad1a4c0711\",\"storageProof\":[{\"key\":\"0x000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaaaa\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf85180a0aec9a5fc3ba2ebedf137fbcf6987b303c9d8718f75253e6e2444b81a4049e5b980808080808080808080a0397f22cb0ad24543caffaad031e3a0a538e5d8ac106d8f6858a703d442c0e4d380808080\",\"0xf84ca02046d62176084b9d1eace1c8bcc2353228d10569a500ccfd1bdbd8c093f4b4e9aaa9ab12000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab12000000000000000000000000000000000000000000000000000000000000000000000000000000\"}]}"); + TestToJson(proof, "{\"accountProof\":[\"0xe215a08c9a7cdf08d4425c138ef5ba3d1f6c2ae18786fe88d9a56230a00b3e83367b25\",\"0xf8518080808080a017934c1c90ce30ca48f32b4f449dab308cfba803fd6d142cab01eb1fad1b70038080808080808080a02352504a0cd6095829b18bae394d0c882d84eead7be5b6ad0a87daaff9d2fb4a8080\",\"0xf869a020227dead52ea912e013e7641ccd6b3b174498e55066b0c174a09c8c3cc4bf5eb846f8448001a0b2375a34ff2c8037d9ff04ebc16367b51d156d9a905ca54cef50bfad1a4c0711a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"],\"address\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"balance\":\"0x1\",\"codeHash\":\"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\",\"nonce\":\"0x0\",\"storageHash\":\"0xb2375a34ff2c8037d9ff04ebc16367b51d156d9a905ca54cef50bfad1a4c0711\",\"storageProof\":[{\"key\":\"0xaaaaaaaaaaaaaaaaaaaaaa\",\"proof\":[\"0xf8918080a02474007b0486d5e951fe3fbcdae3e63cadf9c85cb8f178d3c7ca972e2d77705a808080808080a059c4fa21a1e4c40dc3c87e925befbb78a5b6f729865a12f6f0490d9801bcbf22a06b3576bbd6c91ca7128f69f728f3e30bf8980c6381430a5e80186d0dfec89d4e8080a098cfc3bf071c19a2e230165f4152bb98a5d1ab0fee47c952de65da85fcbdfdb2808080\",\"0xf85180a0aec9a5fc3ba2ebedf137fbcf6987b303c9d8718f75253e6e2444b81a4049e5b980808080808080808080a0397f22cb0ad24543caffaad031e3a0a538e5d8ac106d8f6858a703d442c0e4d380808080\",\"0xf84ca02046d62176084b9d1eace1c8bcc2353228d10569a500ccfd1bdbd8c093f4b4e9aaa9ab12000000000000000000000000000000000000000000000000000000000000000000000000000000\"],\"value\":\"0xab12000000000000000000000000000000000000000000000000000000000000000000000000000000\"}]}"); } } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs index f341a7f90932..6b411197896b 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs @@ -125,7 +125,7 @@ public async Task Get_raw_Header() debugBridge.GetBlock(new BlockParameter((long)0)).Returns(blk); DebugRpcModule rpcModule = CreateDebugRpcModule(debugBridge); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawHeader", 0) as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawHeader", "0x") as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -152,7 +152,7 @@ public async Task Get_rawblock() localDebugBridge.GetBlockRlp(new BlockParameter(1)).Returns(rlp.Bytes); DebugRpcModule rpcModule = CreateDebugRpcModule(localDebugBridge); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", 1) as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", "0x1") as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -188,7 +188,7 @@ public async Task Get_rawblock_when_missing() debugBridge.GetBlockRlp(new BlockParameter(1)).ReturnsNull(); DebugRpcModule rpcModule = CreateDebugRpcModule(debugBridge); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", 1) as JsonRpcErrorResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", "0x1") as JsonRpcErrorResponse; Assert.That(response?.Error?.Code, Is.EqualTo(-32001)); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs index 4566561182f6..379c644d9e3c 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs @@ -38,7 +38,7 @@ public async Task Eth_estimateGas_web3_should_return_insufficient_balance_error( string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That( - serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"insufficient funds for transfer: address 0x0001020304050607080910111213141516171819\"},\"id\":67}")); + serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"insufficient sender balance\"},\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs index 891d1b2dbb71..6fcbea5cd21c 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs @@ -70,7 +70,7 @@ public async Task Eth_call_web3_should_return_insufficient_balance_error() "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500, \"gas\": 100000000}"); string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That( - serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"insufficient funds for transfer: address 0x0001020304050607080910111213141516171819\"},\"id\":67}")); + serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"insufficient sender balance\"},\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs index a8e87bf69664..a6c3a09e5218 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs @@ -174,7 +174,7 @@ public async Task Eth_getBlockReceipts() public async Task Eth_get_transaction_by_block_number_and_index() { using Context ctx = await Context.Create(); - string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByBlockNumberAndIndex", ctx.Test.BlockTree.FindHeadBlock()!.Number.ToString(), "1"); + string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByBlockNumberAndIndex", ctx.Test.BlockTree.FindHeadBlock()!.Number.ToHexString(true), "1"); JToken.Parse(serialized).Should().BeEquivalentTo("""{"jsonrpc":"2.0","result":{"hash":"0x7126cf20a0ad8bd51634837d9049615c34c1bff5e1a54e5663f7e23109bff48b","nonce":"0x2","blockHash":"0x29f141925d2d8e357ae5b6040c97aa12d7ac6dfcbe2b20e7b616d8907ac8e1f3","blockNumber":"0x3","transactionIndex":"0x1","from":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","to":"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","chainId":"0x1","type":"0x0","v":"0x25","s":"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb","r":"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd"},"id":67}"""); } @@ -182,7 +182,7 @@ public async Task Eth_get_transaction_by_block_number_and_index() public async Task Eth_get_transaction_by_block_number_and_index_out_of_bounds() { using Context ctx = await Context.Create(); - string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByBlockNumberAndIndex", ctx.Test.BlockTree.FindHeadBlock()!.Number.ToString(), "100"); + string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByBlockNumberAndIndex", ctx.Test.BlockTree.FindHeadBlock()!.Number.ToHexString(true), "100"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":67}")); } @@ -202,7 +202,7 @@ public async Task Eth_get_uncle_by_block_number_and_index(bool eip1559, string e IBlockTree blockTree = Substitute.For(); blockTree.FindBlock((BlockParameter?)null).ReturnsForAnyArgs(block); ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithBlockFinder(blockTree).Build(specProvider); - string serialized = await ctx.Test!.TestEthRpc("eth_getUncleByBlockNumberAndIndex", ctx.Test.BlockTree.FindHeadBlock()!.Number.ToString(), "1"); + string serialized = await ctx.Test!.TestEthRpc("eth_getUncleByBlockNumberAndIndex", ctx.Test.BlockTree.FindHeadBlock()!.Number.ToHexString(true), "1"); Assert.That(serialized, Is.EqualTo(expectedJson), serialized?.Replace("\"", "\\\"")); } @@ -239,7 +239,7 @@ public async Task Eth_get_uncle_count_by_block_hash() public async Task Eth_get_uncle_count_by_block_number() { using Context ctx = await Context.Create(); - string serialized = await ctx.Test.TestEthRpc("eth_getUncleCountByBlockNumber", ctx.Test.BlockTree.FindHeadBlock()!.Number.ToString()); + string serialized = await ctx.Test.TestEthRpc("eth_getUncleCountByBlockNumber", ctx.Test.BlockTree.FindHeadBlock()!.Number.ToHexString(true)); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x0\",\"id\":67}"), serialized.Replace("\"", "\\\"")); } @@ -766,7 +766,7 @@ public async Task Eth_get_block_by_number_should_not_recover_tx_senders_for_requ public async Task Eth_get_block_by_number_null() { using Context ctx = await Context.Create(); - string serialized = await ctx.Test.TestEthRpc("eth_getBlockByNumber", "1000000", "false"); + string serialized = await ctx.Test.TestEthRpc("eth_getBlockByNumber", 1000000.ToHexString(), "false"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":67}")); } @@ -824,7 +824,7 @@ public async Task Eth_get_proof_withTrimmedStorageKey() { using Context ctx = await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getProof", TestBlockchain.AccountA.ToString(), "[\"0x1\"]", "0x2"); - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"accountProof\":[\"0xf8718080808080a0fc8311b2cabe1a1b33ea04f1865132a44aa0c17c567acd233422f9cfb516877480808080a0be8ea164b2fb1567e2505295dae6d8a9fe5f09e9c5ac854a7da23b2bc5f8523ca053692ab7cdc9bb02a28b1f45afe7be86cb27041ea98586e6ff05d98c9b0667138080808080\",\"0xf8518080808080a00dd1727b2abb59c0a6ac75c01176a9d1a276b0049d5fe32da3e1551096549e258080808080808080a038ca33d3070331da1ccf804819da57fcfc83358cadbef1d8bde89e1a346de5098080\",\"0xf872a020227dead52ea912e013e7641ccd6b3b174498e55066b0c174a09c8c3cc4bf5eb84ff84d01893635c9adc5de9fadf7a0475ae75f323761db271e75cbdae41aede237e48bc04127fb6611f0f33298f72ba0dbe576b4818846aa77e82f4ed5fa78f92766b141f282d36703886d196df39322\"],\"address\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"balance\":\"0x3635c9adc5de9fadf7\",\"codeHash\":\"0xdbe576b4818846aa77e82f4ed5fa78f92766b141f282d36703886d196df39322\",\"nonce\":\"0x1\",\"storageHash\":\"0x475ae75f323761db271e75cbdae41aede237e48bc04127fb6611f0f33298f72b\",\"storageProof\":[{\"key\":\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"proof\":[\"0xe7a120b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf68483abcdef\"],\"value\":\"0xabcdef\"}]},\"id\":67}"), serialized.Replace("\"", "\\\"")); + Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"accountProof\":[\"0xf8718080808080a0fc8311b2cabe1a1b33ea04f1865132a44aa0c17c567acd233422f9cfb516877480808080a0be8ea164b2fb1567e2505295dae6d8a9fe5f09e9c5ac854a7da23b2bc5f8523ca053692ab7cdc9bb02a28b1f45afe7be86cb27041ea98586e6ff05d98c9b0667138080808080\",\"0xf8518080808080a00dd1727b2abb59c0a6ac75c01176a9d1a276b0049d5fe32da3e1551096549e258080808080808080a038ca33d3070331da1ccf804819da57fcfc83358cadbef1d8bde89e1a346de5098080\",\"0xf872a020227dead52ea912e013e7641ccd6b3b174498e55066b0c174a09c8c3cc4bf5eb84ff84d01893635c9adc5de9fadf7a0475ae75f323761db271e75cbdae41aede237e48bc04127fb6611f0f33298f72ba0dbe576b4818846aa77e82f4ed5fa78f92766b141f282d36703886d196df39322\"],\"address\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"balance\":\"0x3635c9adc5de9fadf7\",\"codeHash\":\"0xdbe576b4818846aa77e82f4ed5fa78f92766b141f282d36703886d196df39322\",\"nonce\":\"0x1\",\"storageHash\":\"0x475ae75f323761db271e75cbdae41aede237e48bc04127fb6611f0f33298f72b\",\"storageProof\":[{\"key\":\"0x1\",\"proof\":[\"0xe7a120b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf68483abcdef\"],\"value\":\"0xabcdef\"}]},\"id\":67}"), serialized.Replace("\"", "\\\"")); } [Test] @@ -988,9 +988,9 @@ public async Task Eth_get_transaction_receipt(bool postEip4844) string serialized = await ctx.Test.TestEthRpc("eth_getTransactionReceipt", TestItem.KeccakA.ToString()); if (postEip4844) - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"transactionIndex\":\"0x2\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"cumulativeGasUsed\":\"0x3e8\",\"gasUsed\":\"0x64\",\"blobGasUsed\":\"0x3\",\"blobGasPrice\":\"0x2\",\"effectiveGasPrice\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"contractAddress\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]},{\"removed\":false,\"logIndex\":\"0x1\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]}],\"logsBloom\":\"0x00000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000\",\"root\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"status\":\"0x1\",\"error\":\"error\",\"type\":\"0x0\"},\"id\":67}")); + Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"transactionIndex\":\"0x2\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"cumulativeGasUsed\":\"0x3e8\",\"gasUsed\":\"0x64\",\"blobGasUsed\":\"0x3\",\"blobGasPrice\":\"0x2\",\"effectiveGasPrice\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"contractAddress\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]},{\"removed\":false,\"logIndex\":\"0x1\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]}],\"logsBloom\":\"0x00000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000\",\"root\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"error\":\"error\",\"type\":\"0x0\"},\"id\":67}")); else - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"transactionIndex\":\"0x2\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"cumulativeGasUsed\":\"0x3e8\",\"gasUsed\":\"0x64\",\"effectiveGasPrice\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"contractAddress\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]},{\"removed\":false,\"logIndex\":\"0x1\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]}],\"logsBloom\":\"0x00000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000\",\"root\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"status\":\"0x1\",\"error\":\"error\",\"type\":\"0x0\"},\"id\":67}")); + Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"transactionIndex\":\"0x2\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"cumulativeGasUsed\":\"0x3e8\",\"gasUsed\":\"0x64\",\"effectiveGasPrice\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"contractAddress\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]},{\"removed\":false,\"logIndex\":\"0x1\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]}],\"logsBloom\":\"0x00000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000\",\"root\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"error\":\"error\",\"type\":\"0x0\"},\"id\":67}")); } @@ -1099,7 +1099,7 @@ public async Task Eth_getTransactionReceipt_return_info_about_mined_tx() ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithBlockFinder(blockFinder).WithReceiptFinder(receiptFinder).WithBlockchainBridge(blockchainBridge).Build(); string serialized = await ctx.Test.TestEthRpc("eth_getTransactionReceipt", tx.Hash!.ToString()); - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0xda6b4df2595675cbee0d4889f41c3d0790204e8ed1b8ad4cadaa45a7d50dace5\",\"transactionIndex\":\"0x2\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"cumulativeGasUsed\":\"0x3e8\",\"gasUsed\":\"0x64\",\"effectiveGasPrice\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"contractAddress\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]}],\"logsBloom\":\"0x00000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000\",\"root\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"status\":\"0x1\",\"error\":\"error\",\"type\":\"0x0\"},\"id\":67}")); + Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0xda6b4df2595675cbee0d4889f41c3d0790204e8ed1b8ad4cadaa45a7d50dace5\",\"transactionIndex\":\"0x2\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"cumulativeGasUsed\":\"0x3e8\",\"gasUsed\":\"0x64\",\"effectiveGasPrice\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"contractAddress\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"blockTimestamp\":\"0xa\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]}],\"logsBloom\":\"0x00000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000\",\"root\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"error\":\"error\",\"type\":\"0x0\"},\"id\":67}")); } [Test] @@ -1458,7 +1458,7 @@ public async Task eth_getTransactionByHash_returns_correct_values_on_SetCode_tx( public async Task Eth_get_block_by_number_pruned() { using Context ctx = await Context.CreateWithAncientBarriers(10000); - string serialized = await ctx.Test.TestEthRpc("eth_getBlockByNumber", "100", "false"); + string serialized = await ctx.Test.TestEthRpc("eth_getBlockByNumber", "0xF4240", "false"); Assert.That(serialized, Is.EqualTo("""{"jsonrpc":"2.0","result":null,"id":67}""")); } @@ -1466,7 +1466,7 @@ public async Task Eth_get_block_by_number_pruned() public async Task Eth_tx_count_by_number_pruned() { using Context ctx = await Context.CreateWithAncientBarriers(10000); - string serialized = await ctx.Test.TestEthRpc("eth_getBlockTransactionCountByNumber", 100); + string serialized = await ctx.Test.TestEthRpc("eth_getBlockTransactionCountByNumber", "0x64"); Assert.That(serialized, Is.EqualTo("""{"jsonrpc":"2.0","result":null,"id":67}""")); } @@ -1474,7 +1474,7 @@ public async Task Eth_tx_count_by_number_pruned() public async Task Eth_get_uncle_count_by_block_number_pruned() { using Context ctx = await Context.CreateWithAncientBarriers(10000); - string serialized = await ctx.Test.TestEthRpc("eth_getUncleCountByBlockNumber", 100); + string serialized = await ctx.Test.TestEthRpc("eth_getUncleCountByBlockNumber", "0x64"); Assert.That(serialized, Is.EqualTo("""{"jsonrpc":"2.0","result":null,"id":67}""")); } @@ -1482,7 +1482,7 @@ public async Task Eth_get_uncle_count_by_block_number_pruned() public async Task Eth_get_transaction_by_block_number_and_index_pruned() { using Context ctx = await Context.CreateWithAncientBarriers(10000); - string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByBlockNumberAndIndex", 100, "1"); + string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByBlockNumberAndIndex", "0x100", "1"); Assert.That(serialized, Is.EqualTo("""{"jsonrpc":"2.0","result":null,"id":67}""")); } @@ -1526,7 +1526,7 @@ public async Task Eth_get_transaction_by_block_hash_and_index_pruned() public async Task Eth_getBlockReceipts_pruned() { using Context ctx = await Context.CreateWithAncientBarriers(10000); - string serialized = await ctx.Test.TestEthRpc("eth_getBlockReceipts", 100); + string serialized = await ctx.Test.TestEthRpc("eth_getBlockReceipts", "0x100"); Assert.That(serialized, Is.EqualTo("""{"jsonrpc":"2.0","result":null,"id":67}""")); } @@ -1662,7 +1662,9 @@ public static async Task CreateWithAncientBarriers(long blockNumber) }); } - public static Task Create(ISpecProvider? specProvider = null, IBlockchainBridge? blockchainBridge = null, Action? configurer = null) + public static Task Create(ISpecProvider? specProvider = null, + IBlockchainBridge? blockchainBridge = null, + Action? configurer = null) { Action wrappedConfigurer = builder => { @@ -1674,7 +1676,7 @@ public static Task Create(ISpecProvider? specProvider = null, IBlockcha { TestFactory = () => TestRpcBlockchain.ForTest(SealEngineType.NethDev) .WithBlockchainBridge(blockchainBridge!) - .WithConfig(new JsonRpcConfig() { EstimateErrorMargin = 0 }) + .WithConfig(new JsonRpcConfig { EstimateErrorMargin = 0 }) .Build(wrappedConfigurer).Result, AuraTestFactory = () => TestRpcBlockchain.ForTest(SealEngineType.AuRa) diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs index e05324b2d805..64c5f12b8676 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs @@ -128,7 +128,7 @@ private JsonRpcResult GetBlockAddedToMainResult(BlockReplacementEventArgs blockR })); _blockTree.BlockAddedToMain += Raise.EventWith(new object(), blockReplacementEventArgs); - _receiptStorage.ReceiptsInserted += Raise.EventWith(new object(), blockReplacementEventArgs); + _receiptStorage.NewCanonicalReceipts += Raise.EventWith(new object(), blockReplacementEventArgs); manualResetEvent.WaitOne(TimeSpan.FromMilliseconds(1000)).Should().Be(shouldReceiveResult); subscriptionId = newHeadSubscription.Id; @@ -148,7 +148,7 @@ private List GetLogsSubscriptionResult(Filter filter, BlockReplac })); _blockTree.BlockAddedToMain += Raise.EventWith(new object(), blockEventArgs); - _receiptStorage.ReceiptsInserted += Raise.EventWith(new object(), blockEventArgs); + _receiptStorage.NewCanonicalReceipts += Raise.EventWith(new object(), blockEventArgs); semaphoreSlim.Wait(TimeSpan.FromMilliseconds(500)); subscriptionId = logsSubscription.Id; @@ -769,7 +769,7 @@ public void LogsSubscription_should_not_send_logs_of_new_txs_on_ReceiptsInserted BlockReplacementEventArgs blockEventArgs = new(block); _blockTree.BlockAddedToMain += Raise.EventWith(new object(), blockEventArgs); - _receiptStorage.ReceiptsInserted += Raise.EventWith(new object(), blockEventArgs); + _receiptStorage.NewCanonicalReceipts += Raise.EventWith(new object(), blockEventArgs); manualResetEvent.WaitOne(TimeSpan.FromMilliseconds(200)); jsonRpcResults.Count.Should().Be(1); @@ -949,7 +949,7 @@ public async Task MultipleSubscriptions_concurrent_fast_messages(int messages) { BlockReplacementEventArgs eventArgs = new(Build.A.Block.TestObject); blockTree.BlockAddedToMain += Raise.EventWith(eventArgs); - _receiptStorage.ReceiptsInserted += Raise.EventWith(new object(), eventArgs); + _receiptStorage.NewCanonicalReceipts += Raise.EventWith(new object(), eventArgs); } }); @@ -1204,7 +1204,7 @@ public void LogsSubscription_can_send_logs_with_removed_txs_when_inserted() })); _blockTree.BlockAddedToMain += Raise.EventWith(new object(), blockEventArgs); - _receiptStorage.ReceiptsInserted += Raise.EventWith(new object(), blockEventArgs); + _receiptStorage.NewCanonicalReceipts += Raise.EventWith(new object(), blockEventArgs); manualResetEvent.WaitOne(TimeSpan.FromMilliseconds(2000)); jsonRpcResults.Count.Should().Be(1); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs index 9e994e034d9e..4143ecc5a434 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs @@ -1,10 +1,9 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; using System.IO; using System.Threading.Tasks; -using Nethermind.Blockchain.Filters; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Receipts; using Nethermind.Core.Specs; @@ -39,8 +38,8 @@ using Nethermind.JsonRpc.Modules; using Nethermind.JsonRpc.Modules.Trace; using Nethermind.Network; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Network.Rlpx; +using Nethermind.Serialization.Json; using Nethermind.Stats; using Nethermind.Synchronization.ParallelSync; using Nethermind.Synchronization.Peers; @@ -198,6 +197,7 @@ public async Task Build(Action configurer) protected override async Task Build(Action? configurer = null) { + @EthereumJsonSerializer.StrictHexFormat = RpcConfig.StrictHexFormat; await base.Build(builder => { builder.AddSingleton(new TestSpecProvider(Berlin.Instance)); @@ -217,7 +217,6 @@ await base.Build(builder => Substitute.For(), Substitute.For(), TxPool, - Substitute.For(), Substitute.For(), Substitute.For(), Substitute.For(), @@ -227,8 +226,9 @@ await base.Build(builder => Container.Resolve(), Substitute.For(), WorldStateManager, - Substitute.For(), LimboLogs.Instance, + Substitute.For(), + Substitute.For(), Substitute.For() ); diff --git a/src/Nethermind/Nethermind.JsonRpc/Data/ReceiptForRpc.cs b/src/Nethermind/Nethermind.JsonRpc/Data/ReceiptForRpc.cs index 2e097f92799e..9977af4bc73e 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Data/ReceiptForRpc.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Data/ReceiptForRpc.cs @@ -7,6 +7,7 @@ using Nethermind.Core.Crypto; using Nethermind.Evm; using Nethermind.Int256; +using Nethermind.Serialization.Json; namespace Nethermind.JsonRpc.Data { @@ -33,7 +34,7 @@ public ReceiptForRpc(Hash256 txHash, TxReceipt receipt, ulong blockTimestamp, Tx Logs = (receipt.Logs ?? []).Select((l, idx) => new LogEntryForRpc(receipt, l, blockTimestamp, idx + logIndexStart)).ToArray(); LogsBloom = receipt.Bloom; Root = receipt.PostTransactionState; - Status = receipt.StatusCode; + Status = receipt.PostTransactionState is null ? receipt.StatusCode : null; Error = string.IsNullOrEmpty(receipt.Error) ? null : receipt.Error; Type = receipt.TxType; } @@ -62,7 +63,7 @@ public ReceiptForRpc(Hash256 txHash, TxReceipt receipt, ulong blockTimestamp, Tx public LogEntryForRpc[] Logs { get; set; } public Bloom? LogsBloom { get; set; } public Hash256? Root { get; set; } - public long Status { get; set; } + public long? Status { get; set; } public string? Error { get; set; } public TxType Type { get; set; } @@ -80,7 +81,7 @@ public TxReceipt ToReceipt() BlockNumber = BlockNumber, ContractAddress = ContractAddress, GasUsed = GasUsed, - StatusCode = (byte)Status, + StatusCode = Status is not null ? (byte)Status : byte.MinValue, TxHash = TransactionHash, GasUsedTotal = CumulativeGasUsed, PostTransactionState = Root, diff --git a/src/Nethermind/Nethermind.JsonRpc/ErrorCodes.cs b/src/Nethermind/Nethermind.JsonRpc/ErrorCodes.cs index a1233d4d5922..75a58bb63892 100644 --- a/src/Nethermind/Nethermind.JsonRpc/ErrorCodes.cs +++ b/src/Nethermind/Nethermind.JsonRpc/ErrorCodes.cs @@ -126,5 +126,67 @@ public static class ErrorCodes /// Block is not available due to history expirty policy /// public const int PrunedHistoryUnavailable = 4444; + + /// + /// Default error code + /// + public const int Default = -32000; + + /// + /// Transaction.Nonce is bigger than expected nonce + /// + public const int NonceTooHigh = -38011; + + /// + /// Transaction.Nonce is smaller than expected nonce + /// + public const int NonceTooLow = -38010; + + /// + /// Invalid intrinsic gas. Miner premium is negative + /// + public const int IntrinsicGas = -38013; + + /// + /// Not enough value to cover transaction costs + /// + public const int InsufficientFunds = -38014; + + /// + /// Gas limit reached + /// + public const int BlockGasLimitReached = -38015; + + /// + /// Invalid block number + /// + public const int BlockNumberInvalid = -38020; + + /// + /// Invalid block timestamp + /// + public const int BlockTimestampInvalid = -38021; + + /// + /// Transaction.Sender is not an EOA + /// + public const int SenderIsNotEOA = -38024; + + /// + /// EIP-3860. Code size is to big + /// + public const int MaxInitCodeSizeExceeded = -38025; + + /// + /// Transaction reverted. Geth sets it to -32000 in simulate but suppose to 3. We kepp it as geth for now + /// + public const int RevertedSimulate = -32000; + + /// + /// Error during EVM execution + /// + public const int VMError = -32015; + public const int TxSyncTimeout = 4; + public const int ClientLimitExceeded = -38026; } } diff --git a/src/Nethermind/Nethermind.JsonRpc/IJsonRpcConfig.cs b/src/Nethermind/Nethermind.JsonRpc/IJsonRpcConfig.cs index 85a41f240fa4..fa8acd5c50b5 100644 --- a/src/Nethermind/Nethermind.JsonRpc/IJsonRpcConfig.cs +++ b/src/Nethermind/Nethermind.JsonRpc/IJsonRpcConfig.cs @@ -187,4 +187,9 @@ public interface IJsonRpcConfig : IConfig [ConfigItem(Description = "Preload rpc modules. Useful in rpc provider to reduce latency on first request.", DefaultValue = "false")] bool PreloadRpcModules { get; set; } + + [ConfigItem( + Description = "Enable strict parsing rules for Block Params and Hashas in RPC requests. this will decrease compatibility but increase compliance with the spec.", + DefaultValue = "true")] + bool StrictHexFormat { get; set; } } diff --git a/src/Nethermind/Nethermind.JsonRpc/JsonRpcConfig.cs b/src/Nethermind/Nethermind.JsonRpc/JsonRpcConfig.cs index f5ad44064837..3df17371429a 100644 --- a/src/Nethermind/Nethermind.JsonRpc/JsonRpcConfig.cs +++ b/src/Nethermind/Nethermind.JsonRpc/JsonRpcConfig.cs @@ -74,4 +74,5 @@ public string[] EnabledModules public bool EnablePerMethodMetrics { get; set; } = false; public int FiltersTimeout { get; set; } = 900000; public bool PreloadRpcModules { get; set; } + public bool StrictHexFormat { get; set; } = true; }; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs index 11ad84549e30..acfcad66b042 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs @@ -372,7 +372,7 @@ public ResultWrapper debug_resetHead(Hash256 blockHash) RlpBehaviors encodingSettings = RlpBehaviors.SkipTypedWrapping | (transaction.IsInMempoolForm() ? RlpBehaviors.InMempoolForm : RlpBehaviors.None); using NettyRlpStream stream = TxDecoder.Instance.EncodeToNewNettyStream(transaction, encodingSettings); - return ResultWrapper.Success(stream.AsSpan().ToHexString(false)); + return ResultWrapper.Success(stream.AsSpan().ToHexString(true)); } public ResultWrapper debug_getRawReceipts(BlockParameter blockParameter) diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs index d21fb76a783f..4f0265261150 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs @@ -204,8 +204,7 @@ public ResultWrapper GetFeeHistory( TryRunCleanup(); - return ResultWrapper.Success(new(oldestBlockNumber, baseFeePerGas, - gasUsedRatio, baseFeePerBlobGas, blobGasUsedRatio, rewards)); + return ResultWrapper.Success(new(oldestBlockNumber, baseFeePerGas, gasUsedRatio, baseFeePerBlobGas, blobGasUsedRatio, rewards)); } private void TryRunCleanup() @@ -258,7 +257,7 @@ static IEnumerable CalculateGasUsed(TxReceipt[] txReceipts) TxReceipt[] receipts = _receiptStorage.Get(block, false); Transaction[] txs = block.Transactions; - using ArrayPoolList gasUsed = new(txs.Length, receipts.Length == block.Transactions.Length + using ArrayPoolListRef gasUsed = new(txs.Length, receipts.Length == block.Transactions.Length ? CalculateGasUsed(receipts) // If no receipts available, approximate on GasLimit // We could just go with null here too and just don't return percentiles diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs index 2a9cb5b7922d..3330a296897f 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs @@ -10,6 +10,7 @@ using Nethermind.Core; using Nethermind.Core.Collections; using Nethermind.Evm; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Facade; using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models.Simulate; @@ -133,7 +134,7 @@ public override ResultWrapper>> Execut long lastBlockNumber = header.Number; ulong lastBlockTime = header.Timestamp; - using ArrayPoolList> completeBlockStateCalls = new(call.BlockStateCalls.Count); + using ArrayPoolListRef> completeBlockStateCalls = new(call.BlockStateCalls.Count); foreach (BlockStateCall? blockToSimulate in call.BlockStateCalls) { @@ -216,22 +217,42 @@ protected override ResultWrapper>> Exe } } - if (results.Error is not null) + int? errorCode = results.TransactionResult.TransactionExecuted + ? null + : (int)MapSimulateErrorCode(results.TransactionResult); + if (results.IsInvalidOutput) errorCode = ErrorCodes.Default; + return results.Error is null + ? ResultWrapper>>.Success([.. results.Items]) + : errorCode is not null + ? ResultWrapper>>.Fail(results.Error!, errorCode.Value) + : ResultWrapper>>.Fail(results.Error); + } + + private int MapSimulateErrorCode(TransactionResult txResult) + { + if (txResult.Error != TransactionResult.ErrorType.None) { - results.ErrorCode = results.Error switch + return txResult.Error switch { - var e when e.Contains("invalid transaction", StringComparison.OrdinalIgnoreCase) => ErrorCodes.InvalidTransaction, - var e when e.Contains("InsufficientBalanceException", StringComparison.OrdinalIgnoreCase) => ErrorCodes.InvalidTransaction, - var e when e.Contains("InvalidBlockException", StringComparison.OrdinalIgnoreCase) => ErrorCodes.InvalidParams, - var e when e.Contains("below intrinsic gas", StringComparison.OrdinalIgnoreCase) => ErrorCodes.InsufficientIntrinsicGas, - _ => results.ErrorCode + TransactionResult.ErrorType.BlockGasLimitExceeded => ErrorCodes.BlockGasLimitReached, + TransactionResult.ErrorType.GasLimitBelowIntrinsicGas => ErrorCodes.IntrinsicGas, + TransactionResult.ErrorType.InsufficientMaxFeePerGasForSenderBalance + or TransactionResult.ErrorType.InsufficientSenderBalance => ErrorCodes.InsufficientFunds, + TransactionResult.ErrorType.MalformedTransaction => ErrorCodes.InternalError, + TransactionResult.ErrorType.MinerPremiumNegative => ErrorCodes.InvalidParams, + TransactionResult.ErrorType.NonceOverflow => ErrorCodes.InternalError, + TransactionResult.ErrorType.SenderHasDeployedCode => ErrorCodes.InvalidParams, + TransactionResult.ErrorType.SenderNotSpecified => ErrorCodes.InternalError, + TransactionResult.ErrorType.TransactionSizeOverMaxInitCodeSize => ErrorCodes.MaxInitCodeSizeExceeded, + TransactionResult.ErrorType.WrongTransactionNonce => ErrorCodes.InternalError, + _ => ErrorCodes.InternalError }; } - return results.Error is null - ? ResultWrapper>>.Success([.. results.Items]) - : results.ErrorCode is not null - ? ResultWrapper>>.Fail(results.Error!, results.ErrorCode!.Value) - : ResultWrapper>>.Fail(results.Error); + return txResult.EvmExceptionType switch + { + EvmExceptionType.Revert => ErrorCodes.RevertedSimulate, + _ => ErrorCodes.VMError + }; } } diff --git a/src/Nethermind/Nethermind.KeyStore.Test/CryptoRandomTests.cs b/src/Nethermind/Nethermind.KeyStore.Test/CryptoRandomTests.cs new file mode 100644 index 000000000000..7c7bbd91f879 --- /dev/null +++ b/src/Nethermind/Nethermind.KeyStore.Test/CryptoRandomTests.cs @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Linq; +using Nethermind.Crypto; +using NUnit.Framework; + +namespace Nethermind.KeyStore.Test +{ + [TestFixture] + [Parallelizable(ParallelScope.All)] + public class CryptoRandomTests + { + [Test] + public void NextInt_ReturnsValueWithinBounds() + { + using CryptoRandom rng = new(); + + int[] bounds = { 1, 2, 10, 100, 1024 }; + foreach (int max in bounds) + { + int value = rng.NextInt(max); + Assert.That(value, Is.GreaterThanOrEqualTo(0).And.LessThan(max)); + } + } + + [Test] + public void NextInt_ZeroOrNegative_ReturnsZero() + { + using CryptoRandom rng = new(); + + Assert.That(rng.NextInt(0), Is.EqualTo(0)); + Assert.That(rng.NextInt(-1), Is.EqualTo(0)); + } + + [Test] + public void GenerateRandomBytes_FillsAndVariesBetweenCalls() + { + using CryptoRandom rng = new(); + + byte[] a = rng.GenerateRandomBytes(32); + byte[] b = rng.GenerateRandomBytes(32); + + // Ensure arrays are correct length and not all zeros + Assert.That(a.Length, Is.EqualTo(32)); + Assert.That(b.Length, Is.EqualTo(32)); + Assert.That(a.Any(x => x != 0), Is.True); + Assert.That(b.Any(x => x != 0), Is.True); + + // Extremely unlikely to fail: values should differ + Assert.That(a.SequenceEqual(b), Is.False); + } + + [Test] + public void GenerateRandomBytes_SpanFilled() + { + using CryptoRandom rng = new(); + + Span span = stackalloc byte[16]; + rng.GenerateRandomBytes(span); + + Assert.That(span.ToArray().Any(x => x != 0), Is.True); + } + } +} diff --git a/src/Nethermind/Nethermind.Logging/SimpleConsoleLogger.cs b/src/Nethermind/Nethermind.Logging/SimpleConsoleLogger.cs index c80b2196911e..6b732fccb1bf 100644 --- a/src/Nethermind/Nethermind.Logging/SimpleConsoleLogger.cs +++ b/src/Nethermind/Nethermind.Logging/SimpleConsoleLogger.cs @@ -34,7 +34,7 @@ public void Trace(string text) public void Error(string text, Exception ex = null) { - if (IsError) Console.Error.WriteLine(DateTime.Now.ToString(dateFormat) + text); + if (IsError) Console.Error.WriteLine(DateTime.Now.ToString(dateFormat) + text + (ex is not null ? " " + ex : string.Empty)); } private void WriteEntry(string text) diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Synchronization.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Synchronization.cs index 39bfda22bc42..0d04a8ac4279 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Synchronization.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Synchronization.cs @@ -729,6 +729,8 @@ public async Task Blocks_from_cache_inserted_when_fast_headers_sync_finish_befor } [Test] + [CancelAfter(5000)] + [Retry(3)] public async Task Maintain_correct_pointers_for_beacon_sync_in_archive_sync() { using MergeTestBlockchain chain = await CreateBlockchain(); diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/ExternalRpcIntegrationTests.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/ExternalRpcIntegrationTests.cs index 1317e380c1be..5f3f43eed00c 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/ExternalRpcIntegrationTests.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/ExternalRpcIntegrationTests.cs @@ -1,15 +1,16 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Threading.Tasks; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Facade.Eth; using Nethermind.Int256; -using Nethermind.Overseer.Test.JsonRpc; +using Nethermind.JsonRpc.Client; +using Nethermind.Logging; using Nethermind.Serialization.Json; -using Newtonsoft.Json.Linq; using NUnit.Framework; +using System; +using System.Threading.Tasks; namespace Nethermind.Merge.Plugin.Test; @@ -28,13 +29,12 @@ public async Task CanonicalTreeIsConsistent() int destinationBlockNumber = 5000; long? currentBlockNumber = null; Hash256? currentHash = null; - JsonRpcClient? client = new($"http://127.0.0.1:8545"); + BasicJsonRpcClient client = new(new Uri("http://127.0.0.1:8545"), jsonSerializer, LimboLogs.Instance); do { string? requestedBlockNumber = currentBlockNumber is null ? "latest" : currentBlockNumber.Value.ToHexString(false); - JsonRpcResponse? requestResponse = - await client.PostAsync("eth_getBlockByNumber", new object[] { requestedBlockNumber!, false }); - BlockForRpcForTest? block = jsonSerializer.Deserialize(requestResponse.Result.ToString()); + BlockForRpcForTest block = + await client.Post("eth_getBlockByNumber", [requestedBlockNumber!, false]); if (currentHash is not null) { Assert.That(block.Hash, Is.EqualTo(currentHash), $"incorrect block hash found {block}"); @@ -53,13 +53,12 @@ public async Task ParentTimestampIsAlwaysLowerThanChildTimestamp() int destinationBlockNumber = 5000; long? currentBlockNumber = null; UInt256? childTimestamp = null; - JsonRpcClient? client = new($"http://127.0.0.1:8545"); + BasicJsonRpcClient client = new(new Uri("http://127.0.0.1:8545"), jsonSerializer, LimboLogs.Instance); do { string? requestedBlockNumber = currentBlockNumber is null ? "latest" : currentBlockNumber.Value.ToHexString(false); - JsonRpcResponse? requestResponse = - await client.PostAsync("eth_getBlockByNumber", [requestedBlockNumber!, false]); - BlockForRpcForTest? block = jsonSerializer.Deserialize(requestResponse.Result.ToString()); + BlockForRpcForTest block = + await client.Post("eth_getBlockByNumber", [requestedBlockNumber!, false]); if (childTimestamp is not null) { Assert.That(childTimestamp, Is.GreaterThan(block.Timestamp), $"incorrect timestamp for block {block}"); diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/Nethermind.Merge.Plugin.Test.csproj b/src/Nethermind/Nethermind.Merge.Plugin.Test/Nethermind.Merge.Plugin.Test.csproj index 25f4cddc1a05..353b11485688 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/Nethermind.Merge.Plugin.Test.csproj +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/Nethermind.Merge.Plugin.Test.csproj @@ -14,7 +14,6 @@ - diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/ProcessedTransactionsDbCleanerTests.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/ProcessedTransactionsDbCleanerTests.cs index 852e422d4bbf..8ad968b4305e 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/ProcessedTransactionsDbCleanerTests.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/ProcessedTransactionsDbCleanerTests.cs @@ -5,6 +5,7 @@ using FluentAssertions; using Nethermind.Blockchain; using Nethermind.Core; +using Nethermind.Core.Collections; using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; @@ -40,9 +41,10 @@ Transaction GetTx(PrivateKey sender) IColumnsDb columnsDb = new MemColumnsDb(BlobTxsColumns.ProcessedTxs); BlobTxStorage blobTxStorage = new(columnsDb); - Transaction[] txs = { GetTx(TestItem.PrivateKeyA), GetTx(TestItem.PrivateKeyB) }; - - blobTxStorage.AddBlobTransactionsFromBlock(blockOfTxs, txs); + using (ArrayPoolListRef txs = new(2, GetTx(TestItem.PrivateKeyA), GetTx(TestItem.PrivateKeyB))) + { + blobTxStorage.AddBlobTransactionsFromBlock(blockOfTxs, txs); + } blobTxStorage.TryGetBlobTransactionsFromBlock(blockOfTxs, out Transaction[]? returnedTxs).Should().BeTrue(); returnedTxs!.Length.Should().Be(2); diff --git a/src/Nethermind/Nethermind.Monitoring/Metrics/MetricsController.cs b/src/Nethermind/Nethermind.Monitoring/Metrics/MetricsController.cs index 8b586e326ba5..2a3c91c3a89c 100644 --- a/src/Nethermind/Nethermind.Monitoring/Metrics/MetricsController.cs +++ b/src/Nethermind/Nethermind.Monitoring/Metrics/MetricsController.cs @@ -89,13 +89,21 @@ public void Update() break; case ITuple keyAsTuple: { - using ArrayPoolList labels = new ArrayPoolList(keyAsTuple.Length, keyAsTuple.Length); - for (int i = 0; i < keyAsTuple.Length; i++) + ArrayPoolListRef labels = new(keyAsTuple.Length, keyAsTuple.Length); + try { - labels[i] = keyAsTuple[i]!.ToString()!; + for (int i = 0; i < keyAsTuple.Length; i++) + { + labels[i] = keyAsTuple[i]!.ToString()!; + } + + Update(value, labels.AsSpan()); + } + finally + { + labels.Dispose(); } - Update(value, labels.AsSpan()); break; } default: diff --git a/src/Nethermind/Nethermind.Network.Contract/Messages/PooledTransactionRequestMessage.cs b/src/Nethermind/Nethermind.Network.Contract/Messages/PooledTransactionRequestMessage.cs new file mode 100644 index 000000000000..eb6c45ac5e12 --- /dev/null +++ b/src/Nethermind/Nethermind.Network.Contract/Messages/PooledTransactionRequestMessage.cs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; + +namespace Nethermind.Network.Contract.Messages; + +public readonly struct PooledTransactionRequestMessage : INew +{ + public ValueHash256 TxHash { get; init; } + + public static PooledTransactionRequestMessage New(ValueHash256 txHash) => new() { TxHash = txHash }; +} diff --git a/src/Nethermind/Nethermind.Network.Test/ForkInfoTests.cs b/src/Nethermind/Nethermind.Network.Test/ForkInfoTests.cs index 1ea0081cdc6d..b9086423ed75 100644 --- a/src/Nethermind/Nethermind.Network.Test/ForkInfoTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/ForkInfoTests.cs @@ -109,25 +109,6 @@ public void Fork_id_and_hash_as_expected_with_merge_fork_id(long head, ulong hea Test(head, headTimestamp, KnownHashes.MainnetGenesis, forkHashHex, next, description, provider); } - [TestCase(0, 0ul, "0xc61a6098", 1_696_000_704ul, "Unsynced")] - [TestCase(1, 1_696_000_703ul, "0xc61a6098", 1_696_000_704ul, "Last genesis spec block")] - [TestCase(2, 1_696_000_704ul, "0xfd4f016b", 1_707_305_664ul, "First Shanghai block")] - [TestCase(3, 1_707_305_663ul, "0xfd4f016b", 1_707_305_664ul, "Future Shanghai timestamp")] - [TestCase(4, 1_707_305_664ul, "0x9b192ad0", 1_740_434_112ul, "First Cancun timestamp")] - [TestCase(5, 1_717_305_664ul, "0x9b192ad0", 1_740_434_112ul, "Future Cancun timestamp")] - [TestCase(5, 1_740_434_112ul, "0xdfbd9bed", 1_759_308_480ul, "First Prague timestamp")] - [TestCase(5, 1_750_434_112ul, "0xdfbd9bed", 1_759_308_480ul, "Future Prague timestamp")] - [TestCase(6, 1_759_308_480ul, "0x783def52", 1_759_800_000ul, "First Osaka timestamp")] - [TestCase(6, 1_759_309_480ul, "0x783def52", 1_759_800_000ul, "Future Osaka timestamp")] - [TestCase(7, 1_759_800_000ul, "0xa280a45c", 1_760_389_824ul, "First BPO1 timestamp")] - [TestCase(7, 1_759_801_000ul, "0xa280a45c", 1_760_389_824ul, "Future BPO1 timestamp")] - [TestCase(8, 1_760_389_824ul, "0x9bc6cb31", 0ul, "First BPO2 timestamp")] - [TestCase(8, 1_770_389_824ul, "0x9bc6cb31", 0ul, "Future BPO2 timestamp")] - public void Fork_id_and_hash_as_expected_on_holesky(long head, ulong headTimestamp, string forkHashHex, ulong next, string description) - { - Test(head, headTimestamp, KnownHashes.HoleskyGenesis, forkHashHex, next, description, HoleskySpecProvider.Instance, "holesky.json"); - } - [TestCase(0, 0ul, "0xFE3366E7", 1735371ul, "Sepolia genesis")] [TestCase(1735370, 0ul, "0xFE3366E7", 1_735_371ul, "Sepolia Last block before MergeForkIdTranstion")] [TestCase(1735371, 0ul, "0xb96cbd13", 1_677_557_088ul, "First block - Sepolia MergeForkIdTransition")] @@ -179,10 +160,11 @@ public void Fork_id_and_hash_as_expected_on_gnosis(long head, ulong headTimestam [TestCase(3945317, ChiadoSpecProvider.ShanghaiTimestamp, "0xa15a4252", ChiadoSpecProvider.CancunTimestamp, "First Shanghai timestamp")] [TestCase(4_000_000, ChiadoSpecProvider.CancunTimestamp, "0x5fbc16bc", 1741254220ul, "First Cancun timestamp")] [TestCase(5_000_000, 1741254219u, "0x5fbc16bc", 1741254220ul, "Future Cancun timestamp")] - [TestCase(5_000_000, 1741254220u, "0x8ba51786", 1761037900ul, "First Prague timestamp")] - [TestCase(5_000_000, 1741254420u, "0x8ba51786", 1761037900ul, "Future Prague timestamp")] - [TestCase(5_000_000, 1761037900u, "0x82612523", 0ul, "First Osaka timestamp")] - [TestCase(5_000_000, 1761038000u, "0x82612523", 0ul, "Future Osaka timestamp")] + [TestCase(5_000_000, 1741254220u, "0x8ba51786", 0ul, "First Prague timestamp")] + [TestCase(5_000_000, 1741254420u, "0x8ba51786", 0ul, "Future Prague timestamp")] + // [TestCase(5_000_000, 1741254420u, "0x8ba51786", 1761037900ul, "Future Prague timestamp")] + // [TestCase(5_000_000, 1761037900u, "0x82612523", 0ul, "First Osaka timestamp")] + // [TestCase(5_000_000, 1761038000u, "0x82612523", 0ul, "Future Osaka timestamp")] public void Fork_id_and_hash_as_expected_on_chiado(long head, ulong headTimestamp, string forkHashHex, ulong next, string description) { ChainSpecFileLoader loader = new(new EthereumJsonSerializer(), LimboTraceLogger.Instance); diff --git a/src/Nethermind/Nethermind.Network.Test/Nethermind.Network.Test.csproj b/src/Nethermind/Nethermind.Network.Test/Nethermind.Network.Test.csproj index 1b7da78c08fc..86575e37bea7 100644 --- a/src/Nethermind/Nethermind.Network.Test/Nethermind.Network.Test.csproj +++ b/src/Nethermind/Nethermind.Network.Test/Nethermind.Network.Test.csproj @@ -39,5 +39,6 @@ + diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V62/TxFloodControllerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V62/TxFloodControllerTests.cs index 284d9b09cf95..06923774ec5d 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V62/TxFloodControllerTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V62/TxFloodControllerTests.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -26,6 +26,8 @@ public class TxFloodControllerTests private ISession _session; private ITimestamper _timestamper; + private readonly AcceptTxResult Flooding = AcceptTxResult.NonceGap; + [SetUp] public void Setup() { @@ -66,7 +68,7 @@ public void Is_allowed_will_be_false_when_misbehaving() { for (int i = 0; i < 601; i++) { - _controller.Report(false); + _controller.Report(Flooding); } int allowedCount = 0; @@ -83,22 +85,22 @@ public void Will_only_get_disconnected_when_really_flooding() { for (int i = 0; i < 600; i++) { - _controller.Report(false); + _controller.Report(Flooding); } // for easier debugging - _controller.Report(false); + _controller.Report(Flooding); _session.DidNotReceiveWithAnyArgs() .InitiateDisconnect(DisconnectReason.TxFlooding, null); for (int i = 0; i < 6000 - 601; i++) { - _controller.Report(false); + _controller.Report(Flooding); } // for easier debugging - _controller.Report(false); + _controller.Report(Flooding); _session.Received() .InitiateDisconnect(DisconnectReason.TxFlooding, Arg.Any()); @@ -109,7 +111,7 @@ public void Will_downgrade_at_first() { for (int i = 0; i < 1000; i++) { - _controller.Report(false); + _controller.Report(Flooding); } _controller.IsDowngraded.Should().BeTrue(); @@ -139,7 +141,7 @@ public void Misbehaving_expires() { for (int i = 0; i < 1000; i++) { - _controller.Report(false); + _controller.Report(Flooding); } _controller.IsDowngraded.Should().BeTrue(); @@ -147,5 +149,14 @@ public void Misbehaving_expires() _controller.Report(false); _controller.IsDowngraded.Should().BeFalse(); } + + [Test] + public void Will_disconnect_on_invalid_tx() + { + _controller.Report(AcceptTxResult.Invalid); + + _session.Received(1) + .InitiateDisconnect(DisconnectReason.Other, "invalid tx"); + } } } diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandlerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandlerTests.cs index e420e543bac0..f7c9f1297b13 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandlerTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandlerTests.cs @@ -1,8 +1,7 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Generic; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -10,7 +9,6 @@ using FluentAssertions; using Nethermind.Consensus; using Nethermind.Core; -using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; @@ -21,7 +19,6 @@ using Nethermind.Logging; using Nethermind.Network.P2P; using Nethermind.Network.P2P.Messages; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V65; using Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; @@ -43,7 +40,6 @@ public class Eth65ProtocolHandlerTests private IMessageSerializationService _svc = null!; private ISyncServer _syncManager = null!; private ITxPool _transactionPool = null!; - private IPooledTxsRequestor _pooledTxsRequestor = null!; private ISpecProvider _specProvider = null!; private Block _genesisBlock = null!; private Eth65ProtocolHandler _handler = null!; @@ -64,7 +60,6 @@ public void Setup() _session.When(s => s.DeliverMessage(Arg.Any())).Do(c => c.Arg().AddTo(_disposables)); _syncManager = Substitute.For(); _transactionPool = Substitute.For(); - _pooledTxsRequestor = Substitute.For(); _specProvider = Substitute.For(); _genesisBlock = Build.A.Block.Genesis.TestObject; _syncManager.Head.Returns(_genesisBlock.Header); @@ -80,7 +75,6 @@ public void Setup() _syncManager, RunImmediatelyScheduler.Instance, _transactionPool, - _pooledTxsRequestor, Policy.FullGossip, new ForkInfo(_specProvider, _syncManager), LimboLogs.Instance, @@ -197,7 +191,7 @@ public void should_handle_NewPooledTransactionHashesMessage([Values(true, false) HandleIncomingStatusMessage(); HandleZeroMessage(msg, Eth65MessageCode.NewPooledTransactionHashes); - _pooledTxsRequestor.Received(canGossipTransactions ? 1 : 0).RequestTransactions(Arg.Any>(), Arg.Any>()); + _session.Received(canGossipTransactions ? 1 : 0).DeliverMessage(Arg.Any()); } private void HandleZeroMessage(T msg, int messageCode) where T : MessageBase diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V65/PooledTxsRequestorTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V65/PooledTxsRequestorTests.cs deleted file mode 100644 index 1064f0219b66..000000000000 --- a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V65/PooledTxsRequestorTests.cs +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using FluentAssertions; -using Nethermind.Core.Crypto; -using Nethermind.Core.Test.Builders; -using Nethermind.Network.P2P.Subprotocols.Eth; -using Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; -using Nethermind.TxPool; -using NSubstitute; -using NUnit.Framework; -using Nethermind.Core.Collections; -using Nethermind.Core.Extensions; -using Nethermind.Core.Specs; - -namespace Nethermind.Network.Test.P2P.Subprotocols.Eth.V65 -{ - public class PooledTxsRequestorTests - { - private readonly ITxPool _txPool = Substitute.For(); - private ISpecProvider _specProvider = Substitute.For(); - private readonly Action _doNothing = static msg => msg.Dispose(); - private IPooledTxsRequestor _requestor; - private ArrayPoolList _response; - - [TearDown] - public void TearDown() - { - _response?.Dispose(); - } - - [Test] - public void filter_properly_newPooledTxHashes() - { - _requestor = new PooledTxsRequestor(_txPool, new TxPoolConfig(), _specProvider); - using var skipped = new ArrayPoolList(2) { TestItem.KeccakA, TestItem.KeccakD }; - _requestor.RequestTransactions(_doNothing, skipped); - - using var request = new ArrayPoolList(3) { TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakC }; - using var expected = new ArrayPoolList(3) { TestItem.KeccakB, TestItem.KeccakC }; - _requestor.RequestTransactions(Send, request); - _response.Should().BeEquivalentTo(expected); - } - - [Test] - public void filter_properly_already_pending_hashes() - { - _requestor = new PooledTxsRequestor(_txPool, new TxPoolConfig(), _specProvider); - using var skipped = new ArrayPoolList(3) { TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakC }; - _requestor.RequestTransactions(_doNothing, skipped); - - using var request = new ArrayPoolList(3) { TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakC }; - _requestor.RequestTransactions(Send, request); - _response.Should().BeEmpty(); - } - - [Test] - public void filter_properly_discovered_hashes() - { - _requestor = new PooledTxsRequestor(_txPool, new TxPoolConfig(), _specProvider); - - using var request = new ArrayPoolList(3) { TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakC }; - using var expected = new ArrayPoolList(3) { TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakC }; - _requestor.RequestTransactions(Send, request); - _response.Should().BeEquivalentTo(expected); - } - - [Test] - public void can_handle_empty_argument() - { - _requestor = new PooledTxsRequestor(_txPool, new TxPoolConfig(), _specProvider); - using var skipped = new ArrayPoolList(0); - _requestor.RequestTransactions(Send, skipped); - _response.Should().BeEmpty(); - } - - [Test] - public void filter_properly_hashes_present_in_hashCache() - { - ITxPool txPool = Substitute.For(); - txPool.IsKnown(Arg.Any()).Returns(true); - _requestor = new PooledTxsRequestor(txPool, new TxPoolConfig(), _specProvider); - - using var request = new ArrayPoolList(2) { TestItem.KeccakA, TestItem.KeccakB }; - using var expected = new ArrayPoolList(0) { }; - _requestor.RequestTransactions(Send, request); - _response.Should().BeEquivalentTo(expected); - } - - private void Send(GetPooledTransactionsMessage msg) - { - _response?.Dispose(); - using (msg) - { - _response = msg.Hashes.ToPooledList(); - } - } - } -} diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandlerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandlerTests.cs index 296ce7b8039b..a2c405de0a55 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandlerTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandlerTests.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; @@ -22,7 +22,6 @@ using Nethermind.Network.P2P; using Nethermind.Network.P2P.Messages; using Nethermind.Network.P2P.Subprotocols; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V65; using Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; @@ -41,7 +40,6 @@ using GetBlockBodiesMessage = Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages.GetBlockBodiesMessage; using GetBlockHeadersMessage = Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages.GetBlockHeadersMessage; using GetNodeDataMessage = Nethermind.Network.P2P.Subprotocols.Eth.V63.Messages.GetNodeDataMessage; -using GetPooledTransactionsMessage = Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages.GetPooledTransactionsMessage; using GetReceiptsMessage = Nethermind.Network.P2P.Subprotocols.Eth.V63.Messages.GetReceiptsMessage; using NodeDataMessage = Nethermind.Network.P2P.Subprotocols.Eth.V63.Messages.NodeDataMessage; using PooledTransactionsMessage = Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages.PooledTransactionsMessage; @@ -56,7 +54,6 @@ public class Eth66ProtocolHandlerTests private IMessageSerializationService _svc = null!; private ISyncServer _syncManager = null!; private ITxPool _transactionPool = null!; - private IPooledTxsRequestor _pooledTxsRequestor = null!; private IGossipPolicy _gossipPolicy = null!; private ITimerFactory _timerFactory = null!; private ISpecProvider _specProvider = null!; @@ -78,7 +75,6 @@ public void Setup() _session.When(s => s.DeliverMessage(Arg.Any())).Do(c => c.Arg().AddTo(_disposables)); _syncManager = Substitute.For(); _transactionPool = Substitute.For(); - _pooledTxsRequestor = Substitute.For(); _specProvider = Substitute.For(); _gossipPolicy = Substitute.For(); _genesisBlock = Build.A.Block.Genesis.TestObject; @@ -92,7 +88,6 @@ public void Setup() _syncManager, RunImmediatelyScheduler.Instance, _transactionPool, - _pooledTxsRequestor, _gossipPolicy, new ForkInfo(_specProvider, _syncManager), LimboLogs.Instance); @@ -204,8 +199,7 @@ public void Should_throw_when_receiving_unrequested_block_bodies() [Test] public void Can_handle_get_pooled_transactions() { - using var msg65 = new GetPooledTransactionsMessage(new[] { Keccak.Zero, TestItem.KeccakA }.ToPooledList()); - using var msg66 = new Network.P2P.Subprotocols.Eth.V66.Messages.GetPooledTransactionsMessage(1111, msg65); + using var msg66 = new Network.P2P.Subprotocols.Eth.V66.Messages.GetPooledTransactionsMessage(new[] { Keccak.Zero, TestItem.KeccakA }.ToPooledList()); HandleIncomingStatusMessage(); HandleZeroMessage(msg66, Eth66MessageCode.GetPooledTransactions); @@ -322,7 +316,6 @@ public void should_request_in_GetPooledTransactionsMessage_up_to_256_txs(int num _syncManager, RunImmediatelyScheduler.Instance, _transactionPool, - new PooledTxsRequestor(_transactionPool, new TxPoolConfig(), _specProvider), _gossipPolicy, new ForkInfo(_specProvider, _syncManager), LimboLogs.Instance); diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/GetPooledTransactionsSerializerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/GetPooledTransactionsSerializerTests.cs index 6ec52f4bf63a..732886d05f2f 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/GetPooledTransactionsSerializerTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/GetPooledTransactionsSerializerTests.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core.Crypto; @@ -18,9 +18,8 @@ public void Roundtrip() Hash256 a = new("0x00000000000000000000000000000000000000000000000000000000deadc0de"); Hash256 b = new("0x00000000000000000000000000000000000000000000000000000000feedbeef"); Hash256[] keys = { a, b }; - using var ethMessage = new Network.P2P.Subprotocols.Eth.V65.Messages.GetPooledTransactionsMessage(keys.ToPooledList()); - GetPooledTransactionsMessage message = new(1111, ethMessage); + using GetPooledTransactionsMessage message = new(keys.ToPooledList()) { RequestId = 1111 }; GetPooledTransactionsMessageSerializer serializer = new(); diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/PooledTransactionsRequestingTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/PooledTransactionsRequestingTests.cs new file mode 100644 index 000000000000..6bbf7ace5c0e --- /dev/null +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V66/PooledTransactionsRequestingTests.cs @@ -0,0 +1,198 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using DotNetty.Buffers; +using Nethermind.Blockchain; +using Nethermind.Blockchain.Spec; +using Nethermind.Consensus; +using Nethermind.Consensus.Comparers; +using Nethermind.Consensus.Validators; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Test; +using Nethermind.Core.Test.Builders; +using Nethermind.Core.Test.Db; +using Nethermind.Core.Timers; +using Nethermind.Crypto; +using Nethermind.Logging; +using Nethermind.Network.P2P; +using Nethermind.Network.P2P.Messages; +using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; +using Nethermind.Network.P2P.Subprotocols.Eth.V65; +using Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; +using Nethermind.Network.P2P.Subprotocols.Eth.V66; +using Nethermind.Network.Rlpx; +using Nethermind.Network.Test.Builders; +using Nethermind.Specs; +using Nethermind.Specs.Forks; +using Nethermind.Stats; +using Nethermind.Stats.Model; +using Nethermind.Synchronization; +using Nethermind.TxPool; +using NSubstitute; +using NSubstitute.ReceivedExtensions; +using NUnit.Framework; +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using PooledTransactionsMessage = Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages.PooledTransactionsMessage; + +namespace Nethermind.Network.Test.P2P.Subprotocols.Eth.V66; + +[TestFixture, Parallelizable(ParallelScope.Self), FixtureLifeCycle(LifeCycle.InstancePerTestCase)] +public class PooledTransactionsRequestingTests +{ + private ISession _session = null!; + private ISession _session2 = null!; + private Eth66ProtocolHandler _handler = null!; + private ArrayPoolList _txs = null!; + private IMessageSerializationService _svc = null!; + private Block _genesisBlock = null!; + private CompositeDisposable _disposables = null!; + + private readonly int Timeout = 3000; + private readonly int InTime = 1000; + + [SetUp] + public void Setup() + { + _svc = Build.A.SerializationService().WithEth66().TestObject; + + IGossipPolicy _gossipPolicy = Substitute.For(); + _disposables = []; + + _session = Substitute.For(); + _session.Node.Returns(new Node(TestItem.PublicKeyA, new IPEndPoint(IPAddress.Broadcast, 30303))); + _session.When(s => s.DeliverMessage(Arg.Any())).Do(c => c.Arg().AddTo(_disposables)); + + _genesisBlock = Build.A.Block.Genesis.TestObject; + + TestSingleReleaseSpecProvider specProvider = new(Osaka.Instance); + IBlockTree blockTree = Build.A.BlockTree().WithoutSettingHead.WithSpecProvider(specProvider).TestObject; + + TxPool.TxPool txPool = new( + new EthereumEcdsa(specProvider.ChainId), + new BlobTxStorage(), + new ChainHeadInfoProvider( + new ChainHeadSpecProvider(specProvider, blockTree), blockTree, TestWorldStateFactory.CreateForTestWithStateReader(TestMemDbProvider.Init(), LimboLogs.Instance).Item2), + new TxPoolConfig(), + new TxValidator(specProvider.ChainId), + LimboLogs.Instance, + new TransactionComparerProvider(specProvider, blockTree).GetDefaultComparer()); + + ISyncServer syncManager = Substitute.For(); + syncManager.Head.Returns(_genesisBlock.Header); + syncManager.Genesis.Returns(_genesisBlock.Header); + + ITimerFactory _timerFactory = Substitute.For(); + + _handler = new Eth66ProtocolHandler( + _session, + _svc, + new NodeStatsManager(_timerFactory, LimboLogs.Instance), + syncManager, + RunImmediatelyScheduler.Instance, + txPool, + Substitute.For(), + new ForkInfo(specProvider, syncManager), + LimboLogs.Instance); + + syncManager.AddTo(_disposables); + _handler.Init(); + + Transaction tx = Build.A.Transaction.WithShardBlobTxTypeAndFields(1).WithMaxPriorityFeePerGas(1).WithGasLimit(100) + .SignedAndResolved(new EthereumEcdsa(1), TestItem.PrivateKeyA).TestObject; + + Hash256 _txHash = tx.CalculateHash(); + _txs = new(1) { tx }; + _txs.AddTo(_disposables); + + _session2 = Substitute.For(); + _session2.Node.Returns(new Node(TestItem.PublicKeyB, new IPEndPoint(IPAddress.Loopback, 30304))); + _session2.When(s => s.DeliverMessage(Arg.Any())).Do(c => c.Arg().AddTo(_disposables)); + + + // Create second handler for second peer + Eth66ProtocolHandler handler2 = new( + _session2, + _svc, + new NodeStatsManager(_timerFactory, LimboLogs.Instance), + syncManager, + RunImmediatelyScheduler.Instance, + txPool, + _gossipPolicy, + new ForkInfo(specProvider, syncManager), + LimboLogs.Instance); + handler2.Init(); + handler2.AddTo(_disposables); + + // Setup both handlers to receive status messages + HandleIncomingStatusMessage(_handler); + HandleIncomingStatusMessage(handler2); + + // Act - Send new pooled transaction hashes from both peers + using NewPooledTransactionHashesMessage hashesMsg1 = new(new ArrayPoolList(1) { _txHash }); + using NewPooledTransactionHashesMessage hashesMsg2 = new(new ArrayPoolList(1) { _txHash }); + + HandleZeroMessage(_handler, hashesMsg1, Eth65MessageCode.NewPooledTransactionHashes); + HandleZeroMessage(handler2, hashesMsg2, Eth65MessageCode.NewPooledTransactionHashes); + } + + [TearDown] + public void TearDown() + { + _session.Dispose(); + _session2.Dispose(); + _handler.Dispose(); + _disposables?.Dispose(); + } + + [Test] + public async Task Should_request_from_others_after_timout() + { + await Task.Delay(Timeout); + + _session2.Received(1).DeliverMessage(Arg.Is(m => m.EthMessage.Hashes.Contains(_txs[0].Hash))); + } + + + [Test] + public async Task Should_not_request_from_others_if_received() + { + await Task.Delay(InTime); + HandleZeroMessage(_handler, new Network.P2P.Subprotocols.Eth.V66.Messages.PooledTransactionsMessage(1111, new PooledTransactionsMessage(_txs)), Eth65MessageCode.PooledTransactions); + await Task.Delay(Timeout); + + _session2.Received(0).DeliverMessage(Arg.Is(m => m.EthMessage.Hashes.Contains(_txs[0].Hash))); + } + + + [Test] + public async Task Should_not_request_from_others_if_received_immidietly() + { + HandleZeroMessage(_handler, new Network.P2P.Subprotocols.Eth.V66.Messages.PooledTransactionsMessage(1111, new PooledTransactionsMessage(_txs)), Eth65MessageCode.PooledTransactions); + await Task.Delay(Timeout); + + _session2.Received(0).DeliverMessage(Arg.Is(m => m.EthMessage.Hashes.Contains(_txs[0].Hash))); + } + + private void HandleIncomingStatusMessage(Eth66ProtocolHandler handler) + { + using var statusMsg = new StatusMessage(); + statusMsg.GenesisHash = _genesisBlock.Hash; + statusMsg.BestHash = _genesisBlock.Hash; + + IByteBuffer statusPacket = _svc.ZeroSerialize(statusMsg); + statusPacket.ReadByte(); + handler.HandleMessage(new ZeroPacket(statusPacket) { PacketType = 0 }); + } + + private void HandleZeroMessage(Eth66ProtocolHandler handler, T msg, int messageCode) where T : MessageBase + { + IByteBuffer packet = _svc.ZeroSerialize(msg); + packet.ReadByte(); + handler.HandleMessage(new ZeroPacket(packet) { PacketType = (byte)messageCode }); + } +} diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V67/Eth67ProtocolHandlerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V67/Eth67ProtocolHandlerTests.cs index 16b16c217398..262af32c19e7 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V67/Eth67ProtocolHandlerTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V67/Eth67ProtocolHandlerTests.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Net; @@ -16,7 +16,6 @@ using Nethermind.Logging; using Nethermind.Network.P2P; using Nethermind.Network.P2P.Subprotocols; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V63.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V66; @@ -39,7 +38,6 @@ public class Eth67ProtocolHandlerTests private IMessageSerializationService _svc = null!; private ISyncServer _syncManager = null!; private ITxPool _transactionPool = null!; - private IPooledTxsRequestor _pooledTxsRequestor = null!; private IGossipPolicy _gossipPolicy = null!; private ISpecProvider _specProvider = null!; private Block _genesisBlock = null!; @@ -57,7 +55,6 @@ public void Setup() _session.Node.Returns(node); _syncManager = Substitute.For(); _transactionPool = Substitute.For(); - _pooledTxsRequestor = Substitute.For(); _specProvider = Substitute.For(); _gossipPolicy = Substitute.For(); _genesisBlock = Build.A.Block.Genesis.TestObject; @@ -71,7 +68,6 @@ public void Setup() _syncManager, RunImmediatelyScheduler.Instance, _transactionPool, - _pooledTxsRequestor, _gossipPolicy, new ForkInfo(_specProvider, _syncManager), LimboLogs.Instance); diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandlerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandlerTests.cs index f57b8ab3b748..657350833df7 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandlerTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandlerTests.cs @@ -1,8 +1,6 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Net; using DotNetty.Buffers; using FluentAssertions; using Nethermind.Consensus; @@ -18,8 +16,8 @@ using Nethermind.Network.P2P; using Nethermind.Network.P2P.Messages; using Nethermind.Network.P2P.Subprotocols; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; +using Nethermind.Network.P2P.Subprotocols.Eth.V66; using Nethermind.Network.P2P.Subprotocols.Eth.V66.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V68; using Nethermind.Network.P2P.Subprotocols.Eth.V68.Messages; @@ -32,6 +30,8 @@ using Nethermind.TxPool; using NSubstitute; using NUnit.Framework; +using System; +using System.Net; namespace Nethermind.Network.Test.P2P.Subprotocols.Eth.V68; @@ -41,7 +41,6 @@ public class Eth68ProtocolHandlerTests private IMessageSerializationService _svc = null!; private ISyncServer _syncManager = null!; private ITxPool _transactionPool = null!; - private IPooledTxsRequestor _pooledTxsRequestor = null!; private IGossipPolicy _gossipPolicy = null!; private ISpecProvider _specProvider = null!; private Block _genesisBlock = null!; @@ -64,7 +63,6 @@ public void Setup() _session.When(s => s.DeliverMessage(Arg.Any())).Do(c => c.Arg().AddTo(_disposables)); _syncManager = Substitute.For(); _transactionPool = Substitute.For(); - _pooledTxsRequestor = Substitute.For(); _specProvider = Substitute.For(); _gossipPolicy = Substitute.For(); _genesisBlock = Build.A.Block.Genesis.TestObject; @@ -81,10 +79,11 @@ public void Setup() _syncManager, RunImmediatelyScheduler.Instance, _transactionPool, - _pooledTxsRequestor, _gossipPolicy, new ForkInfo(_specProvider, _syncManager), LimboLogs.Instance, + Substitute.For(), + Substitute.For(), _txGossipPolicy); _handler.Init(); } @@ -123,8 +122,7 @@ public void Can_handle_NewPooledTransactions_message([Values(0, 1, 2, 100)] int HandleIncomingStatusMessage(); HandleZeroMessage(msg, Eth68MessageCode.NewPooledTransactionHashes); - _pooledTxsRequestor.Received(canGossipTransactions ? 1 : 0).RequestTransactionsEth68(Arg.Any>(), - Arg.Any>(), Arg.Any>(), Arg.Any>()); + _session.Received(canGossipTransactions && txCount != 0 ? 1 : 0).DeliverMessage(Arg.Any()); } [TestCase(true)] @@ -149,6 +147,38 @@ public void Should_throw_when_sizes_doesnt_match(bool removeSize) action.Should().Throw(); } + + [Test] + public void Should_disconnect_if_tx_size_is_wrong() + { + GenerateTxLists(4, out ArrayPoolList types, out ArrayPoolList sizes, out ArrayPoolList hashes, out ArrayPoolList txs); + sizes[0] += 10; + using NewPooledTransactionHashesMessage68 hashesMsg = new(types, sizes, hashes); + using PooledTransactionsMessage txsMsg = new(1111, new(txs)); + + HandleIncomingStatusMessage(); + HandleZeroMessage(hashesMsg, Eth68MessageCode.NewPooledTransactionHashes); + HandleZeroMessage(txsMsg, Eth66MessageCode.PooledTransactions); + + _session.Received().InitiateDisconnect(DisconnectReason.BackgroundTaskFailure, "invalid pooled tx type or size"); + } + + + [Test] + public void Should_disconnect_if_tx_type_is_wrong() + { + GenerateTxLists(4, out ArrayPoolList types, out ArrayPoolList sizes, out ArrayPoolList hashes, out ArrayPoolList txs); + types[0]++; + using NewPooledTransactionHashesMessage68 hashesMsg = new(types, sizes, hashes); + using PooledTransactionsMessage txsMsg = new(1111, new(txs)); + + HandleIncomingStatusMessage(); + HandleZeroMessage(hashesMsg, Eth68MessageCode.NewPooledTransactionHashes); + HandleZeroMessage(txsMsg, Eth66MessageCode.PooledTransactions); + + _session.Received().InitiateDisconnect(DisconnectReason.BackgroundTaskFailure, "invalid pooled tx type or size"); + } + [Test] public void Should_process_huge_transaction() { @@ -161,8 +191,7 @@ public void Should_process_huge_transaction() HandleIncomingStatusMessage(); HandleZeroMessage(msg, Eth68MessageCode.NewPooledTransactionHashes); - _pooledTxsRequestor.Received(1).RequestTransactionsEth68(Arg.Any>(), - Arg.Any>(), Arg.Any>(), Arg.Any>()); + _session.Received(1).DeliverMessage(Arg.Any()); } [TestCase(1)] @@ -230,13 +259,14 @@ public void should_divide_GetPooledTransactionsMessage_if_max_message_size_is_ex _syncManager, RunImmediatelyScheduler.Instance, _transactionPool, - new PooledTxsRequestor(_transactionPool, new TxPoolConfig() { MaxTxSize = sizeOfOneTx }, _specProvider), _gossipPolicy, new ForkInfo(_specProvider, _syncManager), LimboLogs.Instance, + Substitute.For(), + Substitute.For(), _txGossipPolicy); - int maxNumberOfTxsInOneMsg = sizeOfOneTx < TransactionsMessage.MaxPacketSize ? TransactionsMessage.MaxPacketSize / sizeOfOneTx : 1; + int maxNumberOfTxsInOneMsg = int.Min(sizeOfOneTx < TransactionsMessage.MaxPacketSize ? TransactionsMessage.MaxPacketSize / sizeOfOneTx : 1, 256); int messagesCount = numberOfTransactions / maxNumberOfTxsInOneMsg + (numberOfTransactions % maxNumberOfTxsInOneMsg == 0 ? 0 : 1); using ArrayPoolList types = new(numberOfTransactions); @@ -276,20 +306,28 @@ private void HandleZeroMessage(T msg, byte messageCode) where T : MessageBase } private void GenerateLists(int txCount, out ArrayPoolList types, out ArrayPoolList sizes, out ArrayPoolList hashes) + { + GenerateTxLists(txCount, out types, out sizes, out hashes, out ArrayPoolList txs); + txs.Dispose(); + } + + private void GenerateTxLists(int txCount, out ArrayPoolList types, out ArrayPoolList sizes, out ArrayPoolList hashes, out ArrayPoolList txs) { TxDecoder txDecoder = TxDecoder.Instance; types = new(txCount); sizes = new(txCount); hashes = new(txCount); + txs = new(txCount); for (int i = 0; i < txCount; ++i) { - Transaction tx = Build.A.Transaction.WithType((TxType)(i % 3)).WithData(new byte[i]) + Transaction tx = Build.A.Transaction.WithType((TxType)(i % 3)).SignedAndResolved().WithData(new byte[i]) .WithHash(i % 2 == 0 ? TestItem.KeccakA : TestItem.KeccakB).TestObject; types.Add((byte)tx.Type); sizes.Add(txDecoder.GetLength(tx, RlpBehaviors.None)); hashes.Add(tx.Hash); + txs.Add(tx); } } } diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V69/Eth69ProtocolHandlerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V69/Eth69ProtocolHandlerTests.cs index 934aa5c2e6b2..91ed9fd438e6 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V69/Eth69ProtocolHandlerTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V69/Eth69ProtocolHandlerTests.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -23,7 +23,6 @@ using Nethermind.Network.P2P; using Nethermind.Network.P2P.Messages; using Nethermind.Network.P2P.Subprotocols; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Network.P2P.Subprotocols.Eth.V63; using Nethermind.Network.P2P.Subprotocols.Eth.V66; using Nethermind.Network.P2P.Subprotocols.Eth.V66.Messages; @@ -47,14 +46,12 @@ public class Eth69ProtocolHandlerTests private IMessageSerializationService _svc = null!; private ISyncServer _syncManager = null!; private ITxPool _transactionPool = null!; - private IPooledTxsRequestor _pooledTxsRequestor = null!; private IGossipPolicy _gossipPolicy = null!; private ISpecProvider _specProvider = null!; private Block _genesisBlock = null!; private Eth69ProtocolHandler _handler = null!; private ITxGossipPolicy _txGossipPolicy = null!; private ITimerFactory _timerFactory = null!; - private IBlockFinder _blockFinder = null!; [SetUp] public void Setup() @@ -66,19 +63,17 @@ public void Setup() _session.Node.Returns(node); _syncManager = Substitute.For(); _transactionPool = Substitute.For(); - _pooledTxsRequestor = Substitute.For(); _specProvider = Substitute.For(); _gossipPolicy = Substitute.For(); _genesisBlock = Build.A.Block.Genesis.TestObject; _syncManager.Head.Returns(_genesisBlock.Header); _syncManager.Genesis.Returns(_genesisBlock.Header); + _syncManager.LowestBlock.Returns(0); _timerFactory = Substitute.For(); _txGossipPolicy = Substitute.For(); _txGossipPolicy.ShouldListenToGossipedTransactions.Returns(true); _txGossipPolicy.ShouldGossipTransaction(Arg.Any()).Returns(true); _svc = Build.A.SerializationService().WithEth69(_specProvider).TestObject; - _blockFinder = Substitute.For(); - _blockFinder.GetLowestBlock().Returns(3); _handler = new Eth69ProtocolHandler( _session, _svc, @@ -86,11 +81,11 @@ public void Setup() _syncManager, RunImmediatelyScheduler.Instance, _transactionPool, - _pooledTxsRequestor, _gossipPolicy, new ForkInfo(_specProvider, _syncManager), - _blockFinder, LimboLogs.Instance, + Substitute.For(), + _specProvider, _txGossipPolicy); _handler.Init(); } @@ -280,7 +275,7 @@ public void On_init_sends_a_status_message() && m.Protocol == Protocol.Eth && m.GenesisHash == _genesisBlock.Hash && m.LatestBlockHash == _genesisBlock.Hash - && m.EarliestBlock == 3)); + && m.EarliestBlock == 0)); } private void HandleIncomingStatusMessage() diff --git a/src/Nethermind/Nethermind.Network.Test/ProtocolsManagerTests.cs b/src/Nethermind/Nethermind.Network.Test/ProtocolsManagerTests.cs index baad57e49fcd..9eba656ac38e 100644 --- a/src/Nethermind/Nethermind.Network.Test/ProtocolsManagerTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/ProtocolsManagerTests.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Numerics; @@ -10,6 +10,7 @@ using Nethermind.Core; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; using Nethermind.Core.Test; using Nethermind.Core.Test.Builders; using Nethermind.Core.Timers; @@ -20,19 +21,15 @@ using Nethermind.Network.P2P.Analyzers; using Nethermind.Network.P2P.Messages; using Nethermind.Network.P2P.ProtocolHandlers; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Network.P2P.Subprotocols.Eth.V62; using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; using Nethermind.Network.Rlpx; using Nethermind.Specs; -using Nethermind.Evm.State; using Nethermind.State; -using Nethermind.State.SnapServer; using Nethermind.Stats; using Nethermind.Stats.Model; using Nethermind.Synchronization; using Nethermind.Synchronization.Peers; -using Nethermind.Synchronization.SnapSync; using Nethermind.TxPool; using NSubstitute; using NUnit.Framework; @@ -65,7 +62,6 @@ public class Context private readonly ISyncServer _syncServer; private readonly ISyncPeerPool _syncPeerPool; private readonly ITxPool _txPool; - private readonly IPooledTxsRequestor _pooledTxsRequestor; private readonly IChannelHandlerContext _channelHandlerContext; private readonly IChannel _channel; private readonly IChannelPipeline _pipeline; @@ -89,7 +85,6 @@ public Context() _syncServer.Genesis.Returns(Build.A.Block.Genesis.TestObject.Header); _syncServer.Head.Returns(Build.A.BlockHeader.TestObject); _txPool = Substitute.For(); - _pooledTxsRequestor = Substitute.For(); _discoveryApp = Substitute.For(); _serializer = new MessageSerializationService( @@ -119,7 +114,6 @@ public Context() _syncServer, RunImmediatelyScheduler.Instance, _txPool, - _pooledTxsRequestor, _discoveryApp, _serializer, _rlpxHost, @@ -129,8 +123,9 @@ public Context() forkInfo, _gossipPolicy, Substitute.For(), - _blockTree, - LimboLogs.Instance); + LimboLogs.Instance, + Substitute.For(), + Substitute.For()); } public Context CreateIncomingSession() diff --git a/src/Nethermind/Nethermind.Network/INodeSource.cs b/src/Nethermind/Nethermind.Network/INodeSource.cs index 4270bbc40e82..2d1db2c89912 100644 --- a/src/Nethermind/Nethermind.Network/INodeSource.cs +++ b/src/Nethermind/Nethermind.Network/INodeSource.cs @@ -1,15 +1,16 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Stats.Model; using System; using System.Collections.Generic; using System.Threading; -using Nethermind.Stats.Model; namespace Nethermind.Network; public interface INodeSource { IAsyncEnumerable DiscoverNodes(CancellationToken cancellationToken); + event EventHandler NodeRemoved; } diff --git a/src/Nethermind/Nethermind.Network/IStaticNodesManager.cs b/src/Nethermind/Nethermind.Network/IStaticNodesManager.cs index 070e03d23c30..fe4237025bc4 100644 --- a/src/Nethermind/Nethermind.Network/IStaticNodesManager.cs +++ b/src/Nethermind/Nethermind.Network/IStaticNodesManager.cs @@ -5,14 +5,13 @@ using System.Threading.Tasks; using Nethermind.Config; -namespace Nethermind.Network +namespace Nethermind.Network; + +public interface IStaticNodesManager : INodeSource { - public interface IStaticNodesManager : INodeSource - { - IEnumerable Nodes { get; } - Task InitAsync(); - Task AddAsync(string enode, bool updateFile = true); - Task RemoveAsync(string enode, bool updateFile = true); - bool IsStatic(string enode); - } + IEnumerable Nodes { get; } + Task InitAsync(); + Task AddAsync(string enode, bool updateFile = true); + Task RemoveAsync(string enode, bool updateFile = true); + bool IsStatic(string enode); } diff --git a/src/Nethermind/Nethermind.Network/ITrustedNodesManager.cs b/src/Nethermind/Nethermind.Network/ITrustedNodesManager.cs index bb8f909f0b4e..46cfb73811af 100644 --- a/src/Nethermind/Nethermind.Network/ITrustedNodesManager.cs +++ b/src/Nethermind/Nethermind.Network/ITrustedNodesManager.cs @@ -1,18 +1,17 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; using System.Threading.Tasks; using Nethermind.Config; -namespace Nethermind.Network +namespace Nethermind.Network; + +public interface ITrustedNodesManager : INodeSource { - public interface ITrustedNodesManager : INodeSource - { - IEnumerable Nodes { get; } - Task InitAsync(); - Task AddAsync(Enode enode, bool updateFile = true); - Task RemoveAsync(Enode enode, bool updateFile = true); - bool IsTrusted(Enode enode); - } + IEnumerable Nodes { get; } + Task InitAsync(); + Task AddAsync(Enode enode, bool updateFile = true); + Task RemoveAsync(Enode enode, bool updateFile = true); + bool IsTrusted(Enode enode); } diff --git a/src/Nethermind/Nethermind.Network/Nethermind.Network.csproj b/src/Nethermind/Nethermind.Network/Nethermind.Network.csproj index 5906af767825..5c0543d30862 100644 --- a/src/Nethermind/Nethermind.Network/Nethermind.Network.csproj +++ b/src/Nethermind/Nethermind.Network/Nethermind.Network.csproj @@ -1,4 +1,4 @@ - + annotations @@ -19,20 +19,16 @@ - - - - - - - - - - + + static-nodes.json + + + trusted-nodes.json + diff --git a/src/Nethermind/Nethermind.Network/NodesManager.cs b/src/Nethermind/Nethermind.Network/NodesManager.cs new file mode 100644 index 000000000000..adf31ca774e8 --- /dev/null +++ b/src/Nethermind/Nethermind.Network/NodesManager.cs @@ -0,0 +1,145 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Config; +using Nethermind.Core.Crypto; +using Nethermind.Logging; +using Nethermind.Serialization.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Nethermind.Network; + +public abstract class NodesManager(string path, ILogger logger) +{ + private static readonly char[] _separator = ['\r', '\n']; + + protected readonly ILogger _logger = logger; + protected ConcurrentDictionary _nodes = []; + + private void EnsureFile(string resource) + { + if (File.Exists(path)) + { + return; + } + else // For backward compatibility. To be removed in future versions. + { + string oldPath = Path.GetFullPath($"Data/{resource}".GetApplicationResourcePath()); + + if (File.Exists(oldPath)) + { + var moved = true; + + try + { + File.Move(oldPath, path, false); + } + catch (Exception ex) + { + moved = false; + + if (_logger.IsWarn) + _logger.Warn($"Failed to move {oldPath} to {Path.GetFullPath(path)}: {ex.Message}\n {resource} is ignored and will not be used"); + } + + if (moved) + { + if (_logger.IsWarn) + _logger.Warn($"{oldPath} has been moved to {Path.GetFullPath(path)}"); + + return; + } + } + } + + // Create the directory if needed + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + if (_logger.IsDebug) _logger.Debug($"Nodes file was not found, creating one at {Path.GetFullPath(path)}"); + + using Stream actualNodes = File.Create(path); + using Stream embeddedNodes = typeof(NodesManager).Assembly.GetManifestResourceStream(resource); + + if (embeddedNodes is null) + { + if (_logger.IsDebug) _logger.Debug($"Embedded resource {resource} was not found"); + + File.WriteAllText(path, "[]\n"); + } + else + { + embeddedNodes.CopyTo(actualNodes); + } + } + + protected virtual void LogNodeList(string title, IDictionary nodes) + { + if (_logger.IsDebug && nodes.Count != 0) + { + var separator = $"{Environment.NewLine} "; + + _logger.Debug($"{title}:{separator}{string.Join(separator, nodes.Values.Select(n => n.ToString()))}"); + } + } + + protected virtual async Task> ParseNodes(string fallbackResource) + { + EnsureFile(fallbackResource); + + string data = await File.ReadAllTextAsync(path); + + IEnumerable? rawNodes; + + try + { + rawNodes = JsonSerializer.Deserialize>(data); + } + catch (JsonException) + { + rawNodes = data.Split(_separator, StringSplitOptions.RemoveEmptyEntries).ToHashSet(); + } + + ConcurrentDictionary nodes = []; + + foreach (string? n in rawNodes ?? []) + { + NetworkNode node; + + try + { + node = new(n); + } + catch (ArgumentException ex) + { + if (_logger.IsError) _logger.Error($"Failed to parse node: {n}", ex); + + continue; + } + + nodes.TryAdd(node.NodeId, node); + } + + if (_logger.IsInfo) + _logger.Info($"Loaded {nodes.Count} nodes from {Path.GetFullPath(path)}"); + + return nodes; + } + + protected virtual Task SaveFileAsync() + { + ArgumentException.ThrowIfNullOrWhiteSpace(path); + + string contents = JsonSerializer.Serialize( + _nodes.Select(static n => n.Value.ToString()), + EthereumJsonSerializer.JsonOptionsIndented + ); + + return File.WriteAllTextAsync(path, contents); + } +} diff --git a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/IProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/IProtocolHandler.cs index 0e9b4bbcfefb..8e710f7db6a1 100644 --- a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/IProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/IProtocolHandler.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; diff --git a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/ProtocolHandlerBase.cs b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/ProtocolHandlerBase.cs index a07b28f4fd54..af5c03ca204d 100644 --- a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/ProtocolHandlerBase.cs +++ b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/ProtocolHandlerBase.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -16,7 +16,6 @@ using Nethermind.Serialization.Rlp; using Nethermind.Stats; using Nethermind.Stats.Model; -using Nethermind.Synchronization; namespace Nethermind.Network.P2P.ProtocolHandlers { @@ -90,6 +89,8 @@ protected T Deserialize(IByteBuffer data) where T : P2PMessage int originalReaderIndex = data.ReaderIndex; T result = _serializer.Deserialize(data); if (data.IsReadable()) ThrowIncompleteDeserializationException(data, originalReaderIndex); + if (Logger.IsTrace) Logger.Trace($"{Counter} Got {typeof(T).Name}"); + return result; } catch (RlpLimitException e) diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/IPooledTxsRequestor.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/IPooledTxsRequestor.cs deleted file mode 100644 index 26f443961a5e..000000000000 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/IPooledTxsRequestor.cs +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using Nethermind.Core.Collections; -using Nethermind.Core.Crypto; -using Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; - -namespace Nethermind.Network.P2P.Subprotocols.Eth -{ - public interface IPooledTxsRequestor - { - void RequestTransactions(Action send, IOwnedReadOnlyList hashes); - void RequestTransactionsEth66(Action send, IOwnedReadOnlyList hashes); - void RequestTransactionsEth68(Action send, IOwnedReadOnlyList hashes, IOwnedReadOnlyList sizes, IOwnedReadOnlyList types); - } -} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/PooledTxsRequestor.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/PooledTxsRequestor.cs deleted file mode 100644 index c50ddaadee10..000000000000 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/PooledTxsRequestor.cs +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Linq; -using Nethermind.Core; -using Nethermind.Core.Caching; -using Nethermind.Core.Collections; -using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; -using Nethermind.Core.Specs; -using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; -using Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; -using Nethermind.TxPool; - -namespace Nethermind.Network.P2P.Subprotocols.Eth -{ - public class PooledTxsRequestor(ITxPool txPool, ITxPoolConfig txPoolConfig, ISpecProvider specProvider) : IPooledTxsRequestor - { - private const int MaxNumberOfTxsInOneMsg = 256; - private readonly bool _blobSupportEnabled = txPoolConfig.BlobsSupport.IsEnabled(); - private readonly long _configuredMaxTxSize = txPoolConfig.MaxTxSize ?? long.MaxValue; - - private readonly long _configuredMaxBlobTxSize = txPoolConfig.MaxBlobTxSize is null - ? long.MaxValue - : txPoolConfig.MaxBlobTxSize.Value + (long)specProvider.GetFinalMaxBlobGasPerBlock(); - - private readonly ClockKeyCache _pendingHashes = new(MemoryAllowance.TxHashCacheSize); - - public void RequestTransactions(Action send, IOwnedReadOnlyList hashes) - { - ArrayPoolList discoveredTxHashes = AddMarkUnknownHashes(hashes.AsSpan()); - RequestPooledTransactions(send, discoveredTxHashes); - } - - public void RequestTransactionsEth66(Action send, IOwnedReadOnlyList hashes) - { - ArrayPoolList discoveredTxHashes = AddMarkUnknownHashes(hashes.AsSpan()); - - if (discoveredTxHashes.Count <= MaxNumberOfTxsInOneMsg) - { - RequestPooledTransactionsEth66(send, discoveredTxHashes); - } - else - { - using ArrayPoolList _ = discoveredTxHashes; - - for (int start = 0; start < discoveredTxHashes.Count; start += MaxNumberOfTxsInOneMsg) - { - var end = Math.Min(start + MaxNumberOfTxsInOneMsg, discoveredTxHashes.Count); - - ArrayPoolList hashesToRequest = new(end - start); - hashesToRequest.AddRange(discoveredTxHashes.AsSpan()[start..end]); - RequestPooledTransactionsEth66(send, hashesToRequest); - } - } - } - - public void RequestTransactionsEth68(Action send, IOwnedReadOnlyList hashes, IOwnedReadOnlyList sizes, IOwnedReadOnlyList types) - { - using ArrayPoolList<(Hash256 Hash, byte Type, int Size)> discoveredTxHashesAndSizes = AddMarkUnknownHashesEth68(hashes.AsSpan(), sizes.AsSpan(), types.AsSpan()); - if (discoveredTxHashesAndSizes.Count == 0) return; - - int packetSizeLeft = TransactionsMessage.MaxPacketSize; - ArrayPoolList hashesToRequest = new(discoveredTxHashesAndSizes.Count); - - var discoveredCount = discoveredTxHashesAndSizes.Count; - var toRequestCount = 0; - foreach ((Hash256 hash, byte type, int size) in discoveredTxHashesAndSizes.AsSpan()) - { - int txSize = size; - TxType txType = (TxType)type; - - long maxSize = txType.SupportsBlobs() ? _configuredMaxBlobTxSize : _configuredMaxTxSize; - if (txSize > maxSize) - continue; - - if (txSize > packetSizeLeft && toRequestCount > 0) - { - RequestPooledTransactionsEth66(send, hashesToRequest); - hashesToRequest = new ArrayPoolList(discoveredCount); - packetSizeLeft = TransactionsMessage.MaxPacketSize; - toRequestCount = 0; - } - - if (_blobSupportEnabled || txType != TxType.Blob) - { - hashesToRequest.Add(hash); - packetSizeLeft -= txSize; - toRequestCount++; - } - } - - RequestPooledTransactionsEth66(send, hashesToRequest); - } - - private ArrayPoolList AddMarkUnknownHashes(ReadOnlySpan hashes) - { - ArrayPoolList discoveredTxHashes = new ArrayPoolList(hashes.Length); - for (int i = 0; i < hashes.Length; i++) - { - Hash256 hash = hashes[i]; - if (!txPool.IsKnown(hash) && _pendingHashes.Set(hash)) - { - discoveredTxHashes.Add(hash); - } - } - - return discoveredTxHashes; - } - - private ArrayPoolList<(Hash256, byte, int)> AddMarkUnknownHashesEth68(ReadOnlySpan hashes, ReadOnlySpan sizes, ReadOnlySpan types) - { - ArrayPoolList<(Hash256, byte, int)> discoveredTxHashesAndSizes = new(hashes.Length); - for (int i = 0; i < hashes.Length; i++) - { - Hash256 hash = hashes[i]; - if (!txPool.IsKnown(hash) && !txPool.ContainsTx(hash, (TxType)types[i]) && _pendingHashes.Set(hash)) - { - discoveredTxHashesAndSizes.Add((hash, types[i], sizes[i])); - } - } - - return discoveredTxHashesAndSizes; - } - - private static void RequestPooledTransactions(Action send, IOwnedReadOnlyList hashesToRequest) - { - send(new(hashesToRequest)); - } - - private static void RequestPooledTransactionsEth66(Action send, IOwnedReadOnlyList hashesToRequest) - { - if (hashesToRequest.Count > 0) - { - GetPooledTransactionsMessage msg65 = new(hashesToRequest); - send(new() { EthMessage = msg65 }); - } - else - { - hashesToRequest.Dispose(); - } - } - } -} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Eth62ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Eth62ProtocolHandler.cs index dbd7bdda279c..ac6795f86d1f 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Eth62ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Eth62ProtocolHandler.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -14,7 +14,6 @@ using Nethermind.Logging; using Nethermind.Network.Contract.P2P; using Nethermind.Network.P2P.EventArg; -using Nethermind.Network.P2P.Messages; using Nethermind.Network.P2P.ProtocolHandlers; using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; using Nethermind.Network.Rlpx; @@ -56,10 +55,7 @@ public Eth62ProtocolHandler(ISession session, EnsureGossipPolicy(); } - public void DisableTxFiltering() - { - _floodController.IsEnabled = false; - } + public void DisableTxFiltering() => _floodController.IsEnabled = false; public override byte ProtocolVersion => EthVersions.Eth62; public override string ProtocolCode => Protocol.Eth; @@ -243,7 +239,7 @@ protected void Handle(TransactionsMessage msg) BackgroundTaskScheduler.ScheduleBackgroundTask((iList, 0), _handleSlow); } - private ValueTask HandleSlow((IOwnedReadOnlyList txs, int startIndex) request, CancellationToken cancellationToken) + protected virtual ValueTask HandleSlow((IOwnedReadOnlyList txs, int startIndex) request, CancellationToken cancellationToken) { IOwnedReadOnlyList transactions = request.txs; ReadOnlySpan transactionsSpan = transactions.AsSpan(); @@ -251,6 +247,7 @@ private ValueTask HandleSlow((IOwnedReadOnlyList txs, int startInde { int startIdx = request.startIndex; bool isTrace = Logger.IsTrace; + for (int i = startIdx; i < transactionsSpan.Length; i++) { if (cancellationToken.IsCancellationRequested) diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/TxFloodController.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/TxFloodController.cs index 704f834f56ed..be3dff9aa0e3 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/TxFloodController.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/TxFloodController.cs @@ -1,10 +1,11 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using Nethermind.Core; using Nethermind.Logging; using Nethermind.Stats.Model; +using Nethermind.TxPool; +using System; namespace Nethermind.Network.P2P.Subprotocols.Eth.V62 { @@ -19,12 +20,19 @@ internal class TxFloodController(Eth62ProtocolHandler protocolHandler, ITimestam internal bool IsDowngraded { get; private set; } - public void Report(bool accepted) + public void Report(AcceptTxResult accepted) { TryReset(); if (!accepted) { + if (accepted == AcceptTxResult.Invalid) + { + if (logger.IsDebug) logger.Debug($"Disconnecting {_protocolHandler} due to invalid tx received"); + _protocolHandler.Disconnect(DisconnectReason.Other, $"invalid tx"); + return; + } + _notAcceptedSinceLastCheck++; if (!IsDowngraded && _notAcceptedSinceLastCheck / _checkInterval.TotalSeconds > 10) { diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandler.cs index d0e8872ce1a6..6861021228a8 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandler.cs @@ -1,17 +1,15 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; using Nethermind.Consensus; using Nethermind.Consensus.Scheduler; using Nethermind.Core; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Logging; +using Nethermind.Network.Contract.Messages; using Nethermind.Network.Contract.P2P; +using Nethermind.Network.P2P.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V64; using Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; @@ -19,6 +17,11 @@ using Nethermind.Stats; using Nethermind.Synchronization; using Nethermind.TxPool; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; namespace Nethermind.Network.P2P.Subprotocols.Eth.V65 { @@ -32,18 +35,19 @@ public class Eth65ProtocolHandler( ISyncServer syncServer, IBackgroundTaskScheduler backgroundTaskScheduler, ITxPool txPool, - IPooledTxsRequestor pooledTxsRequestor, IGossipPolicy gossipPolicy, IForkInfo forkInfo, ILogManager logManager, ITxGossipPolicy? transactionsGossipPolicy = null) - : Eth64ProtocolHandler(session, serializer, nodeStatsManager, syncServer, backgroundTaskScheduler, txPool, - gossipPolicy, forkInfo, logManager, transactionsGossipPolicy) + : Eth64ProtocolHandler(session, serializer, nodeStatsManager, syncServer, backgroundTaskScheduler, txPool, gossipPolicy, forkInfo, logManager, transactionsGossipPolicy), + IMessageHandler { public override string Name => "eth65"; public override byte ProtocolVersion => EthVersions.Eth65; + private const int MaxNumberOfTxsInOneMsg = 256; + public override void HandleMessage(ZeroPacket message) { base.HandleMessage(message); @@ -51,16 +55,16 @@ public override void HandleMessage(ZeroPacket message) int size = message.Content.ReadableBytes; switch (message.PacketType) { - case Eth65MessageCode.PooledTransactions: + case Eth65MessageCode.NewPooledTransactionHashes: if (CanReceiveTransactions) { - PooledTransactionsMessage pooledTxMsg = Deserialize(message.Content); - ReportIn(pooledTxMsg, size); - Handle(pooledTxMsg); + using NewPooledTransactionHashesMessage newPooledTxMsg = Deserialize(message.Content); + ReportIn(newPooledTxMsg, size); + Handle(newPooledTxMsg); } else { - const string ignored = $"{nameof(PooledTransactionsMessage)} ignored, syncing"; + const string ignored = $"{nameof(NewPooledTransactionHashesMessage)} ignored, syncing"; ReportIn(ignored, size); } @@ -68,16 +72,16 @@ public override void HandleMessage(ZeroPacket message) case Eth65MessageCode.GetPooledTransactions: HandleInBackground(message, Handle); break; - case Eth65MessageCode.NewPooledTransactionHashes: + case Eth65MessageCode.PooledTransactions: if (CanReceiveTransactions) { - NewPooledTransactionHashesMessage newPooledTxMsg = Deserialize(message.Content); - ReportIn(newPooledTxMsg, size); - Handle(newPooledTxMsg); + PooledTransactionsMessage pooledTxMsg = Deserialize(message.Content); + ReportIn(pooledTxMsg, size); + Handle(pooledTxMsg); } else { - const string ignored = $"{nameof(NewPooledTransactionHashesMessage)} ignored, syncing"; + const string ignored = $"{nameof(PooledTransactionsMessage)} ignored, syncing"; ReportIn(ignored, size); } @@ -87,17 +91,7 @@ public override void HandleMessage(ZeroPacket message) protected virtual void Handle(NewPooledTransactionHashesMessage msg) { - using var _ = msg; - AddNotifiedTransactions(msg.Hashes); - - long startTime = Stopwatch.GetTimestamp(); - - TxPool.Metrics.PendingTransactionsHashesReceived += msg.Hashes.Count; - pooledTxsRequestor.RequestTransactions(Send, msg.Hashes); - - if (Logger.IsTrace) - Logger.Trace($"OUT {Counter:D5} {nameof(NewPooledTransactionHashesMessage)} to {Node:c} " + - $"in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); + RequestPooledTransactions(msg.Hashes); } protected void AddNotifiedTransactions(IReadOnlyList hashes) @@ -147,6 +141,8 @@ internal Task FulfillPooledTransactionsRequest(GetPoo protected override void SendNewTransactionsCore(IEnumerable txs, bool sendFullTx) { + void SendNewPooledTransactionMessage(IOwnedReadOnlyList hashes) => Send(new NewPooledTransactionHashesMessage(hashes)); + if (sendFullTx) { base.SendNewTransactionsCore(txs, true); @@ -180,10 +176,73 @@ protected override void SendNewTransactionsCore(IEnumerable txs, bo } } - private void SendNewPooledTransactionMessage(IOwnedReadOnlyList hashes) + protected virtual void RequestPooledTransactions(IOwnedReadOnlyList hashes) + where GetPooledTransactionsMessage : P2PMessage, INew, GetPooledTransactionsMessage> + { + AddNotifiedTransactions(hashes); + + long startTime = Stopwatch.GetTimestamp(); + TxPool.Metrics.PendingTransactionsHashesReceived += hashes.Count; + + ArrayPoolList newTxHashes = AddMarkUnknownHashes(hashes.AsSpan()); + + if (newTxHashes.Count is 0) + { + newTxHashes.Dispose(); + return; + } + + if (newTxHashes.Count <= MaxNumberOfTxsInOneMsg) + { + Send(GetPooledTransactionsMessage.New(newTxHashes)); + } + else + { + try + { + for (int start = 0; start < newTxHashes.Count; start += MaxNumberOfTxsInOneMsg) + { + int end = Math.Min(start + MaxNumberOfTxsInOneMsg, newTxHashes.Count); + + ArrayPoolList hashesToRequest = new(end - start); + hashesToRequest.AddRange(newTxHashes.AsSpan()[start..end]); + + Send(GetPooledTransactionsMessage.New(hashesToRequest)); + } + } + finally + { + newTxHashes.Dispose(); + } + } + + if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} {nameof(NewPooledTransactionHashesMessage)} to {Node:c} " + + $"in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); + } + + private ArrayPoolList AddMarkUnknownHashes(ReadOnlySpan hashes) + { + ArrayPoolList discoveredTxHashesAndSizes = new(hashes.Length); + + for (int i = 0; i < hashes.Length; i++) + { + Hash256 hash = hashes[i]; + if (!_txPool.IsKnown(hash)) + { + if (_txPool.AnnounceTx(hash, this) is AnnounceResult.New) + { + discoveredTxHashesAndSizes.Add(hash); + } + } + } + + return discoveredTxHashesAndSizes; + } + + public virtual void HandleMessage(PooledTransactionRequestMessage message) { - NewPooledTransactionHashesMessage msg = new(hashes); - Send(msg); + using ArrayPoolList hashesToRetry = new(1) { new Hash256(message.TxHash) }; + RequestPooledTransactions(hashesToRetry); } } } diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Messages/GetPooledTransactionsMessage.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Messages/GetPooledTransactionsMessage.cs index 365a6a83451a..b93a9ec98571 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Messages/GetPooledTransactionsMessage.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Messages/GetPooledTransactionsMessage.cs @@ -1,16 +1,18 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Core; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; -namespace Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages +namespace Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; + +public class GetPooledTransactionsMessage(IOwnedReadOnlyList hashes) : HashesMessage(hashes), INew, GetPooledTransactionsMessage> { - public class GetPooledTransactionsMessage(IOwnedReadOnlyList hashes) : HashesMessage(hashes) - { - public override int PacketType => Eth65MessageCode.GetPooledTransactions; - public override string Protocol => "eth"; + public override int PacketType => Eth65MessageCode.GetPooledTransactions; + public override string Protocol => "eth"; + + public static GetPooledTransactionsMessage New(IOwnedReadOnlyList arg) => new(arg); - public override string ToString() => $"{nameof(GetPooledTransactionsMessage)}({Hashes?.Count})"; - } + public override string ToString() => $"{nameof(GetPooledTransactionsMessage)}({Hashes?.Count})"; } diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandler.cs index d9192294a6c4..1ecb96967d4c 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandler.cs @@ -1,15 +1,13 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; using Nethermind.Consensus; using Nethermind.Consensus.Scheduler; using Nethermind.Core; using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; using Nethermind.Logging; +using Nethermind.Network.Contract.Messages; using Nethermind.Network.Contract.P2P; using Nethermind.Network.P2P.Subprotocols.Eth.V65; using Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; @@ -18,6 +16,9 @@ using Nethermind.Stats; using Nethermind.Synchronization; using Nethermind.TxPool; +using System; +using System.Threading; +using System.Threading.Tasks; using GetPooledTransactionsMessage = Nethermind.Network.P2P.Subprotocols.Eth.V66.Messages.GetPooledTransactionsMessage; using PooledTransactionsMessage = Nethermind.Network.P2P.Subprotocols.Eth.V66.Messages.PooledTransactionsMessage; @@ -32,8 +33,7 @@ public class Eth66ProtocolHandler : Eth65ProtocolHandler private readonly MessageDictionary _bodiesRequests66; private readonly MessageDictionary> _nodeDataRequests66; private readonly MessageDictionary, long)> _receiptsRequests66; - private readonly IPooledTxsRequestor _pooledTxsRequestor; - private readonly Action _sendAction; + public Eth66ProtocolHandler(ISession session, IMessageSerializationService serializer, @@ -41,20 +41,16 @@ public Eth66ProtocolHandler(ISession session, ISyncServer syncServer, IBackgroundTaskScheduler backgroundTaskScheduler, ITxPool txPool, - IPooledTxsRequestor pooledTxsRequestor, IGossipPolicy gossipPolicy, IForkInfo forkInfo, ILogManager logManager, ITxGossipPolicy? transactionsGossipPolicy = null) - : base(session, serializer, nodeStatsManager, syncServer, backgroundTaskScheduler, txPool, pooledTxsRequestor, gossipPolicy, forkInfo, logManager, transactionsGossipPolicy) + : base(session, serializer, nodeStatsManager, syncServer, backgroundTaskScheduler, txPool, gossipPolicy, forkInfo, logManager, transactionsGossipPolicy) { _headersRequests66 = new MessageDictionary>(Send); _bodiesRequests66 = new MessageDictionary(Send); _nodeDataRequests66 = new MessageDictionary>(Send); _receiptsRequests66 = new MessageDictionary, long)>(Send); - _pooledTxsRequestor = pooledTxsRequestor; - // Capture Action once rather than per call - _sendAction = Send; } public override string Name => "eth66"; @@ -89,8 +85,7 @@ public override void HandleMessage(ZeroPacket message) case Eth66MessageCode.PooledTransactions: if (CanReceiveTransactions) { - PooledTransactionsMessage pooledTxMsg - = Deserialize(message.Content); + PooledTransactionsMessage pooledTxMsg = Deserialize(message.Content); ReportIn(pooledTxMsg, size); Handle(pooledTxMsg.EthMessage); } @@ -178,19 +173,8 @@ protected void Handle(ReceiptsMessage msg, long size) _receiptsRequests66.Handle(msg.RequestId, (msg.EthMessage.TxReceipts, size), size); } - protected override void Handle(NewPooledTransactionHashesMessage msg) - { - using var message = msg; - bool isTrace = Logger.IsTrace; - long startTime = Stopwatch.GetTimestamp(); - - TxPool.Metrics.PendingTransactionsHashesReceived += message.Hashes.Count; - _pooledTxsRequestor.RequestTransactionsEth66(_sendAction, message.Hashes); + protected override void Handle(NewPooledTransactionHashesMessage message) => RequestPooledTransactions(message.Hashes); - if (isTrace) - Logger.Trace($"OUT {Counter:D5} {nameof(NewPooledTransactionHashesMessage)} to {Node:c} " + - $"in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); - } protected override async Task> SendRequest(V62.Messages.GetBlockHeadersMessage message, CancellationToken token) { @@ -283,5 +267,11 @@ CancellationToken token return HandleResponse(request, speedType, describeRequestFunc, token); } + + public override void HandleMessage(PooledTransactionRequestMessage message) + { + ArrayPoolList hashesToRetry = new(1) { new Hash256(message.TxHash) }; + RequestPooledTransactions(hashesToRetry); + } } } diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Messages/GetPooledTransactionsMessage.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Messages/GetPooledTransactionsMessage.cs index d4d1a99570bf..81d502d4252a 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Messages/GetPooledTransactionsMessage.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Messages/GetPooledTransactionsMessage.cs @@ -1,16 +1,21 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -namespace Nethermind.Network.P2P.Subprotocols.Eth.V66.Messages +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; + +namespace Nethermind.Network.P2P.Subprotocols.Eth.V66.Messages; + +public class GetPooledTransactionsMessage : Eth66Message, INew, GetPooledTransactionsMessage> { - public class GetPooledTransactionsMessage : Eth66Message + public GetPooledTransactionsMessage() { - public GetPooledTransactionsMessage() - { - } + } - public GetPooledTransactionsMessage(long requestId, V65.Messages.GetPooledTransactionsMessage ethMessage) : base(requestId, ethMessage) - { - } + public GetPooledTransactionsMessage(IOwnedReadOnlyList hashes) : base(MessageConstants.Random.NextLong(), new V65.Messages.GetPooledTransactionsMessage(hashes)) + { } + + public static GetPooledTransactionsMessage New(IOwnedReadOnlyList arg) => new(arg); } diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V67/Eth67ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V67/Eth67ProtocolHandler.cs index 74c754ae670f..d90953907767 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V67/Eth67ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V67/Eth67ProtocolHandler.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Consensus; @@ -23,13 +23,12 @@ public class Eth67ProtocolHandler( ISyncServer syncServer, IBackgroundTaskScheduler backgroundTaskScheduler, ITxPool txPool, - IPooledTxsRequestor pooledTxsRequestor, IGossipPolicy gossipPolicy, IForkInfo forkInfo, ILogManager logManager, ITxGossipPolicy? transactionsGossipPolicy = null) : Eth66ProtocolHandler(session, serializer, nodeStatsManager, syncServer, backgroundTaskScheduler, txPool, - pooledTxsRequestor, gossipPolicy, forkInfo, logManager, transactionsGossipPolicy) + gossipPolicy, forkInfo, logManager, transactionsGossipPolicy) { public override string Name => "eth67"; diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandler.cs index b9d807130b6c..8b44ac53396f 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandler.cs @@ -1,54 +1,59 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Collections.Generic; -using System.Diagnostics; using Nethermind.Consensus; using Nethermind.Consensus.Scheduler; using Nethermind.Core; +using Nethermind.Core.Caching; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; using Nethermind.Logging; using Nethermind.Network.Contract.P2P; +using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V67; using Nethermind.Network.P2P.Subprotocols.Eth.V68.Messages; using Nethermind.Network.Rlpx; using Nethermind.Stats; using Nethermind.Synchronization; using Nethermind.TxPool; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; namespace Nethermind.Network.P2P.Subprotocols.Eth.V68; -public class Eth68ProtocolHandler : Eth67ProtocolHandler +public class Eth68ProtocolHandler(ISession session, + IMessageSerializationService serializer, + INodeStatsManager nodeStatsManager, + ISyncServer syncServer, + IBackgroundTaskScheduler backgroundTaskScheduler, + ITxPool txPool, + IGossipPolicy gossipPolicy, + IForkInfo forkInfo, + ILogManager logManager, + ITxPoolConfig txPoolConfig, + ISpecProvider specProvider, + ITxGossipPolicy? transactionsGossipPolicy = null + ) + : Eth67ProtocolHandler(session, serializer, nodeStatsManager, syncServer, backgroundTaskScheduler, txPool, gossipPolicy, forkInfo, logManager, transactionsGossipPolicy) { - private readonly IPooledTxsRequestor _pooledTxsRequestor; + private readonly bool _blobSupportEnabled = txPoolConfig.BlobsSupport.IsEnabled(); + private readonly long _configuredMaxTxSize = txPoolConfig.MaxTxSize ?? long.MaxValue; - private readonly Action _sendAction; + private readonly long _configuredMaxBlobTxSize = txPoolConfig.MaxBlobTxSize is null + ? long.MaxValue + : txPoolConfig.MaxBlobTxSize.Value + (long)specProvider.GetFinalMaxBlobGasPerBlock(); + + private ClockCache TxShapeAnnouncements { get; } = new(MemoryAllowance.TxHashCacheSize / 10); public override string Name => "eth68"; public override byte ProtocolVersion => EthVersions.Eth68; - public Eth68ProtocolHandler(ISession session, - IMessageSerializationService serializer, - INodeStatsManager nodeStatsManager, - ISyncServer syncServer, - IBackgroundTaskScheduler backgroundTaskScheduler, - ITxPool txPool, - IPooledTxsRequestor pooledTxsRequestor, - IGossipPolicy gossipPolicy, - IForkInfo forkInfo, - ILogManager logManager, - ITxGossipPolicy? transactionsGossipPolicy = null) - : base(session, serializer, nodeStatsManager, syncServer, backgroundTaskScheduler, txPool, pooledTxsRequestor, gossipPolicy, forkInfo, logManager, transactionsGossipPolicy) - { - _pooledTxsRequestor = pooledTxsRequestor; - - // Capture Action once rather than per call - _sendAction = Send; - } - public override void HandleMessage(ZeroPacket message) { int size = message.Content.ReadableBytes; @@ -77,15 +82,15 @@ public override void HandleMessage(ZeroPacket message) private void Handle(NewPooledTransactionHashesMessage68 msg) { - using var message = msg; - bool isTrace = Logger.IsTrace; + using NewPooledTransactionHashesMessage68 message = msg; + if (message.Hashes.Count != message.Types.Count || message.Hashes.Count != message.Sizes.Count) { string errorMessage = $"Wrong format of {nameof(NewPooledTransactionHashesMessage68)} message. " + $"Hashes count: {message.Hashes.Count} " + $"Types count: {message.Types.Count} " + $"Sizes count: {message.Sizes.Count}"; - if (isTrace) Logger.Trace(errorMessage); + if (Logger.IsTrace) Logger.Trace(errorMessage); throw new SubprotocolException(errorMessage); } @@ -94,11 +99,86 @@ private void Handle(NewPooledTransactionHashesMessage68 msg) AddNotifiedTransactions(message.Hashes); - long startTime = isTrace ? Stopwatch.GetTimestamp() : 0; + long startTime = Logger.IsTrace ? Stopwatch.GetTimestamp() : 0; - _pooledTxsRequestor.RequestTransactionsEth68(_sendAction, message.Hashes, message.Sizes, message.Types); + RequestPooledTransactions(message.Hashes, message.Sizes, message.Types); - if (isTrace) Logger.Trace($"OUT {Counter:D5} {nameof(NewPooledTransactionHashesMessage68)} to {Node:c} in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds}ms"); + if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} {nameof(NewPooledTransactionHashesMessage68)} to {Node:c} in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds}ms"); + } + + protected void RequestPooledTransactions(IOwnedReadOnlyList hashes, IOwnedReadOnlyList sizes, IOwnedReadOnlyList types) + { + using ArrayPoolListRef newTxHashesIndexes = AddMarkUnknownHashes(hashes.AsSpan()); + + if (newTxHashesIndexes.Count == 0) + { + hashes.Dispose(); + sizes.Dispose(); + types.Dispose(); + + return; + } + + int packetSizeLeft = TransactionsMessage.MaxPacketSize; + ArrayPoolList hashesToRequest = new(newTxHashesIndexes.Count); + + int discoveredCount = newTxHashesIndexes.Count; + int toRequestCount = 0; + + foreach (int index in newTxHashesIndexes.AsSpan()) + { + Hash256 hash = hashes[index]; + int txSize = sizes[index]; + TxType txType = (TxType)types[index]; + TxShapeAnnouncements.Set(hash, (txSize, txType)); + + long maxTxSize = txType.SupportsBlobs() ? _configuredMaxBlobTxSize : _configuredMaxTxSize; + + if (txSize > maxTxSize) + continue; + + if ((txSize > packetSizeLeft && toRequestCount > 0) || toRequestCount >= 256) + { + Send(V66.Messages.GetPooledTransactionsMessage.New(hashesToRequest)); + hashesToRequest = new ArrayPoolList(discoveredCount); + packetSizeLeft = TransactionsMessage.MaxPacketSize; + toRequestCount = 0; + } + + if (_blobSupportEnabled || txType != TxType.Blob) + { + hashesToRequest.Add(hash); + packetSizeLeft -= txSize; + toRequestCount++; + } + } + + if (hashesToRequest.Count is not 0) + { + Send(V66.Messages.GetPooledTransactionsMessage.New(hashesToRequest)); + } + else + { + hashesToRequest.Dispose(); + } + } + + private ArrayPoolListRef AddMarkUnknownHashes(ReadOnlySpan hashes) + { + ArrayPoolListRef discoveredTxHashesAndSizes = new(hashes.Length); + for (int i = 0; i < hashes.Length; i++) + { + Hash256 hash = hashes[i]; + if (!_txPool.IsKnown(hash)) + { + if (_txPool.AnnounceTx(hash, this) is AnnounceResult.New) + { + discoveredTxHashesAndSizes.Add(i); + } + } + } + + return discoveredTxHashesAndSizes; } protected override void SendNewTransactionCore(Transaction tx) @@ -165,4 +245,21 @@ private void SendMessage(IOwnedReadOnlyList types, IOwnedReadOnlyList NewPooledTransactionHashesMessage68 message = new(types, sizes, hashes); Send(message); } + + protected override ValueTask HandleSlow((IOwnedReadOnlyList txs, int startIndex) request, CancellationToken cancellationToken) + { + int startIdx = request.startIndex; + for (int i = startIdx; i < request.txs.Count; i++) + { + if (!ValidateSizeAndType(request.txs[i])) + { + throw new SubprotocolException("invalid pooled tx type or size"); + } + } + + return base.HandleSlow(request, cancellationToken); + } + + private bool ValidateSizeAndType(Transaction tx) + => !TxShapeAnnouncements.Delete(tx.Hash!, out (int Size, TxType Type) txShape) || (tx.GetLength() == txShape.Size && tx.Type == txShape.Type); } diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V69/Eth69ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V69/Eth69ProtocolHandler.cs index 784defdf4892..4616984be10d 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V69/Eth69ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V69/Eth69ProtocolHandler.cs @@ -1,15 +1,14 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; using System.Threading; using System.Threading.Tasks; -using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Synchronization; using Nethermind.Consensus; using Nethermind.Consensus.Scheduler; using Nethermind.Core; - +using Nethermind.Core.Specs; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Network.Contract.P2P; @@ -35,14 +34,14 @@ public class Eth69ProtocolHandler( ISyncServer syncServer, IBackgroundTaskScheduler backgroundTaskScheduler, ITxPool txPool, - IPooledTxsRequestor pooledTxsRequestor, IGossipPolicy gossipPolicy, IForkInfo forkInfo, - IBlockFinder blockFinder, ILogManager logManager, + ITxPoolConfig txPoolConfig, + ISpecProvider specProvider, ITxGossipPolicy? transactionsGossipPolicy = null) : Eth68ProtocolHandler(session, serializer, nodeStatsManager, syncServer, backgroundTaskScheduler, txPool, - pooledTxsRequestor, gossipPolicy, forkInfo, logManager, transactionsGossipPolicy), ISyncPeer + gossipPolicy, forkInfo, logManager, txPoolConfig, specProvider, transactionsGossipPolicy), ISyncPeer { public override string Name => "eth69"; @@ -153,7 +152,7 @@ protected override void NotifyOfStatus(BlockHeader head) NetworkId = SyncServer.NetworkId, GenesisHash = SyncServer.Genesis.Hash!, ForkId = _forkInfo.GetForkId(head.Number, head.Timestamp), - EarliestBlock = blockFinder.GetLowestBlock(), + EarliestBlock = SyncServer.LowestBlock, LatestBlock = head.Number, LatestBlockHash = head.Hash! }; diff --git a/src/Nethermind/Nethermind.Network/ProtocolsManager.cs b/src/Nethermind/Nethermind.Network/ProtocolsManager.cs index 2419d87d427d..3fc08e163bc7 100644 --- a/src/Nethermind/Nethermind.Network/ProtocolsManager.cs +++ b/src/Nethermind/Nethermind.Network/ProtocolsManager.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -7,11 +7,11 @@ using System.Linq; using System.Numerics; using Autofac.Features.AttributeFilters; -using Nethermind.Blockchain.Find; using Nethermind.Config; using Nethermind.Consensus; using Nethermind.Consensus.Scheduler; using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; using Nethermind.Db; using Nethermind.Logging; using Nethermind.Network.Contract.P2P; @@ -19,7 +19,6 @@ using Nethermind.Network.P2P.EventArg; using Nethermind.Network.P2P.Messages; using Nethermind.Network.P2P.ProtocolHandlers; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Network.P2P.Subprotocols.Eth.V66; using Nethermind.Network.P2P.Subprotocols.Eth.V67; using Nethermind.Network.P2P.Subprotocols.Eth.V68; @@ -57,7 +56,6 @@ public class ProtocolsManager : IProtocolsManager private readonly ISyncPeerPool _syncPool; private readonly ISyncServer _syncServer; private readonly ITxPool _txPool; - private readonly IPooledTxsRequestor _pooledTxsRequestor; private readonly IDiscoveryApp _discoveryApp; private readonly IMessageSerializationService _serializer; private readonly IRlpxHost _rlpxHost; @@ -68,19 +66,19 @@ public class ProtocolsManager : IProtocolsManager private readonly IGossipPolicy _gossipPolicy; private readonly ITxGossipPolicy _txGossipPolicy; private readonly ILogManager _logManager; + private readonly ITxPoolConfig _txPoolConfdig; + private readonly ISpecProvider _specProvider; private readonly ILogger _logger; private readonly IDictionary> _protocolFactories; private readonly HashSet _capabilities = DefaultCapabilities.ToHashSet(); private readonly IBackgroundTaskScheduler _backgroundTaskScheduler; private readonly ISnapServer? _snapServer; - private readonly IBlockFinder _blockFinder; public ProtocolsManager( ISyncPeerPool syncPeerPool, ISyncServer syncServer, IBackgroundTaskScheduler backgroundTaskScheduler, ITxPool txPool, - IPooledTxsRequestor pooledTxsRequestor, IDiscoveryApp discoveryApp, IMessageSerializationService serializationService, IRlpxHost rlpxHost, @@ -90,15 +88,15 @@ public ProtocolsManager( IForkInfo forkInfo, IGossipPolicy gossipPolicy, IWorldStateManager worldStateManager, - IBlockFinder blockFinder, ILogManager logManager, + ITxPoolConfig txPoolConfdig, + ISpecProvider specProvider, ITxGossipPolicy? transactionsGossipPolicy = null) { _syncPool = syncPeerPool ?? throw new ArgumentNullException(nameof(syncPeerPool)); _syncServer = syncServer ?? throw new ArgumentNullException(nameof(syncServer)); _backgroundTaskScheduler = backgroundTaskScheduler ?? throw new ArgumentNullException(nameof(backgroundTaskScheduler)); _txPool = txPool ?? throw new ArgumentNullException(nameof(txPool)); - _pooledTxsRequestor = pooledTxsRequestor ?? throw new ArgumentNullException(nameof(pooledTxsRequestor)); _discoveryApp = discoveryApp ?? throw new ArgumentNullException(nameof(discoveryApp)); _serializer = serializationService ?? throw new ArgumentNullException(nameof(serializationService)); _rlpxHost = rlpxHost ?? throw new ArgumentNullException(nameof(rlpxHost)); @@ -109,8 +107,9 @@ public ProtocolsManager( _gossipPolicy = gossipPolicy ?? throw new ArgumentNullException(nameof(gossipPolicy)); _txGossipPolicy = transactionsGossipPolicy ?? ShouldGossip.Instance; _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); + _txPoolConfdig = txPoolConfdig; + _specProvider = specProvider; _snapServer = worldStateManager.SnapServer; - _blockFinder = blockFinder ?? throw new ArgumentNullException(nameof(blockFinder)); _logger = _logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _protocolFactories = GetProtocolFactories(); @@ -222,12 +221,12 @@ private IDictionary> GetProtocolFa }, [Protocol.Eth] = (session, version) => { - var ethHandler = version switch + Eth66ProtocolHandler ethHandler = version switch { - 66 => new Eth66ProtocolHandler(session, _serializer, _stats, _syncServer, _backgroundTaskScheduler, _txPool, _pooledTxsRequestor, _gossipPolicy, _forkInfo, _logManager, _txGossipPolicy), - 67 => new Eth67ProtocolHandler(session, _serializer, _stats, _syncServer, _backgroundTaskScheduler, _txPool, _pooledTxsRequestor, _gossipPolicy, _forkInfo, _logManager, _txGossipPolicy), - 68 => new Eth68ProtocolHandler(session, _serializer, _stats, _syncServer, _backgroundTaskScheduler, _txPool, _pooledTxsRequestor, _gossipPolicy, _forkInfo, _logManager, _txGossipPolicy), - 69 => new Eth69ProtocolHandler(session, _serializer, _stats, _syncServer, _backgroundTaskScheduler, _txPool, _pooledTxsRequestor, _gossipPolicy, _forkInfo, _blockFinder, _logManager, _txGossipPolicy), + 66 => new Eth66ProtocolHandler(session, _serializer, _stats, _syncServer, _backgroundTaskScheduler, _txPool, _gossipPolicy, _forkInfo, _logManager, _txGossipPolicy), + 67 => new Eth67ProtocolHandler(session, _serializer, _stats, _syncServer, _backgroundTaskScheduler, _txPool, _gossipPolicy, _forkInfo, _logManager, _txGossipPolicy), + 68 => new Eth68ProtocolHandler(session, _serializer, _stats, _syncServer, _backgroundTaskScheduler, _txPool, _gossipPolicy, _forkInfo, _logManager, _txPoolConfdig, _specProvider, _txGossipPolicy), + 69 => new Eth69ProtocolHandler(session, _serializer, _stats, _syncServer, _backgroundTaskScheduler, _txPool, _gossipPolicy, _forkInfo, _logManager, _txPoolConfdig, _specProvider, _txGossipPolicy), _ => throw new NotSupportedException($"Eth protocol version {version} is not supported.") }; @@ -236,7 +235,7 @@ private IDictionary> GetProtocolFa }, [Protocol.Snap] = (session, version) => { - var handler = version switch + SnapProtocolHandler handler = version switch { 1 => new SnapProtocolHandler(session, _stats, _serializer, _backgroundTaskScheduler, _logManager, _snapServer), _ => throw new NotSupportedException($"{Protocol.Snap}.{version} is not supported.") @@ -247,7 +246,7 @@ private IDictionary> GetProtocolFa }, [Protocol.NodeData] = (session, version) => { - var handler = version switch + NodeDataProtocolHandler handler = version switch { 1 => new NodeDataProtocolHandler(session, _serializer, _stats, _syncServer, _backgroundTaskScheduler, _logManager), _ => throw new NotSupportedException($"{Protocol.NodeData}.{version} is not supported.") diff --git a/src/Nethermind/Nethermind.Network/StaticNodes/StaticNodesManager.cs b/src/Nethermind/Nethermind.Network/StaticNodes/StaticNodesManager.cs index 6c41afdabad5..74a541f3ff34 100644 --- a/src/Nethermind/Nethermind.Network/StaticNodes/StaticNodesManager.cs +++ b/src/Nethermind/Nethermind.Network/StaticNodes/StaticNodesManager.cs @@ -4,172 +4,111 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Net.Sockets; using System.Runtime.CompilerServices; -using System.Text.Json; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using Nethermind.Config; using Nethermind.Core.Crypto; using Nethermind.Logging; -using Nethermind.Serialization.Json; using Nethermind.Stats.Model; -namespace Nethermind.Network.StaticNodes +namespace Nethermind.Network.StaticNodes; + +public class StaticNodesManager(string staticNodesPath, ILogManager logManager) : NodesManager(staticNodesPath, logManager.GetClassLogger()), IStaticNodesManager { - public class StaticNodesManager : IStaticNodesManager + public IEnumerable Nodes => _nodes.Values; + + public async Task InitAsync() { - private ConcurrentDictionary _nodes = new(); + ConcurrentDictionary nodes = await ParseNodes("static-nodes.json"); - private readonly string _staticNodesPath; - private readonly ILogger _logger; + LogNodeList("Static nodes", nodes); + + _nodes = nodes; + } - public StaticNodesManager(string staticNodesPath, ILogManager logManager) + public async Task AddAsync(string enode, bool updateFile = true) + { + NetworkNode networkNode = new(enode); + if (!_nodes.TryAdd(networkNode.NodeId, networkNode)) { - _staticNodesPath = staticNodesPath.GetApplicationResourcePath(); - _logger = logManager.GetClassLogger(); + if (_logger.IsInfo) _logger.Info($"Static node was already added: {enode}"); + return false; } - public IEnumerable Nodes => _nodes.Values; + if (_logger.IsInfo) _logger.Info($"Static node added: {enode}"); - private static readonly char[] separator = new[] { '\r', '\n' }; + Node node = new(networkNode); + NodeAdded?.Invoke(this, new NodeEventArgs(node)); - public async Task InitAsync() + if (updateFile) { - if (!File.Exists(_staticNodesPath)) - { - if (_logger.IsDebug) _logger.Debug($"Static nodes file was not found for path: {_staticNodesPath}"); - - return; - } - - string data = await File.ReadAllTextAsync(_staticNodesPath); - string[] nodes = GetNodes(data); - if (_logger.IsInfo) - _logger.Info($"Loaded {nodes.Length} static nodes from file: {Path.GetFullPath(_staticNodesPath)}"); - if (nodes.Length != 0) - { - if (_logger.IsDebug) _logger.Debug($"Static nodes: {Environment.NewLine}{data}"); - } + await SaveFileAsync(); + } - List networkNodes = new(); - foreach (string? n in nodes) - { - try - { - NetworkNode networkNode = new(n); - networkNodes.Add(networkNode); - } - catch (Exception exception) when (exception is ArgumentException or SocketException) - { - if (_logger.IsError) _logger.Error("Unable to process node. ", exception); - } - } + return true; + } - _nodes = new ConcurrentDictionary(networkNodes.ToDictionary(static n => n.NodeId, static n => n)); + public async Task RemoveAsync(string enode, bool updateFile = true) + { + NetworkNode networkNode = new(enode); + if (!_nodes.TryRemove(networkNode.NodeId, out _)) + { + if (_logger.IsInfo) _logger.Info($"Static node was not found: {enode}"); + return false; } - private static string[] GetNodes(string data) + if (_logger.IsInfo) _logger.Info($"Static node was removed: {enode}"); + Node node = new(networkNode); + NodeRemoved?.Invoke(this, new NodeEventArgs(node)); + if (updateFile) { - string[] nodes; - try - { - nodes = JsonSerializer.Deserialize(data) ?? []; - } - catch (JsonException) - { - nodes = data.Split(separator, StringSplitOptions.RemoveEmptyEntries); - } - - return nodes.Distinct().ToArray(); + await SaveFileAsync(); } - public async Task AddAsync(string enode, bool updateFile = true) - { - NetworkNode networkNode = new(enode); - if (!_nodes.TryAdd(networkNode.NodeId, networkNode)) - { - if (_logger.IsInfo) _logger.Info($"Static node was already added: {enode}"); - return false; - } + return true; + } - if (_logger.IsInfo) _logger.Info($"Static node added: {enode}"); - Node node = new(networkNode); - NodeAdded?.Invoke(this, new NodeEventArgs(node)); - if (updateFile) - { - await SaveFileAsync(); - } + public bool IsStatic(string enode) + { + NetworkNode node = new(enode); + return _nodes.TryGetValue(node.NodeId, out NetworkNode staticNode) && string.Equals(staticNode.Host, + node.Host, StringComparison.OrdinalIgnoreCase); + } - return true; - } + public async IAsyncEnumerable DiscoverNodes([EnumeratorCancellation] CancellationToken cancellationToken) + { + Channel ch = Channel.CreateBounded(128); // Some reasonably large value - public async Task RemoveAsync(string enode, bool updateFile = true) + foreach (Node node in _nodes.Values.Select(n => new Node(n))) { - NetworkNode networkNode = new(enode); - if (!_nodes.TryRemove(networkNode.NodeId, out _)) - { - if (_logger.IsInfo) _logger.Info($"Static node was not found: {enode}"); - return false; - } - - if (_logger.IsInfo) _logger.Info($"Static node was removed: {enode}"); - Node node = new(networkNode); - NodeRemoved?.Invoke(this, new NodeEventArgs(node)); - if (updateFile) - { - await SaveFileAsync(); - } - - return true; + cancellationToken.ThrowIfCancellationRequested(); + yield return node; } - public bool IsStatic(string enode) + void handler(object? _, NodeEventArgs args) { - NetworkNode node = new(enode); - return _nodes.TryGetValue(node.NodeId, out NetworkNode staticNode) && string.Equals(staticNode.Host, - node.Host, StringComparison.OrdinalIgnoreCase); + ch.Writer.TryWrite(args.Node); } - private Task SaveFileAsync() - => File.WriteAllTextAsync(_staticNodesPath, - JsonSerializer.Serialize(_nodes.Select(static n => n.Value.ToString()), EthereumJsonSerializer.JsonOptionsIndented)); - - public async IAsyncEnumerable DiscoverNodes([EnumeratorCancellation] CancellationToken cancellationToken) + try { - Channel ch = Channel.CreateBounded(128); // Some reasonably large value + NodeAdded += handler; - foreach (Node node in _nodes.Values.Select(n => new Node(n))) + await foreach (Node node in ch.Reader.ReadAllAsync(cancellationToken)) { - cancellationToken.ThrowIfCancellationRequested(); yield return node; } - - void handler(object? _, NodeEventArgs args) - { - ch.Writer.TryWrite(args.Node); - } - - try - { - NodeAdded += handler; - - await foreach (Node node in ch.Reader.ReadAllAsync(cancellationToken)) - { - yield return node; - } - } - finally - { - NodeAdded -= handler; - } } + finally + { + NodeAdded -= handler; + } + } - private event EventHandler? NodeAdded; + private event EventHandler? NodeAdded; - public event EventHandler? NodeRemoved; - } + public event EventHandler? NodeRemoved; } diff --git a/src/Nethermind/Nethermind.Network/StaticNodes/static-nodes.json b/src/Nethermind/Nethermind.Network/StaticNodes/static-nodes.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/src/Nethermind/Nethermind.Network/StaticNodes/static-nodes.json @@ -0,0 +1 @@ +[] diff --git a/src/Nethermind/Nethermind.Network/TrustedNodes/TrustedNodesManager.cs b/src/Nethermind/Nethermind.Network/TrustedNodes/TrustedNodesManager.cs index ad4b2cbd1dae..a76405c19f04 100644 --- a/src/Nethermind/Nethermind.Network/TrustedNodes/TrustedNodesManager.cs +++ b/src/Nethermind/Nethermind.Network/TrustedNodes/TrustedNodesManager.cs @@ -1,192 +1,131 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Config; +using Nethermind.Core.Crypto; +using Nethermind.Logging; +using Nethermind.Stats.Model; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Sockets; using System.Runtime.CompilerServices; -using System.Text.Json; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; -using Nethermind.Config; -using Nethermind.Core.Crypto; -using Nethermind.Logging; -using Nethermind.Stats.Model; -namespace Nethermind.Network +namespace Nethermind.Network; + +public class TrustedNodesManager(string trustedNodesPath, ILogManager logManager) + : NodesManager(trustedNodesPath, logManager.GetClassLogger()), ITrustedNodesManager { - public class TrustedNodesManager : ITrustedNodesManager + private readonly Channel _nodeChannel = Channel.CreateBounded( + new BoundedChannelOptions(1 << 16) // capacity of 2^16 = 65536 { - private ConcurrentDictionary _nodes = new(); - private readonly string _trustedNodesPath; - private readonly ILogger _logger; - private readonly Channel _nodeChannel = Channel.CreateBounded( - new BoundedChannelOptions(1 << 16) // capacity of 2^16 = 65536 - { - // "Wait" to have writers wait until there is space. - FullMode = BoundedChannelFullMode.Wait - }); + // "Wait" to have writers wait until there is space. + FullMode = BoundedChannelFullMode.Wait + }); - public TrustedNodesManager(string trustedNodesPath, ILogManager logManager) - { - _trustedNodesPath = trustedNodesPath.GetApplicationResourcePath(); - _logger = logManager.GetClassLogger(); - } - - public IEnumerable Nodes => _nodes.Values; + public IEnumerable Nodes => _nodes.Values; - public async Task InitAsync() - { - if (!File.Exists(_trustedNodesPath)) - { - if (_logger.IsDebug) _logger.Debug($"Trusted nodes file not found at: {_trustedNodesPath}"); - return; - } - - var nodes = new ConcurrentDictionary(); + public async Task InitAsync() + { + ConcurrentDictionary nodes = await ParseNodes("trusted-nodes.json"); - await foreach (string line in File.ReadLinesAsync(_trustedNodesPath)) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - try - { - NetworkNode node = new NetworkNode(line); - nodes.TryAdd(node.NodeId, node); - } - catch (Exception ex) when (ex is ArgumentException or SocketException) - { - if (_logger.IsError) - { - _logger.Error($"Failed to parse '{line}' as a trusted node.", ex); - } - } - } + LogNodeList("Trusted nodes", nodes); - if (_logger.IsInfo) - { - _logger.Info($"Loaded {nodes.Count} trusted nodes from: {Path.GetFullPath(_trustedNodesPath)}"); - } - if (_logger.IsDebug && !nodes.IsEmpty) - { - _logger.Debug("Trusted nodes:\n" + string.Join(Environment.NewLine, nodes.Values.Select(n => n.ToString()))); - } + _nodes = nodes; + } - _nodes = nodes; + public async IAsyncEnumerable DiscoverNodes([EnumeratorCancellation] CancellationToken cancellationToken) + { + // yield existing nodes. + foreach (NetworkNode netNode in _nodes.Values) + { + cancellationToken.ThrowIfCancellationRequested(); + yield return new Node(netNode) { IsTrusted = true }; } - - // ---- INodeSource requirement: IAsyncEnumerable ---- - public async IAsyncEnumerable DiscoverNodes([EnumeratorCancellation] CancellationToken cancellationToken) + // yield new nodes as they are added via the channel + await foreach (Node node in _nodeChannel.Reader.ReadAllAsync(cancellationToken)) { - // yield existing nodes. - foreach (NetworkNode netNode in _nodes.Values) - { - cancellationToken.ThrowIfCancellationRequested(); - yield return new Node(netNode) { IsTrusted = true }; - } - - // yield new nodes as they are added via the channel - await foreach (Node node in _nodeChannel.Reader.ReadAllAsync(cancellationToken)) - { - yield return node; - } + yield return node; } + } - public async Task AddAsync(Enode enode, bool updateFile = true) + public async Task AddAsync(Enode enode, bool updateFile = true) + { + NetworkNode networkNode = new(enode); + if (!_nodes.TryAdd(networkNode.NodeId, networkNode)) { - NetworkNode networkNode = new NetworkNode(enode); - if (!_nodes.TryAdd(networkNode.NodeId, networkNode)) - { - if (_logger.IsInfo) - { - _logger.Info($"Trusted node was already added: {enode}"); - } - return false; - } - if (_logger.IsInfo) { - _logger.Info($"Trusted node added: {enode}"); - } - - // Publish the newly added node to the channel so DiscoverNodes will yield it. - Node newNode = new Node(networkNode) { IsTrusted = true }; - await _nodeChannel.Writer.WriteAsync(newNode); - - if (updateFile) - { - await SaveFileAsync(); + _logger.Info($"Trusted node was already added: {enode}"); } - return true; + return false; } - public async Task RemoveAsync(Enode enode, bool updateFile = true) + if (_logger.IsInfo) { - NetworkNode networkNode = new(enode.ToString()); - if (!_nodes.TryRemove(networkNode.NodeId, out _)) - { - if (_logger.IsInfo) - { - _logger.Info($"Trusted node was not found: {enode}"); - } - return false; - } - - if (_logger.IsInfo) - { - _logger.Info($"Trusted node was removed: {enode}"); - } - - if (updateFile) - { - await SaveFileAsync(); - } + _logger.Info($"Trusted node added: {enode}"); + } - OnNodeRemoved(networkNode); + // Publish the newly added node to the channel so DiscoverNodes will yield it. + Node newNode = new(networkNode) { IsTrusted = true }; + await _nodeChannel.Writer.WriteAsync(newNode); - return true; + if (updateFile) + { + await SaveFileAsync(); } + return true; + } - public bool IsTrusted(Enode enode) + public async Task RemoveAsync(Enode enode, bool updateFile = true) + { + NetworkNode networkNode = new(enode.ToString()); + if (!_nodes.TryRemove(networkNode.NodeId, out _)) { - if (enode.PublicKey is null) - { - return false; - } - if (_nodes.TryGetValue(enode.PublicKey, out NetworkNode storedNode)) + if (_logger.IsInfo) { - // Compare not only the public key, but also the host and port. - return storedNode.Host == enode.HostIp?.ToString() && storedNode.Port == enode.Port; + _logger.Info($"Trusted node was not found: {enode}"); } return false; } + if (_logger.IsInfo) + { + _logger.Info($"Trusted node was removed: {enode}"); + } - - // ---- INodeSource requirement: event EventHandler ---- - public event EventHandler? NodeRemoved; - - private void OnNodeRemoved(NetworkNode node) + if (updateFile) { - Node nodeForEvent = new Node(node); - NodeRemoved?.Invoke(this, new NodeEventArgs(nodeForEvent)); + await SaveFileAsync(); } + OnNodeRemoved(networkNode); - private async Task SaveFileAsync() + return true; + } + + public bool IsTrusted(Enode enode) + { + if (enode.PublicKey is null) { - IEnumerable enodes = _nodes.Values.Select(n => n.ToString()); - using (FileStream stream = File.Create(_trustedNodesPath)) - { - await JsonSerializer.SerializeAsync(stream, enodes, new JsonSerializerOptions { WriteIndented = true }); - } + return false; + } + if (_nodes.TryGetValue(enode.PublicKey, out NetworkNode storedNode)) + { + // Compare not only the public key, but also the host and port. + return storedNode.Host == enode.HostIp?.ToString() && storedNode.Port == enode.Port; } + return false; + } + + public event EventHandler? NodeRemoved; + + private void OnNodeRemoved(NetworkNode node) + { + Node nodeForEvent = new(node); + NodeRemoved?.Invoke(this, new NodeEventArgs(nodeForEvent)); } } diff --git a/src/Nethermind/Nethermind.Network/TrustedNodes/trusted-nodes.json b/src/Nethermind/Nethermind.Network/TrustedNodes/trusted-nodes.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/src/Nethermind/Nethermind.Network/TrustedNodes/trusted-nodes.json @@ -0,0 +1 @@ +[] diff --git a/src/Nethermind/Nethermind.Optimism/CL/P2P/OptimismCLP2P.cs b/src/Nethermind/Nethermind.Optimism/CL/P2P/OptimismCLP2P.cs index 9790024c45f3..198c101bfdfd 100644 --- a/src/Nethermind/Nethermind.Optimism/CL/P2P/OptimismCLP2P.cs +++ b/src/Nethermind/Nethermind.Optimism/CL/P2P/OptimismCLP2P.cs @@ -265,7 +265,7 @@ private bool TryValidateAndDecodePayload(byte[] msg, [MaybeNullWhen(false)] out return false; } - using ArrayPoolList decompressed = new(length, length); + using ArrayPoolListRef decompressed = new(length, length); Snappy.Decompress(msg, decompressed.AsSpan()); Span signature = decompressed.AsSpan()[..65]; diff --git a/src/Nethermind/Nethermind.Optimism/OptimismReceiptStorageDecoder.cs b/src/Nethermind/Nethermind.Optimism/OptimismReceiptStorageDecoder.cs index 6434742d6069..dc046174dd10 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismReceiptStorageDecoder.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismReceiptStorageDecoder.cs @@ -41,7 +41,7 @@ public OptimismTxReceipt Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = int sequenceLength = rlpStream.ReadSequenceLength(); int logEntriesCheck = sequenceLength + rlpStream.Position; - using ArrayPoolList logEntries = new(sequenceLength * 2 / LengthOfAddressRlp); + using ArrayPoolListRef logEntries = new(sequenceLength * 2 / LengthOfAddressRlp); while (rlpStream.Position < logEntriesCheck) { @@ -104,7 +104,7 @@ public OptimismTxReceipt Decode(ref ValueDecoderContext decoderContext, int logEntriesCheck = sequenceLength + decoderContext.Position; // Don't know the size exactly, I'll just assume its just an address and add some margin - using ArrayPoolList logEntries = new(sequenceLength * 2 / LengthOfAddressRlp); + using ArrayPoolListRef logEntries = new(sequenceLength * 2 / LengthOfAddressRlp); while (decoderContext.Position < logEntriesCheck) { logEntries.Add(CompactLogEntryDecoder.Decode(ref decoderContext, RlpBehaviors.AllowExtraBytes)!); diff --git a/src/Nethermind/Nethermind.Optimism/OptimismTransactionProcessor.cs b/src/Nethermind/Nethermind.Optimism/OptimismTransactionProcessor.cs index 5108f8492620..3976b0c9919b 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismTransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismTransactionProcessor.cs @@ -45,7 +45,7 @@ protected override TransactionResult Execute(Transaction tx, ITxTracer tracer, E TransactionResult result = base.Execute(tx, tracer, opts); - if (!result && tx.IsDeposit() && result.Error != "block gas limit exceeded") + if (!result && tx.IsDeposit() && result.Error != TransactionResult.ErrorType.BlockGasLimitExceeded) { // deposit tx should be included WorldState.Restore(snapshot); diff --git a/src/Nethermind/Nethermind.Overseer.Test/AuRaTest.cs b/src/Nethermind/Nethermind.Overseer.Test/AuRaTest.cs deleted file mode 100644 index e075e10530ad..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/AuRaTest.cs +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; -using Nethermind.Overseer.Test.Framework; - -using NUnit.Framework; - -namespace Nethermind.Overseer.Test -{ - [Explicit] - public class AuRaTests : TestBuilder - { - [SetUp] - public void Setup() - { - } - - [Test] - public async Task One_validator() - { - StartAuRaMiner("auraval1", "0xcff9b5a51f50cfddbbd227a273c769164dfe6b6185b56f63e4eb2c545bf5ca38") - .Wait(5000) - .Kill("auraval1"); - - await ScenarioCompletion; - } - - [Test] - public async Task Multiple_validators() - { - (string Name, string Address, string PrivateKey)[] validators = new (string Name, string Address, string PrivateKey)[] - { - ("auraval11", "0x557abc72a6594d1bd9a655a1cb58a595526416c8", "0xcff9b5a51f50cfddbbd227a273c769164dfe6b6185b56f63e4eb2c545bf5ca38"), - ("auraval22", "0x69399093be61566a1c86b09bd02612c6bf31214f", "0xcb807c162517bfb179adfeee0d440b81e0bba770e377be4f887e0a4e6c27575d"), - ("auraval33", "0x4cb87ff61e0e3f9f4043f69fe391a62b5a018b97", "0x2429abae64ce7db0f75941082dc6fa1de10c48a7907f29f54c1c1e9f5bd2baf3"), - }; - - var auRaState = new AuRaState(); - - var context = - StartAuRaMiner(validators[0].Name, validators[0].PrivateKey) - .StartAuRaMiner(validators[1].Name, validators[1].PrivateKey) - .StartAuRaMiner(validators[2].Name, validators[2].PrivateKey) - .SetContext(new AuRaContext(auRaState)) - .Wait(40000) - .SwitchNode(validators[1].Name) - .ReadBlockNumber(); - - await ScenarioCompletion; - - context.ReadBlockAuthors() - .LeaveContext() - .KillAll(); - - await ScenarioCompletion; - - var expectedCount = 14; - - auRaState.BlocksCount.Should().BeGreaterThanOrEqualTo(expectedCount, $"at least {expectedCount} steps."); - - var blockNumbers = auRaState.Blocks.Take(expectedCount).Select(v => v.Key); - blockNumbers.Should().BeEquivalentTo(Enumerable.Range(1, expectedCount), "block numbers sequential from 1."); - - var steps = auRaState.Blocks.Take(expectedCount).Select(v => v.Value.Step); - var startStep = auRaState.Blocks.First().Value.Step; - steps.Should().BeEquivalentTo(Enumerable.Range(0, expectedCount).Select(i => i + startStep), $"steps sequential from {startStep}."); - - var authors = auRaState.Blocks.Take(expectedCount).Select(v => v.Value.Author).Distinct(); - authors.Should().Contain(validators.Select(v => v.Address), "each validator produced a block."); - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/BasicTests.cs b/src/Nethermind/Nethermind.Overseer.Test/BasicTests.cs deleted file mode 100644 index 97b89eae4c90..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/BasicTests.cs +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Threading.Tasks; -using Nethermind.Overseer.Test.Framework; -using NUnit.Framework; - -namespace Nethermind.Overseer.Test -{ - [Explicit] - public class BasicTests : TestBuilder - { - [SetUp] - public void Setup() - { - } - - [Test] - public async Task Test1() - { - StartCliqueNode("basicnode1") - .Wait(3000) - .Kill(); - - await ScenarioCompletion; - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs b/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs deleted file mode 100644 index f1583b6339b4..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Threading.Tasks; -using Nethermind.Core.Extensions; -using Nethermind.Crypto; -using Nethermind.Facade.Eth.RpcTransaction; -using Nethermind.Overseer.Test.Framework; -using NUnit.Framework; - -namespace Nethermind.Overseer.Test -{ - [Explicit] - public class CliqueTests : TestBuilder - { - [SetUp] - public void Setup() - { - } - - [Test] - public async Task One_validator() - { - StartCliqueMiner("cliqueval1a") - .Wait(5000) - .Kill("cliqueval1a"); - - await ScenarioCompletion; - } - - [Test] - public async Task Two_validators() - { - StartCliqueMiner("cliqueval1b") - .StartCliqueMiner("cliqueval2b") - .Wait(10000) - .Kill("cliqueval1b") - .Kill("cliqueval2b"); - - await ScenarioCompletion; - } - - [Test] - public async Task Clique_vote() - { - StartCliqueMiner("cliqueval1c") - .StartCliqueMiner("cliqueval2c") - .StartCliqueMiner("cliqueval3c") - .StartCliqueMiner("cliqueval4c") - .StartCliqueMiner("cliqueval5c") - .StartCliqueMiner("cliqueval6c") - .StartCliqueMiner("cliqueval7c") - .StartCliqueNode("cliquenode1c") - .SetContext(new CliqueContext(new CliqueState())) - .Wait(20000) - .SwitchNode("cliqueval1c") - .Propose(Nodes["cliquenode1c"].Address, true) - .SwitchNode("cliqueval2c") - .Propose(Nodes["cliquenode1c"].Address, true) - .SwitchNode("cliqueval5c") - .Propose(Nodes["cliquenode1c"].Address, true) - .SwitchNode("cliqueval7c") - .Propose(Nodes["cliquenode1c"].Address, true) - .Wait(10000) - .LeaveContext() - .KillAll(); - - await ScenarioCompletion; - } - - [Test] - public async Task Clique_transaction_broadcast() - { - var tx = new LegacyTransactionForRpc - { - Value = 2.Ether(), - GasPrice = 20.GWei(), - Gas = 21000, - From = new PrivateKey(new byte[32] { - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,3 - }).Address, - To = new PrivateKey(new byte[32] { - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,1 - }).Address - }; - - StartCliqueMiner("cliqueval1d") - .StartCliqueMiner("cliqueval2d") - .StartCliqueNode("cliquenode3d") - .SetContext(new CliqueContext(new CliqueState())) - .Wait(5000) - .SendTransaction(tx) - .Wait(10000) - .LeaveContext() - .KillAll(); - - await ScenarioCompletion; - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/AuRaContext.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/AuRaContext.cs deleted file mode 100644 index 89eb5ed9d429..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/AuRaContext.cs +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using Nethermind.Overseer.Test.JsonRpc; -using Newtonsoft.Json.Linq; - -namespace Nethermind.Overseer.Test.Framework -{ - public class AuRaContext : TestContextBase - { - public AuRaContext(AuRaState state) : base(state) - { - } - - public AuRaContext ReadBlockAuthors() - { - for (int i = 1; i <= State.BlocksCount; i++) - { - ReadBlockAuthor(i); - } - - return this; - } - - public AuRaContext ReadBlockNumber() - { - IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; - return AddJsonRpc("Read block number", "eth_blockNumber", - () => client.PostAsync("eth_blockNumber"), stateUpdater: (s, r) => s.BlocksCount = r.Result - ); - } - - private AuRaContext ReadBlockAuthor(long blockNumber) - { - IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; - return AddJsonRpc("Read block", "eth_getBlockByNumber", - () => client.PostAsync("eth_getBlockByNumber", new object[] { blockNumber, false }), - stateUpdater: (s, r) => s.Blocks.Add( - Convert.ToInt64(r.Result["number"].Value(), 16), - (r.Result["miner"].Value(), Convert.ToInt64(r.Result["step"].Value(), 16)))); - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/AuRaState.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/AuRaState.cs deleted file mode 100644 index a4739a58f161..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/AuRaState.cs +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; - -namespace Nethermind.Overseer.Test.Framework -{ - public class AuRaState : ITestState - { - public IDictionary Blocks { get; set; } = new SortedDictionary(); - public long BlocksCount { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs deleted file mode 100644 index 6661b28c2fc1..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core; -using Nethermind.Facade.Eth.RpcTransaction; -using Nethermind.Overseer.Test.JsonRpc; - -namespace Nethermind.Overseer.Test.Framework; - -public class CliqueContext(CliqueState state) : TestContextBase(state) -{ - public CliqueContext Propose(Address address, bool vote) - { - IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; - return AddJsonRpc($"vote {vote} for {address}", "clique_propose", - () => client.PostAsync("clique_propose", [address, vote])); - } - - public CliqueContext Discard(Address address) - { - IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; - return AddJsonRpc($"discard vote for {address}", "clique_discard", - () => client.PostAsync("clique_discard", [address])); - } - - public CliqueContext SendTransaction(TransactionForRpc tx) - { - IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; - return AddJsonRpc($"send tx to {TestBuilder.CurrentNode.HttpPort}", "eth_sendTransaction", - () => client.PostAsync("eth_SendTransaction", [tx])); - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueState.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueState.cs deleted file mode 100644 index ff8bdd45858c..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueState.cs +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.Framework -{ - public class CliqueState : ITestState; -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/ITestContext.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/ITestContext.cs deleted file mode 100644 index fbfe7de359e8..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/ITestContext.cs +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.Framework -{ - // Marker - public interface ITestContext - { - void SetBuilder(TestBuilder builder); - } - - public interface ITestState; -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/NethermindProcessWrapper.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/NethermindProcessWrapper.cs deleted file mode 100644 index 108d10849329..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/NethermindProcessWrapper.cs +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Diagnostics; -using Nethermind.Core; -using Nethermind.Overseer.Test.JsonRpc; - -namespace Nethermind.Overseer.Test.Framework -{ - public class NethermindProcessWrapper - { - public string Enode { get; } - public IJsonRpcClient JsonRpcClient { get; } - public string Name { get; } - public Process Process { get; } - public bool IsRunning { get; private set; } - - public Address Address { get; private set; } - - public int HttpPort { get; private set; } - - public NethermindProcessWrapper(string name, Process process, int httpPort, Address address, string enode, IJsonRpcClient jsonRpcClient) - { - HttpPort = httpPort; - Address = address; - Enode = enode; - JsonRpcClient = jsonRpcClient; - Name = name; - Process = process; - } - - public void Start() - { - if (IsRunning) - { - throw new InvalidOperationException(); - } - - Console.WriteLine($"Starting in {Process.StartInfo.WorkingDirectory}"); - Process.Start(); - IsRunning = true; - } - - public void Kill() - { - if (!IsRunning) - { - throw new InvalidOperationException(); - } - - Process.Kill(); - Process.WaitForExit(); - IsRunning = false; - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/ProcessBuilder.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/ProcessBuilder.cs deleted file mode 100644 index c39a33a0044e..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/ProcessBuilder.cs +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using Nethermind.Crypto; -using Nethermind.Overseer.Test.JsonRpc; -using NUnit.Framework; - -namespace Nethermind.Overseer.Test.Framework -{ - public class ProcessBuilder - { - public NethermindProcessWrapper Create(string name, string workingDirectory, string config, string dbPath, int httpPort, int p2pPort, string nodeKey, string bootnode) - { - var process = new Process { EnableRaisingEvents = true }; - process.ErrorDataReceived += ProcessOnErrorDataReceived; - process.OutputDataReceived += ProcessOnOutputDataReceived; - process.Exited += ProcessOnExited; - process.StartInfo.WorkingDirectory = workingDirectory; - process.StartInfo.FileName = "dotnet"; - var arguments = $"nethermind.dll -c {config} --JsonRpc.Port {httpPort} --Network.P2PPort {p2pPort} --Network.DiscoveryPort {p2pPort} --KeyStore.TestNodeKey {nodeKey}"; - if (!string.IsNullOrEmpty(dbPath)) - { - arguments = $"{arguments} -d {dbPath}"; - } - - if (!string.IsNullOrEmpty(bootnode)) - { - arguments = $"{arguments} --Discovery.Bootnodes {bootnode}"; - } - - process.StartInfo.Arguments = arguments; - process.StartInfo.UseShellExecute = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - process.StartInfo.CreateNoWindow = false; - process.StartInfo.WindowStyle = ProcessWindowStyle.Normal; - - return new NethermindProcessWrapper(name, process, httpPort, new PrivateKey(nodeKey).Address, $"enode://{new PrivateKey(nodeKey).PublicKey.ToString(false)}@127.0.0.1:{p2pPort}", new JsonRpcClient($"http://localhost:{httpPort}")); - } - - private static void ProcessOnExited(object sender, EventArgs eventArgs) - { - TestContext.Out.WriteLine($"Process exited: {((Process)sender).StartInfo.Arguments}"); - } - - private static void ProcessOnOutputDataReceived(object sender, DataReceivedEventArgs dataReceivedEventArgs) - { - } - - private static void ProcessOnErrorDataReceived(object sender, DataReceivedEventArgs dataReceivedEventArgs) - { - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/JsonRpcTestStep.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/JsonRpcTestStep.cs deleted file mode 100644 index 70d335284994..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/JsonRpcTestStep.cs +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Threading.Tasks; -using Nethermind.Overseer.Test.JsonRpc; - -namespace Nethermind.Overseer.Test.Framework.Steps -{ - public class JsonRpcTestStep : TestStepBase - { - private readonly Func _validator; - private readonly Func>> _request; - private JsonRpcResponse _response; - - public JsonRpcTestStep(string name, - Func>> request, - Func validator) : base(name) - { - _validator = validator; - _request = request; - } - - public override async Task ExecuteAsync() - { - _response = await _request(); - - return _response.IsValid - ? GetResult(_validator?.Invoke(_response.Result) ?? true) - : GetResult(false); - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/KillProcessTestStep.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/KillProcessTestStep.cs deleted file mode 100644 index 58ff87a5c486..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/KillProcessTestStep.cs +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Threading.Tasks; - -namespace Nethermind.Overseer.Test.Framework.Steps -{ - public class KillProcessTestStep : TestStepBase - { - private readonly NethermindProcessWrapper _process; - private readonly int _delay; - - public KillProcessTestStep(string name, NethermindProcessWrapper process, - int delay = 0) : base(name) - { - _process = process; - _delay = delay; - } - - public override async Task ExecuteAsync() - { - _process.Kill(); - if (_delay > 0) - { - await Task.Delay(_delay); - } - - return GetResult(!_process.IsRunning); - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/StartProcessTestStep.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/StartProcessTestStep.cs deleted file mode 100644 index aff01f0158d2..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/StartProcessTestStep.cs +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Threading.Tasks; - -namespace Nethermind.Overseer.Test.Framework.Steps -{ - public class StartProcessTestStep : TestStepBase - { - private readonly NethermindProcessWrapper _process; - private readonly int _delay; - - public StartProcessTestStep(string name, NethermindProcessWrapper process, - int delay = 0) : base(name) - { - _process = process; - _delay = delay; - } - - public override async Task ExecuteAsync() - { - _process.Start(); - if (_delay > 0) - { - await Task.Delay(_delay); - } - - return GetResult(true); - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/TestStepBase.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/TestStepBase.cs deleted file mode 100644 index 10a1802dd1d9..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/TestStepBase.cs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Threading.Tasks; - -namespace Nethermind.Overseer.Test.Framework.Steps -{ - public abstract class TestStepBase - { - public string Name { get; } - - private static int _order = 1; - - protected TestStepBase(string name) - { - Name = name; - } - - public abstract Task ExecuteAsync(); - - protected TestResult GetResult(bool passed) => new TestResult(_order++, Name, passed); - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/WaitTestStep.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/WaitTestStep.cs deleted file mode 100644 index 4c0a9e8f6d11..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/Steps/WaitTestStep.cs +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Threading.Tasks; - -namespace Nethermind.Overseer.Test.Framework.Steps -{ - public class WaitTestStep : TestStepBase - { - private readonly int _delay; - - public WaitTestStep(string name, int delay = 5000) : base(name) - { - _delay = delay; - } - - public override async Task ExecuteAsync() - { - if (_delay > 0) - { - await Task.Delay(_delay); - } - - return GetResult(true); - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestBuilder.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/TestBuilder.cs deleted file mode 100644 index 42cbe499665e..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestBuilder.cs +++ /dev/null @@ -1,300 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Nethermind.Core.Extensions; -using Nethermind.Overseer.Test.Framework.Steps; -using NUnit.Framework; - -namespace Nethermind.Overseer.Test.Framework; - -/// -/// https://stackoverflow.com/questions/32112418/how-to-make-a-fluent-async-inferface-in-c-sharp -/// -public abstract class TestBuilder -{ - [TearDown] - public void TearDown() - { - var passedCount = _results.Count(static r => r.Passed); - var failedCount = _results.Count - passedCount; - - TestContext.Out.WriteLine("=========================== TESTS RESULTS ==========================="); - TestContext.Out.WriteLine($"TESTS PASSED: {passedCount}, FAILED: {failedCount}"); - foreach (var testResult in _results) - { - string message = $"{testResult.Order}. {testResult.Name} has " + - $"{(testResult.Passed ? "passed [+]" : "failed [-]")}"; - TestContext.Out.WriteLine(message); - } - } - -#pragma warning disable NUnit1032 - /// - /// Gets the task representing the fluent work. - /// - /// - /// The task. - /// - public Task ScenarioCompletion { get; private set; } -#pragma warning restore NUnit1032 - - /// - /// Queues up asynchronous work. - /// - /// The work to be queued. - public void QueueWork(Action work) - { - // queue up the work - ScenarioCompletion = ScenarioCompletion.ContinueWith(task => - { - try - { - work(); - } - catch (Exception e) - { - TestContext.Out.WriteLine(e.ToString()); - throw; - } - - return this; - }, TaskContinuationOptions.OnlyOnRanToCompletion); - } - - /// - /// Queues up asynchronous work. - /// - /// The work to be queued. - public void QueueWork(Func work) - { - // queue up the work - ScenarioCompletion = ScenarioCompletion.ContinueWith(async task => - { - try - { - await work(); - } - catch (Exception e) - { - TestContext.Out.WriteLine(e.ToString()); - throw; - } - - return this; - }, TaskContinuationOptions.OnlyOnRanToCompletion); - } - - public void QueueWork(TestStepBase step) - { - // queue up the work - ScenarioCompletion = ScenarioCompletion.ContinueWith(async task => - { - TestContext.Out.WriteLine($"Awaiting step {step.Name}"); - try - { - _results.Add(await step.ExecuteAsync()); - } - catch (Exception e) - { - TestContext.Out.WriteLine($"Step {step.Name} failed with error: {e}"); - throw; - } - - TestContext.Out.WriteLine($"Step {step.Name} complete"); - }, TaskContinuationOptions.OnlyOnRanToCompletion).Unwrap(); - } - - private readonly ProcessBuilder _processBuilder; - - private static readonly string _runnerDir; - private static readonly string _dbsDir; - private static readonly string _configsDir; - - static TestBuilder() - { - string testContextDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "context"); - _runnerDir = Path.Combine(testContextDir, "runner"); - _configsDir = Path.Combine(testContextDir, "configs"); - _dbsDir = Path.Combine(testContextDir, "dbs"); - - if (Directory.Exists(testContextDir)) - { - Directory.Delete(testContextDir, true); - } - - Directory.CreateDirectory(_dbsDir); - Directory.CreateDirectory(_configsDir); - } - - public TestBuilder() - { - _processBuilder = new ProcessBuilder(); - - if (!Directory.Exists(_runnerDir)) - { - Directory.CreateDirectory(_runnerDir); - CopyRunnerFiles(_runnerDir); - } - - // The entry point for the async work. - // Spin up a completed task to start with - // so that we dont have to do null checks - this.ScenarioCompletion = Task.FromResult(0); - } - - public T SetContext(T newContext) where T : ITestContext - { - newContext.SetBuilder(this); - return newContext; - } - - private const int _startHttpPort = 8600; - private const int _startPort = 30200; - - private byte _nodeCounter; - - public NethermindProcessWrapper CurrentNode { get; private set; } - - public List _results = new List(); - - public TestBuilder SwitchNode(string node) - { - CurrentNode = Nodes[node]; - return this; - } - - public TestBuilder Wait(int delay = 5000, string name = "Wait") - { - QueueWork(async () => await Task.Delay(delay)); - return this; - } - - public TestBuilder StartCliqueNode(string name) - { - return StartNode(name, "configs/cliqueNode.json"); - } - - public TestBuilder StartCliqueMiner(string name) - { - return StartNode(name, "configs/cliqueMiner.json"); - } - - public TestBuilder StartAuRaMiner(string name, string key) - { - return StartNode(name, "configs/auRaMiner.json", key); - } - - public TestBuilder StartNode(string name, string baseConfigFile, string key = null) - { - CurrentNode = GetOrCreateNode(name, baseConfigFile, key); - var step = new StartProcessTestStep($"Start {name}", CurrentNode); - QueueWork(step); - return this; - } - - private NethermindProcessWrapper GetOrCreateNode(string name, string baseConfigFile, string key) - { - if (!Nodes.TryGetValue(name, out NethermindProcessWrapper value)) - { - string bootnodes = string.Empty; - foreach ((_, NethermindProcessWrapper process) in Nodes) - { - bootnodes += $",{process.Enode}"; - } - - bootnodes = bootnodes.TrimStart(','); - - var nodeKey = GetNodeKey(key); - - string dbDir = Path.Combine(_dbsDir, name); - string configPath = Path.Combine(_configsDir, $"{name}.json"); - File.Copy(baseConfigFile, configPath); - int p2pPort = _startPort + _nodeCounter; - int httpPort = _startHttpPort + _nodeCounter; - TestContext.Out.WriteLine($"Creating {name} at {p2pPort}, http://localhost:{httpPort}"); - value = _processBuilder.Create(name, _runnerDir, configPath, dbDir, httpPort, p2pPort, nodeKey, bootnodes); - Nodes[name] = value; - _nodeCounter++; - } - - return value; - } - - private string GetNodeKey(string key) - { - if (key is null) - { - byte[] keyArray = new byte[32]; - keyArray[0] = 1; - keyArray[31] = _nodeCounter; - key = keyArray.ToHexString(); - } - - return key; - } - - public Dictionary Nodes { get; } = new Dictionary(); - - public TestBuilder Kill() - { - return Kill(CurrentNode.Name); - } - - public TestBuilder Kill(string name) - { - var step = new KillProcessTestStep($"Kill {name}", Nodes[name]); - QueueWork(step); - return this; - } - - public TestBuilder KillAll() - { - foreach (KeyValuePair keyValuePair in Nodes) - { - var step = new KillProcessTestStep($"Kill {keyValuePair.Key}", Nodes[keyValuePair.Key]); - QueueWork(step); - } - - return this; - } - -#if DEBUG - const string buildConfiguration = "Debug"; -#else - const string buildConfiguration = "Release"; -#endif - - private void CopyRunnerFiles(string targetDirectory) - { - string sourceDirectory = Path.Combine(Directory.GetCurrentDirectory(), $"../../../../artifacts/bin/Nethermind.Runner/{buildConfiguration}/"); - if (!Directory.Exists(sourceDirectory)) - { - throw new IOException($"Runner not found at {sourceDirectory}"); - } - - TestContext.Out.WriteLine($"Copying runner files from {sourceDirectory} to {targetDirectory}"); - CopyDir(sourceDirectory, targetDirectory); - string chainsDir = Path.Combine(Directory.GetCurrentDirectory(), "chainspec"); - CopyDir(chainsDir, Path.Combine(targetDirectory, "chainspec")); - } - - private void CopyDir(string sourceDirectory, string targetDirectory) - { - foreach (string file in Directory.GetFiles(sourceDirectory)) - { - File.Copy(file, Path.Combine(targetDirectory, Path.GetFileName(file)), true); - } - - foreach (string directory in Directory.GetDirectories(sourceDirectory)) - { - string targetSubDir = Path.Combine(targetDirectory, Path.GetFileName(directory)); - Directory.CreateDirectory(targetSubDir); - CopyDir(directory, targetSubDir); - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestContextBase.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/TestContextBase.cs deleted file mode 100644 index 40dfc1263d96..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestContextBase.cs +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Text.Json; -using System.Threading.Tasks; -using Nethermind.Overseer.Test.Framework.Steps; -using Nethermind.Overseer.Test.JsonRpc; - -using NUnit.Framework; - -namespace Nethermind.Overseer.Test.Framework -{ - public abstract class TestContextBase : ITestContext where TState : ITestState where TContext : TestContextBase - { - protected TState State { get; } - protected TestBuilder TestBuilder; - - protected TestContextBase(TState state) - { - State = state; - } - - public TContext SwitchNode(string node) - { - TestBuilder.SwitchNode(node); - return (TContext)this; - } - - public TestBuilder LeaveContext() - { - return TestBuilder; - } - - public TContext Wait(int delay = 5000, string name = "Wait") - => Add(new WaitTestStep($"name {delay}", delay)); - - protected TContext AddJsonRpc(string name, string methodName, - Func>> func, Func validator = null, - Action> stateUpdater = null) - => Add(new JsonRpcTestStep(name, - async () => - { - - var result = await ExecuteJsonRpcAsync(methodName, func); - if (result.IsValid) - { - stateUpdater?.Invoke(State, result); - } - - return result; - }, validator)); - - protected TContext Add(TestStepBase step) - { - TestBuilder.QueueWork(step); - return (TContext)this; - } - - private async Task> ExecuteJsonRpcAsync( - string methodName, Func>> func) - { - TestContext.Out.WriteLine($"Sending JSON RPC call: '{methodName}'."); - var delay = Task.Delay(20000); - var funcTask = func(); - var first = await Task.WhenAny(delay, funcTask); - if (first == delay) - { - string message = $"JSON RPC call '{methodName}' timed out"; - TestContext.Out.WriteLine(message); - throw new TimeoutException(message); - } - - var result = await funcTask; - - TestContext.Out.WriteLine($"Received a response for JSON RPC call '{methodName}'." + - $"{Environment.NewLine}{JsonSerializer.Serialize(result)}"); - - return await funcTask; - } - - public void SetBuilder(TestBuilder builder) - { - TestBuilder = builder; - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestResult.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/TestResult.cs deleted file mode 100644 index a8cfeea3c60e..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestResult.cs +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.Framework -{ - public class TestResult - { - public int Order { get; } - public string Name { get; } - public bool Passed { get; } - - public TestResult(int order, string name, bool passed) - { - Order = order; - Name = name; - Passed = passed; - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestResults.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/TestResults.cs deleted file mode 100644 index b75a1a04c8c8..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestResults.cs +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; - -namespace Nethermind.Overseer.Test.Framework -{ - public class TestResults - { - public List Results { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetDataDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetDataDto.cs deleted file mode 100644 index ce6da9734759..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetDataDto.cs +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class DataAssetDataDto - { - public string DataAssetId { get; set; } - public string Subscription { get; set; } - public string Data { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetDto.cs deleted file mode 100644 index 047ba7212766..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetDto.cs +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class DataAssetDto - { - // Default ID when adding a new data asset (otherwise, will fail for null). - public string Id { get; set; } = "0xd45c6b02474e7c60aeaf60df4ee451a53a09bb5df0a7e9231a0def145785f086"; - public string Name { get; set; } - public string Description { get; set; } - public string UnitPrice { get; set; } - public string UnitType { get; set; } - public uint MinUnits { get; set; } - public uint MaxUnits { get; set; } - public DataAssetRulesDto Rules { get; set; } - public DataAssetProviderDto Provider { get; set; } - public string File { get; set; } - public byte[] Data { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetProviderDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetProviderDto.cs deleted file mode 100644 index 0e1f3be48518..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetProviderDto.cs +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class DataAssetProviderDto - { - public string Address { get; set; } - public string Name { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetRuleDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetRuleDto.cs deleted file mode 100644 index 00ee5ebc902a..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetRuleDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class DataAssetRuleDto - { - public string Value { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetRulesDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetRulesDto.cs deleted file mode 100644 index 3539a70365a0..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataAssetRulesDto.cs +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class DataAssetRulesDto - { - public DataAssetRuleDto Expiry { get; set; } - public DataAssetRuleDto UpfrontPayment { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataRequestDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataRequestDto.cs deleted file mode 100644 index e1a81c98c684..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DataRequestDto.cs +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class DataRequestDto - { - public string DataAssetId { get; set; } - public uint Units { get; set; } - public string Value { get; set; } - public uint ExpiryTime { get; set; } - public string Provider { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DepositDetailsDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DepositDetailsDto.cs deleted file mode 100644 index e557d96c2648..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DepositDetailsDto.cs +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class DepositDetailsDto - { - public DepositDto Deposit { get; set; } - public bool Confirmed { get; set; } - public uint StartTimestamp { get; set; } - public uint SessionTimestamp { get; set; } - public string TransactionHash { get; set; } - public DataAssetDto DataAsset { get; set; } - public DataRequestDto DataRequest { get; set; } - public string[] Args { get; set; } - public bool StreamEnabled { get; set; } - public long ProviderTotalUnits { get; set; } - public long ConsumerTotalUnits { get; set; } - public long StartUnits { get; set; } - public long CurrentUnits { get; set; } - public long UnpaidUnits { get; set; } - public long PaidUnits { get; set; } - public string DataAvailability { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DepositDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DepositDto.cs deleted file mode 100644 index c3f4203e9716..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/DepositDto.cs +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class DepositDto - { - public string Id { get; set; } - public uint Units { get; set; } - public string Value { get; set; } - public uint ExpiryTime { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/MakeDepositDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/MakeDepositDto.cs deleted file mode 100644 index 80cc15022905..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/MakeDepositDto.cs +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class MakeDepositDto - { - public string DataAssetId { get; set; } - public uint Units { get; set; } - public string Value { get; set; } - public uint ExpiryTime { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/UnitsRangeDto.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/UnitsRangeDto.cs deleted file mode 100644 index 3928626d6b5a..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/Dto/UnitsRangeDto.cs +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc.Dto -{ - public class UnitsRangeDto - { - public uint From { get; set; } - public uint To { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/IJsonRpcClient.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/IJsonRpcClient.cs deleted file mode 100644 index 5b7dd08e2107..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/IJsonRpcClient.cs +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Threading.Tasks; - -namespace Nethermind.Overseer.Test.JsonRpc -{ - public interface IJsonRpcClient - { - Task> PostAsync(string method); - Task> PostAsync(string method, object[] @params); - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/JsonRpcClient.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/JsonRpcClient.cs deleted file mode 100644 index 3d314baadd5e..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/JsonRpcClient.cs +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using DotNetty.Common.Utilities; -using Nethermind.JsonRpc; -using Nethermind.Serialization.Json; - -namespace Nethermind.Overseer.Test.JsonRpc -{ - public class JsonRpcClient : IJsonRpcClient - { - private readonly string _host; - private readonly string _methodPrefix = "ndm_"; - private readonly HttpClient _client; - - public JsonRpcClient(string host) - { - _host = host; - _client = new HttpClient - { - BaseAddress = new Uri(host) - }; - } - - public Task> PostAsync(string method) - => PostAsync(method, []); - - public async Task> PostAsync(string method, object[] @params) - { - string methodToCall = method.Contains('_') ? method : $"{_methodPrefix}{method}"; - Console.WriteLine($"Sending {methodToCall} to {_host}"); - var request = new JsonRpcRequest(methodToCall, @params); - var payload = GetPayload(request); - string errorMessage = null; - HttpResponseMessage response = await _client.PostAsync("/", payload).ContinueWith((t) => - { - if (t.IsFaulted) - { - errorMessage = t.Exception.Unwrap().Message; - return null; - } - else if (t.IsCanceled) - { - return null; - } - - return t.Result; - }); - - if (!(response?.IsSuccessStatusCode ?? false)) - { - var result = new JsonRpcResponse(); - result.Error = new JsonRpcResponse.ErrorResponse(ErrorCodes.InternalError, errorMessage); - return result; - } - - return await response.Content.ReadAsStringAsync() - .ContinueWith(t => new EthereumJsonSerializer().Deserialize>(t.Result)); - } - - private StringContent GetPayload(JsonRpcRequest request) - => new StringContent(new EthereumJsonSerializer().Serialize(request), Encoding.UTF8, "application/json"); - - private class JsonRpcRequest - { - public string JsonRpc { get; set; } - public int Id { get; set; } - public string Method { get; set; } - public object[] Params { get; set; } - - public JsonRpcRequest(string method, object[] @params) : this("2.0", 1, method, @params) - { - } - - public JsonRpcRequest(string jsonRpc, int id, string method, object[] @params) - { - JsonRpc = jsonRpc; - Id = id; - Method = method; - Params = @params; - } - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/JsonRpcResponse.cs b/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/JsonRpcResponse.cs deleted file mode 100644 index 54e2b5cb0a03..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/JsonRpc/JsonRpcResponse.cs +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Overseer.Test.JsonRpc -{ - public class JsonRpcResponse - { - public int Id { get; set; } - public string JsonRpc { get; set; } - public T Result { get; set; } - public ErrorResponse Error { get; set; } - public bool IsValid => Error is null; - - public class ErrorResponse - { - public ErrorResponse(int code, string message, object data = null) - { - Code = code; - Message = message; - } - - public int Code { get; set; } - public string Message { get; set; } - public object Data { get; set; } - } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/Nethermind.Overseer.Test.csproj b/src/Nethermind/Nethermind.Overseer.Test/Nethermind.Overseer.Test.csproj deleted file mode 100644 index d450cc905964..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/Nethermind.Overseer.Test.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - PreserveNewest - - - - - - - - - - diff --git a/src/Nethermind/Nethermind.Overseer.Test/chainspec/auRa.json b/src/Nethermind/Nethermind.Overseer.Test/chainspec/auRa.json deleted file mode 100644 index cd92dd481744..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/chainspec/auRa.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - "name": "AuRaTest", - "dataDir": "aura_test", - "engine": { - "authorityRound": { - "params": { - "stepDuration": 3, - "blockReward": "0xDE0B6B3A7640000", - "maximumUncleCountTransition": 0, - "maximumUncleCount": 0, - "validators": { - "multi": { - "0" : { - "list": ["0x557abc72a6594d1bd9a655a1cb58a595526416c8"] - }, - "4" : { - "list": ["0x557abc72a6594d1bd9a655a1cb58a595526416c8", "0x69399093be61566a1c86b09bd02612c6bf31214f"] - }, - "8" : { - "list": ["0x557abc72a6594d1bd9a655a1cb58a595526416c8", "0x4cb87ff61e0e3f9f4043f69fe391a62b5a018b97"] - }, - "12" : { - "safeContract": "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" - } - } - }, - "blockRewardContractAddress": "0x3145197AD50D7083D0222DE4fCCf67d9BD05C30D", - "blockRewardContractTransition": 4639000 - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x400", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID": "0x4D00", - - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "eip145Transition": 6464300, - "eip1014Transition": 6464300, - "eip1052Transition": 6464300, - "eip1283Transition": 6464300, - "eip1283DisableTransition": 7026400, - - "eip152Transition": "0xFFFFFFFF", - "eip1108Transition": "0xFFFFFFFF", - "eip1344Transition": "0xFFFFFFFF", - "eip1884Transition": "0xFFFFFFFF", - "eip2028Transition": "0xFFFFFFFF", - "eip2200Transition": "0xFFFFFFFF" - }, - "genesis": { - "seal": { - "authorityRound": { - "step": "0x0", - "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x20000", - "gasLimit": "0x863BE0" - }, - "accounts": { - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x0", "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x0", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x0", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000, "eip1108_transition_base": 0, "eip1108_transition_pair": 0 } } } }, - - "0x0000000000000000000000000000000000000001": { - "balance": "1", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "1", - "builtin": { - "name": "sha256", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "1", - "builtin": { - "name": "ripemd160", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "1", - "builtin": { - "name": "identity", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "0x557abc72a6594d1bd9a655a1cb58a595526416c8": { - "balance": "252460800000000000000000000" - }, - "0x8bf38d4764929064f2d4d3a56520a76ab3df415b": { - "balance":"1", - "constructor": "0x608060405234801561001057600080fd5b506040516109c43803806109c4833981018060405281019080805182019291905050508060009080519060200190610049929190610082565b508060019080519060200190610060929190610082565b506001600260006101000a81548160ff0219169083151502179055505061014f565b8280548282559060005260206000209081019282156100fb579160200282015b828111156100fa5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906100a2565b5b509050610108919061010c565b5090565b61014c91905b8082111561014857600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101610112565b5090565b90565b6108668061015e6000396000f300608060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806340a141ff1461007d5780634d238c8e146100c05780637528621114610103578063b3f05b971461011a578063b7ab4db514610149578063eebc7a39146101b5575b600080fd5b34801561008957600080fd5b506100be600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610221565b005b3480156100cc57600080fd5b50610101600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610453565b005b34801561010f57600080fd5b506101186105a7565b005b34801561012657600080fd5b5061012f610625565b604051808215151515815260200191505060405180910390f35b34801561015557600080fd5b5061015e610638565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101a1578082015181840152602081019050610186565b505050509050019250505060405180910390f35b3480156101c157600080fd5b506101ca6106c6565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561020d5780820151818401526020810190506101f2565b505050509050019250505060405180910390f35b6000600260009054906101000a900460ff16151561023e57600080fd5b600090505b60008054905081101561044e5760008181548110151561025f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156104415760006001600080549050038154811015156102d457fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660008281548110151561030e57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600080548091906001900361036b9190610754565b506001430340600019167f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c8960006040518080602001828103825283818154815260200191508054801561041357602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116103c9575b50509250505060405180910390a26000600260006101000a81548160ff02191690831515021790555061044f565b8080600101915050610243565b5b5050565b600260009054906101000a900460ff16151561046e57600080fd5b60008190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506001430340600019167f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c8960006040518080602001828103825283818154815260200191508054801561057b57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610531575b50509250505060405180910390a26000600260006101000a81548160ff02191690831515021790555050565b73fffffffffffffffffffffffffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156105f557600080fd5b60006001908054610607929190610780565b506001600260006101000a81548160ff021916908315150217905550565b600260009054906101000a900460ff1681565b606060018054806020026020016040519081016040528092919081815260200182805480156106bc57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610672575b5050505050905090565b6060600080548060200260200160405190810160405280929190818152602001828054801561074a57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610700575b5050505050905090565b81548183558181111561077b5781836000526020600020918201910161077a91906107d2565b5b505050565b8280548282559060005260206000209081019282156107c15760005260206000209182015b828111156107c05782548255916001019190600101906107a5565b5b5090506107ce91906107f7565b5090565b6107f491905b808211156107f05760008160009055506001016107d8565b5090565b90565b61083791905b8082111561083357600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016107fd565b5090565b905600a165627a7a7230582077d7be9f11ab8944b6f6dc9e5086fe426593e38f30a5999ae41c11052ec424e4002900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000557abc72a6594d1bd9a655a1cb58a595526416c8" - }, - "0xe85db1afba971890bd66ba08dfeec018ab80726e": { - "balance":"1", - "constructor": "0x608060405234801561001057600080fd5b506040516109c43803806109c4833981018060405281019080805182019291905050508060009080519060200190610049929190610082565b508060019080519060200190610060929190610082565b506001600260006101000a81548160ff0219169083151502179055505061014f565b8280548282559060005260206000209081019282156100fb579160200282015b828111156100fa5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906100a2565b5b509050610108919061010c565b5090565b61014c91905b8082111561014857600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101610112565b5090565b90565b6108668061015e6000396000f300608060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806340a141ff1461007d5780634d238c8e146100c05780637528621114610103578063b3f05b971461011a578063b7ab4db514610149578063eebc7a39146101b5575b600080fd5b34801561008957600080fd5b506100be600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610221565b005b3480156100cc57600080fd5b50610101600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610453565b005b34801561010f57600080fd5b506101186105a7565b005b34801561012657600080fd5b5061012f610625565b604051808215151515815260200191505060405180910390f35b34801561015557600080fd5b5061015e610638565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101a1578082015181840152602081019050610186565b505050509050019250505060405180910390f35b3480156101c157600080fd5b506101ca6106c6565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561020d5780820151818401526020810190506101f2565b505050509050019250505060405180910390f35b6000600260009054906101000a900460ff16151561023e57600080fd5b600090505b60008054905081101561044e5760008181548110151561025f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156104415760006001600080549050038154811015156102d457fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660008281548110151561030e57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600080548091906001900361036b9190610754565b506001430340600019167f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c8960006040518080602001828103825283818154815260200191508054801561041357602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116103c9575b50509250505060405180910390a26000600260006101000a81548160ff02191690831515021790555061044f565b8080600101915050610243565b5b5050565b600260009054906101000a900460ff16151561046e57600080fd5b60008190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506001430340600019167f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c8960006040518080602001828103825283818154815260200191508054801561057b57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610531575b50509250505060405180910390a26000600260006101000a81548160ff02191690831515021790555050565b73fffffffffffffffffffffffffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156105f557600080fd5b60006001908054610607929190610780565b506001600260006101000a81548160ff021916908315150217905550565b600260009054906101000a900460ff1681565b606060018054806020026020016040519081016040528092919081815260200182805480156106bc57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610672575b5050505050905090565b6060600080548060200260200160405190810160405280929190818152602001828054801561074a57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610700575b5050505050905090565b81548183558181111561077b5781836000526020600020918201910161077a91906107d2565b5b505050565b8280548282559060005260206000209081019282156107c15760005260206000209182015b828111156107c05782548255916001019190600101906107a5565b5b5090506107ce91906107f7565b5090565b6107f491905b808211156107f05760008160009055506001016107d8565b5090565b90565b61083791905b8082111561083357600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016107fd565b5090565b905600a165627a7a7230582077d7be9f11ab8944b6f6dc9e5086fe426593e38f30a5999ae41c11052ec424e4002900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000557abc72a6594d1bd9a655a1cb58a595526416c8" - }, - "0x69399093be61566a1c86b09bd02612c6bf31214f": { - "balance":"1000000000000" - }, - "0x7e99aceb5d3dba8c79732c6cf49f184b00cff69b": { - "balance":"1000000000000" - }, - "0x2da63c1f7ede7305cdaa931e3feb2e4a234780d0": { - "balance":"10000000000000000000000000000000000" - } - } -} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Overseer.Test/chainspec/clique.json b/src/Nethermind/Nethermind.Overseer.Test/chainspec/clique.json deleted file mode 100644 index 5f2e17948042..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/chainspec/clique.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "name": "CliqueTest", - "dataDir": "clique_test", - "engine": { - "clique": { - "params": { - "period": 2, - "epoch": 30000, - "blockReward": "0x0" - } - } - }, - "params": { - "accountStartNonce": "0x0", - "eip98Transition": "0xffffffffffffffff", - "eip140Transition": "0x0", - "eip145Transition": "0x0", - "eip150Transition": "0x0", - "eip155Transition": "0x0", - "eip158Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "eip1283Transition": "0x0", - "gasLimitBoundDivisor": "0x400", - "homesteadTransition": "0x0", - "kip4Transition": "0xffffffffffffffff", - "kip6Transition": "0xffffffffffffffff", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x0", - "maximumExtraDataSize": "0x64", - "minGasLimit": "0x1388", - "networkID": "0x188c", - "validateReceipts": false, - "validateReceiptsTransition": "0xffffffffffffffff", - "wasmActivationTransition": "0xffffffffffffffff" - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "author": "0x0000000000000000000000000000000000000000", - "difficulty": "0x1", - "extraData": "0x2249276d20646f6e652077616974696e672e2e2e20666f7220626c6f636b20667691ee0343b9a529675e1a8a70197b3b704f90b7dc5b20847f43d67928f49cd4f85d696b5a7617b50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0xa00000", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "0x5bdda800" - }, - "nodes": [ - ], - "accounts": { - "0x0000000000000000000000000000000000000000": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000001": { - "balance": "0x1", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "0x1", - "builtin": { - "name": "sha256", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "0x1", - "builtin": { - "name": "ripemd160", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "0x1", - "builtin": { - "name": "identity", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "0x0000000000000000000000000000000000000005": { - "balance": "0x1", - "builtin": { - "name": "modexp", - "activate_at": "0x0", - "pricing": { - "modexp": { - "divisor": 20 - } - } - } - }, - "0x0000000000000000000000000000000000000006": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_add", - "activate_at": "0x0", - "pricing": { - "linear": { - "base": 500, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000007": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_mul", - "activate_at": "0x0", - "pricing": { - "linear": { - "base": 40000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000008": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_pairing", - "activate_at": "0x0", - "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 - } - } - } - }, - "7e5f4552091a69125d5dfcb7b8c2659029395bdf": { "balance": "100000000000000000000" }, - "2b5ad5c4795c026514f8317c7a215e218dccd6cf": { "balance": "100000000000000000000" }, - "6813eb9362372eef6200f3b1dbc3f819671cba69": { "balance": "100000000000000000000" }, - "1eff47bc3a10a45d4b230b5d10e37751fe6aa718": { "balance": "100000000000000000000" }, - "e1ab8145f7e55dc933d51a18c793f901a3a0b276": { "balance": "100000000000000000000" }, - "e57bfe9f44b819898f47bf37e5af72a0783e1141": { "balance": "100000000000000000000" }, - "d41c057fd1c78805aac12b0a94a405c0461a6fbb": { "balance": "100000000000000000000" }, - "f1f6619b38a98d6de0800f1defc0a6399eb6d30c": { "balance": "100000000000000000000" }, - "f7edc8fa1ecc32967f827c9043fcae6ba73afa5c": { "balance": "100000000000000000000" }, - "4cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528": { "balance": "100000000000000000000" } - } -} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Overseer.Test/chainspec/nethdev.json b/src/Nethermind/Nethermind.Overseer.Test/chainspec/nethdev.json deleted file mode 100644 index 2046a0836a48..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/chainspec/nethdev.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "nethdev", - "dataDir": "test", - "engine": { - "NethDev": { - "params": { - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "registrar": "0x81a4b044831c4f12ba601adb9274516939e9b8a2", - "accountStartNonce": "0x0", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x63", - "forkBlock": 641350, - "forkCanonHash": "0x8033403e9fe5811a7b6d6b469905915de1c59207ce2172cbcf5d6ff14fa6a2eb", - "maxCodeSize": 24576, - "maxCodeSizeTransition": 10, - "eip155Transition": 10, - "eip98Transition": "0x7fffffffffffff", - "eip86Transition": "0x7fffffffffffff", - "eip140Transition": 1700000, - "eip211Transition": 1700000, - "eip214Transition": 1700000, - "eip658Transition": 1700000 - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x100000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x3535353535353535353535353535353535353535353535353535353535353535", - "gasLimit": "0x1000000" - }, - "nodes": [ - ], - "accounts": { - "0000000000000000000000000000000000000000": { "balance": "1" }, - "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "0", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "0", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "0", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "0", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "balance": "1", "nonce": "0", "builtin": { "name": "modexp", "activate_at": 1700000, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "balance": "1", "nonce": "0", "builtin": { "name": "alt_bn128_add", "activate_at": 1700000, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "balance": "1", "nonce": "0", "builtin": { "name": "alt_bn128_mul", "activate_at": 1700000, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "balance": "1", "nonce": "0", "builtin": { "name": "alt_bn128_pairing", "activate_at": 1700000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, - "7e5f4552091a69125d5dfcb7b8c2659029395bdf": { "balance": "100000000000000000000" }, - "2b5ad5c4795c026514f8317c7a215e218dccd6cf": { "balance": "100000000000000000000" }, - "6813eb9362372eef6200f3b1dbc3f819671cba69": { "balance": "100000000000000000000" }, - "1eff47bc3a10a45d4b230b5d10e37751fe6aa718": { "balance": "100000000000000000000" }, - "e1ab8145f7e55dc933d51a18c793f901a3a0b276": { "balance": "100000000000000000000" }, - "e57bfe9f44b819898f47bf37e5af72a0783e1141": { "balance": "100000000000000000000" }, - "d41c057fd1c78805aac12b0a94a405c0461a6fbb": { "balance": "100000000000000000000" }, - "f1f6619b38a98d6de0800f1defc0a6399eb6d30c": { "balance": "100000000000000000000" }, - "f7edc8fa1ecc32967f827c9043fcae6ba73afa5c": { "balance": "100000000000000000000" }, - "4cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528": { "balance": "100000000000000000000" } - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/configs/auRaMiner.json b/src/Nethermind/Nethermind.Overseer.Test/configs/auRaMiner.json deleted file mode 100644 index ab8d13199450..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/configs/auRaMiner.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "Init": { - "EnableUnsecuredDevWallet": true, - "KeepDevWalletInMemory": true, - "ChainSpecPath": "chainspec/auRa.json", - "GenesisHash": "", - "BaseDbPath": "aura", - "LogFileName": "auRaMiner.log" - }, - "Network": { - "DiscoveryPort": 30303, - "P2PPort": 30303, - }, - "JsonRpc": { - "Host": "127.0.0.1", - "Port": 8545, - "Enabled": true, - }, - "Db": { - "WriteBufferSize": 67108864, - "WriteBufferNumber": 6, - "BlockCacheSize": 67108864, - "CacheIndexAndFilterBlocks": false - }, - "Aura": - { - "ForceSealing": true - }, - "Mining": - { - "Enabled": true - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueMiner.json b/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueMiner.json deleted file mode 100644 index a34b7c0ce715..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueMiner.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "Init": { - "EnableUnsecuredDevWallet": true, - "KeepDevWalletInMemory": true, - "ChainSpecPath": "chainspec/clique.json", - "GenesisHash": "", - "BaseDbPath": "clique", - "LogFileName": "cliqueMiner.log" - }, - "Network": { - "DiscoveryPort": 30303, - "P2PPort": 30303, - }, - "JsonRpc": { - "Host": "127.0.0.1", - "Port": 8545, - "Enabled": true, - }, - "Db": { - "WriteBufferSize": 67108864, - "WriteBufferNumber": 6, - "BlockCacheSize": 67108864, - "CacheIndexAndFilterBlocks": false - }, - "Mining": - { - "Enabled": true - } -} diff --git a/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueNode.json b/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueNode.json deleted file mode 100644 index 2694dddb357a..000000000000 --- a/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueNode.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "Init": { - "EnableUnsecuredDevWallet": true, - "KeepDevWalletInMemory": true, - "ChainSpecPath": "chainspec/clique.json", - "GenesisHash": "", - "BaseDbPath": "clique", - "LogFileName": "cliqueNode.log" - }, - "Network": { - "DiscoveryPort": 30303, - "P2PPort": 30303, - }, - "JsonRpc": { - "Host": "127.0.0.1", - "Port": 8545, - "Enabled": true, - }, - "Db": { - "WriteBufferSize": 67108864, - "WriteBufferNumber": 6, - "BlockCacheSize": 67108864, - "CacheIndexAndFilterBlocks": false - } -} diff --git a/src/Nethermind/Nethermind.Runner.Test/ChainSpecFilesTests.cs b/src/Nethermind/Nethermind.Runner.Test/ChainSpecFilesTests.cs index fb84e934f28d..8a3af1cab95b 100644 --- a/src/Nethermind/Nethermind.Runner.Test/ChainSpecFilesTests.cs +++ b/src/Nethermind/Nethermind.Runner.Test/ChainSpecFilesTests.cs @@ -37,15 +37,6 @@ public void ChainSpec_from_file(string chainSpecPath, ulong chainId) .Match(cs => cs.ChainId == chainId); } - // This holesky.json contains invalid config values. This test ensues that those config values are - // ignored for the correct ones contained in another holesky.json file embedded in the config directory - [TestCase("holesky.json", 0x4268UL)] - public void ignoring_custom_chainSpec_when_embedded_exists(string chainSpecPath, ulong chainId) - { - _loader.LoadEmbeddedOrFromFile(chainSpecPath).Should() - .Match(cs => cs.ChainId == chainId); - } - [TestCase("chainspec/custom_chainspec_that_does_not_exist.json")] public void ChainSpecNotFound(string chainSpecPath) { diff --git a/src/Nethermind/Nethermind.Runner.Test/ConfigFilesTests.cs b/src/Nethermind/Nethermind.Runner.Test/ConfigFilesTests.cs index 3f1ba93dff20..d4b139074aab 100644 --- a/src/Nethermind/Nethermind.Runner.Test/ConfigFilesTests.cs +++ b/src/Nethermind/Nethermind.Runner.Test/ConfigFilesTests.cs @@ -169,7 +169,7 @@ public void Network_diag_tracer_disabled_by_default(string configWildcard) } [TestCase("mainnet", 2048)] - [TestCase("holesky", 1024)] + [TestCase("hoodi", 1024)] [TestCase("sepolia", 1024)] [TestCase("gnosis", 2048)] [TestCase("poacore", 2048)] @@ -186,9 +186,9 @@ public void Tx_pool_defaults_are_correct(string configWildcard, int poolSize) [TestCase("gnosis", true)] [TestCase("mainnet", true)] [TestCase("sepolia", true)] - [TestCase("holesky", true)] + [TestCase("hoodi", true)] [TestCase("chiado", true)] - [TestCase("^spaceneth ^mainnet ^gnosis ^sepolia ^holesky ^chiado", false)] + [TestCase("^spaceneth ^mainnet ^gnosis ^sepolia ^hoodi ^chiado", false)] public void Json_defaults_are_correct(string configWildcard, bool jsonEnabled) { Test(configWildcard, static c => c.Enabled, jsonEnabled); @@ -225,11 +225,11 @@ public void Snap_sync_settings_as_expected(string configWildcard, bool enabled) Test(configWildcard, static c => c.SnapSync, enabled); } - [TestCase("^aura ^sepolia ^holesky ^mainnet", false)] + [TestCase("^aura ^sepolia ^hoodi ^mainnet", false)] [TestCase("aura ^archive", true)] [TestCase("^archive ^spaceneth", true)] [TestCase("sepolia ^archive", true)] - [TestCase("holesky ^archive", true)] + [TestCase("hoodi ^archive", true)] [TestCase("mainnet ^archive", true)] public void Stays_on_full_sync(string configWildcard, bool stickToFullSyncAfterFastSync) { @@ -268,8 +268,7 @@ public void Base_db_path_is_set(string configWildcard, string startWith) Test(configWildcard, c => c.BaseDbPath, (cf, p) => p.Should().StartWith(startWith)); } - [TestCase("^sepolia", "Data/static-nodes.json")] - [TestCase("sepolia", "Data/static-nodes-sepolia.json")] + [TestCase("*", "static-nodes.json")] public void Static_nodes_path_is_default(string configWildcard, string staticNodesPath) { Test(configWildcard, static c => c.StaticNodesPath, staticNodesPath); @@ -310,11 +309,11 @@ public void Simulating_block_production_on_every_slot_is_always_disabled(string } [TestCase("sepolia", BlobsSupportMode.StorageWithReorgs)] - [TestCase("holesky", BlobsSupportMode.StorageWithReorgs)] + [TestCase("hoodi", BlobsSupportMode.StorageWithReorgs)] [TestCase("chiado", BlobsSupportMode.StorageWithReorgs)] [TestCase("mainnet", BlobsSupportMode.StorageWithReorgs)] [TestCase("gnosis", BlobsSupportMode.StorageWithReorgs)] - [TestCase("^sepolia ^holesky ^chiado ^mainnet ^gnosis", BlobsSupportMode.Disabled)] + [TestCase("^sepolia ^hoodi ^chiado ^mainnet ^gnosis", BlobsSupportMode.Disabled)] public void Blob_txs_support_is_correct(string configWildcard, BlobsSupportMode blobsSupportMode) { Test(configWildcard, static c => c.BlobsSupport, blobsSupportMode); @@ -352,8 +351,8 @@ public void Arena_order_is_default(string configWildcard) [TestCase("gnosis", 17_000_000L, 5UL, 3000)] [TestCase("mainnet", 60_000_000L)] [TestCase("sepolia", 60_000_000L)] - [TestCase("holesky", 60_000_000L)] - [TestCase("^chiado ^gnosis ^mainnet ^sepolia ^holesky")] + [TestCase("hoodi", 60_000_000L)] + [TestCase("^chiado ^gnosis ^mainnet ^sepolia ^hoodi")] public void Blocks_defaults_are_correct(string configWildcard, long? targetBlockGasLimit = null, ulong secondsPerSlot = 12, int blockProductionTimeout = 4000) { Test(configWildcard, static c => c.TargetBlockGasLimit, targetBlockGasLimit); @@ -404,8 +403,8 @@ public void Memory_hint_is_enough(string configWildcard) protected override IEnumerable Configs { get; } = new HashSet { - "holesky.json", - "holesky_archive.json", + "hoodi.json", + "hoodi_archive.json", "mainnet_archive.json", "mainnet.json", "poacore.json", diff --git a/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/Migrations/ReceiptMigrationTests.cs b/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/Migrations/ReceiptMigrationTests.cs index 02a695382dfd..6fb33121c458 100644 --- a/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/Migrations/ReceiptMigrationTests.cs +++ b/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/Migrations/ReceiptMigrationTests.cs @@ -160,7 +160,8 @@ public void RemoveReceipts(Block block) } #pragma warning disable CS0067 - public event EventHandler ReceiptsInserted; + public event EventHandler NewCanonicalReceipts; + public event EventHandler ReceiptsInserted; #pragma warning restore CS0067 } } diff --git a/src/Nethermind/Nethermind.Runner.Test/holesky.json b/src/Nethermind/Nethermind.Runner.Test/holesky.json deleted file mode 100644 index 5766bce3a56d..000000000000 --- a/src/Nethermind/Nethermind.Runner.Test/holesky.json +++ /dev/null @@ -1,933 +0,0 @@ -{ - "_comment": "IMPORTANT! This holesky chainspec file is incorrect. it exists only for testing purposes.", - "name": "Holesky Incorrect Testnet", - "dataDir": "holesky", - "engine": { - "clique": { - "params": { - "period": 15, - "epoch": 30000 - } - } - }, - "params": { - "accountStartNonce": "0x0", - "chainID": "0x55555", - "eip140Transition": "0x0", - "eip145Transition": "0x0", - "eip150Transition": "0x0", - "eip155Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "eip1283Transition": "0x0", - "eip1283DisableTransition": "0x0", - "eip152Transition": "0x17D433", - "eip1108Transition": "0x17D433", - "eip1344Transition": "0x17D433", - "eip1884Transition": "0x17D433", - "eip2028Transition": "0x17D433", - "eip2200Transition": "0x17D433", - "eip2565Transition": "0x441064", - "eip2929Transition": "0x441064", - "eip2930Transition": "0x441064", - "eip1559Transition": "0x4D3FCD", - "eip3198Transition": "0x4D3FCD", - "eip3529Transition": "0x4D3FCD", - "eip3541Transition": "0x4D3FCD", - "terminalTotalDifficulty": "A4A470", - "gasLimitBoundDivisor": "0x400", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x0", - "maximumExtraDataSize": "0xffff", - "minGasLimit": "0x1388", - "networkID": "0x5" - }, - "genesis": { - "author": "0x0000000000000000000000000000000000000000", - "difficulty": "0x1", - "extraData": "0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0xa00000", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "seal": { - "ethereum": { - "nonce": "0x0000000000000000", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "timestamp": "0x5c51a607" - }, - "nodes": [ - "enode://011f758e6552d105183b1761c5e2dea0111bc20fd5f6422bc7f91e0fabbec9a6595caf6239b37feb773dddd3f87240d99d859431891e4a642cf2a0a9e6cbb98a@51.141.78.53:30303", - "enode://176b9417f511d05b6b2cf3e34b756cf0a7096b3094572a8f6ef4cdcb9d1f9d00683bf0f83347eebdf3b81c3521c2332086d9592802230bf528eaf606a1d9677b@13.93.54.137:30303", - "enode://46add44b9f13965f7b9875ac6b85f016f341012d84f975377573800a863526f4da19ae2c620ec73d11591fa9510e992ecc03ad0751f53cc02f7c7ed6d55c7291@94.237.54.114:30313", - "enode://c1f8b7c2ac4453271fa07d8e9ecf9a2e8285aa0bd0c07df0131f47153306b0736fd3db8924e7a9bf0bed6b1d8d4f87362a71b033dc7c64547728d953e43e59b2@52.64.155.147:30303", - "enode://f4a9c6ee28586009fb5a96c8af13a58ed6d8315a9eee4772212c1d4d9cebe5a8b8a78ea4434f318726317d04a3f531a1ef0420cf9752605a562cfe858c46e263@213.186.16.82:30303", - "enode://a61215641fb8714a373c80edbfa0ea8878243193f57c96eeb44d0bc019ef295abd4e044fd619bfc4c59731a73fb79afe84e9ab6da0c743ceb479cbb6d263fa91@3.11.147.67:30303", - "enode://b5948a2d3e9d486c4d75bf32713221c2bd6cf86463302339299bd227dc2e276cd5a1c7ca4f43a0e9122fe9af884efed563bd2a1fd28661f3b5f5ad7bf1de5949@18.218.250.66:30303", - "enode://d2b720352e8216c9efc470091aa91ddafc53e222b32780f505c817ceef69e01d5b0b0797b69db254c586f493872352f5a022b4d8479a00fc92ec55f9ad46a27e@88.99.70.182:30303", - "enode://d4f764a48ec2a8ecf883735776fdefe0a3949eb0ca476bd7bc8d0954a9defe8fea15ae5da7d40b5d2d59ce9524a99daedadf6da6283fca492cc80b53689fb3b3@46.4.99.122:32109" - ], - "accounts": { - "0x0000000000000000000000000000000000000000": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000001": { - "balance": "0x1", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "0x1", - "builtin": { - "name": "sha256", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "0x1", - "builtin": { - "name": "ripemd160", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "0x1", - "builtin": { - "name": "identity", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "0x0000000000000000000000000000000000000005": { - "balance": "0x1", - "builtin": { - "name": "modexp", - "activate_at": "0x0", - "pricing": { - "modexp": { - "divisor": 20 - } - } - } - }, - "0x0000000000000000000000000000000000000006": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_add", - "activate_at": "0x0", - "pricing": { - "linear": { - "base": 500, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000007": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_mul", - "activate_at": "0x0", - "pricing": { - "linear": { - "base": 40000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000008": { - "balance": "0x1", - "builtin": { - "name": "alt_bn128_pairing", - "activate_at": "0x0", - "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 - } - } - } - }, - "0x0000000000000000000000000000000000000009": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000000f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000010": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000011": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000012": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000013": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000014": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000015": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000016": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000017": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000018": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000019": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000001f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000020": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000021": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000022": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000023": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000024": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000025": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000026": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000027": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000028": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000029": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000002f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000030": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000031": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000032": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000033": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000034": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000035": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000036": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000037": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000038": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000039": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000003f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000040": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000041": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000042": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000043": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000044": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000045": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000046": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000047": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000048": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000049": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000004f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000050": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000051": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000052": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000053": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000054": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000055": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000056": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000057": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000058": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000059": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000005f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000060": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000061": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000062": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000063": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000064": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000065": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000066": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000067": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000068": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000069": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000006f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000070": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000071": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000072": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000073": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000074": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000075": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000076": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000077": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000078": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000079": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000007f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000080": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000081": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000082": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000083": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000084": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000085": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000086": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000087": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000088": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000089": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000008f": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000090": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000091": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000092": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000093": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000094": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000095": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000096": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000097": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000098": { - "balance": "0x1" - }, - "0x0000000000000000000000000000000000000099": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009a": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009b": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009c": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009d": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009e": { - "balance": "0x1" - }, - "0x000000000000000000000000000000000000009f": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000a9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000aa": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ab": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ac": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ad": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ae": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000af": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000b9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ba": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000be": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000bf": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000c9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ca": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ce": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000cf": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000d9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000da": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000db": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000dc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000dd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000de": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000df": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000e9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ea": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000eb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ec": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ed": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ee": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ef": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f0": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f1": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f2": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f3": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f4": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f5": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f6": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f7": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f8": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000f9": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fa": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fb": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fc": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fd": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000fe": { - "balance": "0x1" - }, - "0x00000000000000000000000000000000000000ff": { - "balance": "0x1" - }, - "0x4c2ae482593505f0163cdefc073e81c63cda4107": { - "balance": "0x152d02c7e14af6800000" - }, - "0xa8e8f14732658e4b51e8711931053a8a69baf2b1": { - "balance": "0x152d02c7e14af6800000" - }, - "0xd9a5179f091d85051d3c982785efd1455cec8699": { - "balance": "0x84595161401484a000000" - }, - "0xe0a2bd4258d2768837baa26a28fe71dc079f84c7": { - "balance": "0x4a47e3c12448f4ad000000" - } - } -} diff --git a/src/Nethermind/Nethermind.Runner/Data/static-nodes.json b/src/Nethermind/Nethermind.Runner/Data/static-nodes.json deleted file mode 100644 index dcc120e00104..000000000000 --- a/src/Nethermind/Nethermind.Runner/Data/static-nodes.json +++ /dev/null @@ -1,4 +0,0 @@ -[ -] - - diff --git a/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs b/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs index c9c4312a67b9..735fecf7a790 100644 --- a/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs +++ b/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs @@ -36,7 +36,9 @@ public ApiBuilder(IProcessExitSource processExitSource, IConfigProvider configPr _processExitSource = processExitSource; _configProvider = configProvider ?? throw new ArgumentNullException(nameof(configProvider)); _initConfig = configProvider.GetConfig(); - _jsonSerializer = new EthereumJsonSerializer(configProvider.GetConfig().JsonSerializationMaxDepth); + IJsonRpcConfig? jsonRpcConfig = configProvider.GetConfig(); + EthereumJsonSerializer.StrictHexFormat = jsonRpcConfig.StrictHexFormat; + _jsonSerializer = new EthereumJsonSerializer(jsonRpcConfig.JsonSerializationMaxDepth); ChainSpec = LoadChainSpec(_jsonSerializer); } diff --git a/src/Nethermind/Nethermind.Runner/Monitoring/DataFeed.cs b/src/Nethermind/Nethermind.Runner/Monitoring/DataFeed.cs index 12e8c198ce0e..6553a6433fcc 100644 --- a/src/Nethermind/Nethermind.Runner/Monitoring/DataFeed.cs +++ b/src/Nethermind/Nethermind.Runner/Monitoring/DataFeed.cs @@ -426,7 +426,7 @@ private class ReceiptForWeb public UInt256 EffectiveGasPrice { get; set; } public Address? ContractAddress { get; set; } public LogEntryForWeb[] Logs { get; set; } - public long Status { get; set; } + public long? Status { get; set; } public UInt256 BlobGasPrice { get; set; } public ulong BlobGasUsed { get; set; } } diff --git a/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj b/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj index 36b19e804671..bc999d46fb23 100644 --- a/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj +++ b/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj @@ -87,11 +87,6 @@ PreserveNewest true - - PreserveNewest - PreserveNewest - true - diff --git a/src/Nethermind/Nethermind.Runner/Program.cs b/src/Nethermind/Nethermind.Runner/Program.cs index 39b78655ff70..54fce7573e1b 100644 --- a/src/Nethermind/Nethermind.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Runner/Program.cs @@ -429,6 +429,8 @@ RootCommand CreateRootCommand() BasicOptions.PluginsDirectory ]; + rootCommand.Description = "Nethermind Ethereum execution client"; + var versionOption = (VersionOption)rootCommand.Children.SingleOrDefault(c => c is VersionOption); if (versionOption is not null) @@ -484,30 +486,31 @@ void ResolveDataDirectory(string? path, IInitConfig initConfig, IKeyStoreConfig { if (string.IsNullOrWhiteSpace(path)) { - initConfig.BaseDbPath ??= string.Empty.GetApplicationResourcePath("db"); - keyStoreConfig.KeyStoreDirectory ??= string.Empty.GetApplicationResourcePath("keystore"); - initConfig.LogDirectory ??= string.Empty.GetApplicationResourcePath("logs"); + initConfig.BaseDbPath ??= "db".GetApplicationResourcePath(); + initConfig.LogDirectory ??= "logs".GetApplicationResourcePath(); + keyStoreConfig.KeyStoreDirectory ??= "keystore".GetApplicationResourcePath(); } else { string newDbPath = initConfig.BaseDbPath.GetApplicationResourcePath(path); - string newKeyStorePath = keyStoreConfig.KeyStoreDirectory.GetApplicationResourcePath(path); string newLogDirectory = initConfig.LogDirectory.GetApplicationResourcePath(path); + string newKeyStorePath = keyStoreConfig.KeyStoreDirectory.GetApplicationResourcePath(path); string newSnapshotPath = snapshotConfig.SnapshotDirectory.GetApplicationResourcePath(path); if (logger.IsInfo) { logger.Info($"{nameof(initConfig.BaseDbPath)}: {Path.GetFullPath(newDbPath)}"); - logger.Info($"{nameof(keyStoreConfig.KeyStoreDirectory)}: {Path.GetFullPath(newKeyStorePath)}"); logger.Info($"{nameof(initConfig.LogDirectory)}: {Path.GetFullPath(newLogDirectory)}"); + logger.Info($"{nameof(keyStoreConfig.KeyStoreDirectory)}: {Path.GetFullPath(newKeyStorePath)}"); if (snapshotConfig.Enabled) logger.Info($"{nameof(snapshotConfig.SnapshotDirectory)}: {Path.GetFullPath(newSnapshotPath)}"); } initConfig.BaseDbPath = newDbPath; - keyStoreConfig.KeyStoreDirectory = newKeyStorePath; + initConfig.DataDir = path; initConfig.LogDirectory = newLogDirectory; + keyStoreConfig.KeyStoreDirectory = newKeyStorePath; snapshotConfig.SnapshotDirectory = newSnapshotPath; } } diff --git a/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json b/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json index d98931d69082..05debc054992 100644 --- a/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json +++ b/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json @@ -35,13 +35,6 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Holesky": { - "commandName": "Project", - "commandLineArgs": "-c holesky --data-dir .data", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, "Hoodi": { "commandName": "Project", "commandLineArgs": "-c hoodi --data-dir .data", @@ -121,11 +114,11 @@ }, "Docker": { "commandName": "Docker", - "commandLineArgs": "-c holesky --data-dir /data --jsonrpc-enginehost 0.0.0.0 --jsonrpc-engineport 8551 --jsonrpc-host 0.0.0.0" + "commandLineArgs": "-c hoodi --data-dir /data --jsonrpc-enginehost 0.0.0.0 --jsonrpc-engineport 8551 --jsonrpc-host 0.0.0.0" }, "WSL": { "commandName": "WSL2", - "commandLineArgs": "\"{OutDir}/nethermind.dll\" -c holesky --data-dir .data", + "commandLineArgs": "\"{OutDir}/nethermind.dll\" -c hoodi --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, diff --git a/src/Nethermind/Nethermind.Runner/configs/base-mainnet.json b/src/Nethermind/Nethermind.Runner/configs/base-mainnet.json index 98ce76b4cb79..fd8d2ac609ec 100644 --- a/src/Nethermind/Nethermind.Runner/configs/base-mainnet.json +++ b/src/Nethermind/Nethermind.Runner/configs/base-mainnet.json @@ -13,8 +13,8 @@ "FastSync": true, "SnapSync": true, "FastSyncCatchUpHeightDelta": "10000000000", - "PivotNumber": 36710000, - "PivotHash": "0xd759d49a2f98653576201ce1ee1176526dce480784128f51a1598fce9f9c08ed" + "PivotNumber": 37310000, + "PivotHash": "0x6914684fefc777f69ee0ff75937ae5aeb0f8713ad9a3401ca65b5da2bc15d268" }, "Discovery": { "DiscoveryVersion": "V5" diff --git a/src/Nethermind/Nethermind.Runner/configs/base-sepolia.json b/src/Nethermind/Nethermind.Runner/configs/base-sepolia.json index 82a53a7697df..c21fd84419d4 100644 --- a/src/Nethermind/Nethermind.Runner/configs/base-sepolia.json +++ b/src/Nethermind/Nethermind.Runner/configs/base-sepolia.json @@ -13,8 +13,8 @@ "FastSync": true, "SnapSync": true, "FastSyncCatchUpHeightDelta": "10000000000", - "PivotNumber": 32220000, - "PivotHash": "0xe49b39cff401755b410215f0f4eed742e3794133d3c39e161da585ba870be7aa" + "PivotNumber": 32820000, + "PivotHash": "0xd9097a4f00e38b283e9af34f30c8dd7ff290332f63ec3a40a8f5000b8cab29ab" }, "Discovery": { "DiscoveryVersion": "V5" diff --git a/src/Nethermind/Nethermind.Runner/configs/chiado.json b/src/Nethermind/Nethermind.Runner/configs/chiado.json index 2f4b49fb3637..8fcd38ba486e 100644 --- a/src/Nethermind/Nethermind.Runner/configs/chiado.json +++ b/src/Nethermind/Nethermind.Runner/configs/chiado.json @@ -18,8 +18,8 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 17990000, - "PivotHash": "0x7aaf5863f3ee3416c1bd0aea25cca5202dcf9e96ee41fa721b729e768d5c72c8", + "PivotNumber": 18430000, + "PivotHash": "0x1502c72cda0ba5433ee8d5746b949dcab48660b3e1c28ca8dfbf6c7f4cb17a2c", "PivotTotalDifficulty": "231708131825107706987652208063906496124457284", "FastSyncCatchUpHeightDelta": 10000000000, "UseGethLimitsInFastBlocks": false diff --git a/src/Nethermind/Nethermind.Runner/configs/gnosis.json b/src/Nethermind/Nethermind.Runner/configs/gnosis.json index 3406ad3b0437..c52465595601 100644 --- a/src/Nethermind/Nethermind.Runner/configs/gnosis.json +++ b/src/Nethermind/Nethermind.Runner/configs/gnosis.json @@ -14,8 +14,8 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 42330000, - "PivotHash": "0x31957266d3c971a5090266ca6eec717f443b894f0e496e5735d5c2ccfa697607", + "PivotNumber": 42800000, + "PivotHash": "0xfe4205f86c14a6ac7183460eecbef5d78383019ff6257f7478fe83241e6b5070", "PivotTotalDifficulty": "8626000110427538733349499292577475819600160930", "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000, diff --git a/src/Nethermind/Nethermind.Runner/configs/holesky.json b/src/Nethermind/Nethermind.Runner/configs/holesky.json deleted file mode 100644 index ac05e9bbc1f4..000000000000 --- a/src/Nethermind/Nethermind.Runner/configs/holesky.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/NethermindEth/core-scripts/refs/heads/main/schemas/config.json", - "Init": { - "ChainSpecPath": "chainspec/holesky.json", - "GenesisHash": "0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4", - "BaseDbPath": "nethermind_db/holesky", - "LogFileName": "holesky.log" - }, - "TxPool": { - "Size": 1024 - }, - "Sync": { - "FastSync": true, - "SnapSync": true, - "FastSyncCatchUpHeightDelta": "10000000000" - }, - "Metrics": { - "NodeName": "Holesky" - }, - "Blocks": { - "TargetBlockGasLimit": 60000000 - }, - "JsonRpc": { - "Enabled": true, - "Timeout": 20000, - "Host": "127.0.0.1", - "Port": 8545, - "EngineHost": "127.0.0.1", - "EnginePort": 8551, - "EngineEnabledModules": "net,eth,subscribe,engine,web3,client,flashbots" - }, - "Merge": { - "Enabled": true - } -} diff --git a/src/Nethermind/Nethermind.Runner/configs/holesky_archive.json b/src/Nethermind/Nethermind.Runner/configs/holesky_archive.json deleted file mode 100644 index 52e790c09b17..000000000000 --- a/src/Nethermind/Nethermind.Runner/configs/holesky_archive.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/NethermindEth/core-scripts/refs/heads/main/schemas/config.json", - "Init": { - "ChainSpecPath": "chainspec/holesky.json", - "GenesisHash": "0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4", - "BaseDbPath": "nethermind_db/holesky_archive", - "LogFileName": "holesky_archive.log" - }, - "TxPool": { - "Size": 1024 - }, - "Metrics": { - "NodeName": "Holesky Archive" - }, - "Blocks": { - "TargetBlockGasLimit": 60000000 - }, - "Receipt": { - "TxLookupLimit": 0 - }, - "Pruning": { - "Mode": "None" - }, - "JsonRpc": { - "Enabled": true, - "Timeout": 20000, - "Host": "127.0.0.1", - "Port": 8545, - "EngineHost": "127.0.0.1", - "EnginePort": 8551, - "EngineEnabledModules": "net,eth,subscribe,engine,web3,client" - }, - "Merge": { - "Enabled": true - } -} diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.json b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.json index e16c5574f563..d061f18fc6d5 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.json +++ b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.json @@ -12,9 +12,9 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 19870000, - "PivotHash": "0x84d796ac1e45b7afb917b6982adf81b69d3f4e12c79897811d93d2249c0a4b1d", - "PivotTotalDifficulty": "36316120" + "PivotNumber": 20350000, + "PivotHash": "0x1322f952adf52f780a1f4042b50698ca2cbffe47a6c52c3ee8cfe45325939504", + "PivotTotalDifficulty": "37043030" }, "Metrics": { "NodeName": "JOC-Mainnet" diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-testnet.json b/src/Nethermind/Nethermind.Runner/configs/joc-testnet.json index d6ab25b20670..5a26d1836576 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-testnet.json +++ b/src/Nethermind/Nethermind.Runner/configs/joc-testnet.json @@ -12,9 +12,9 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 13480000, - "PivotHash": "0x8da352578c2ae41884ea6038557a995bd9e68ceb348ea1d6d63baf61210c1137", - "PivotTotalDifficulty": "22696204" + "PivotNumber": 13960000, + "PivotHash": "0x49b78a4bc2c1c2b973d928e142cd005fc660af2082d877923aecf373264153fa", + "PivotTotalDifficulty": "23415163" }, "Metrics": { "NodeName": "JOC-Testnet" diff --git a/src/Nethermind/Nethermind.Runner/configs/linea-mainnet.json b/src/Nethermind/Nethermind.Runner/configs/linea-mainnet.json index 91a4a83e6fde..b6a1fe0ab7c3 100644 --- a/src/Nethermind/Nethermind.Runner/configs/linea-mainnet.json +++ b/src/Nethermind/Nethermind.Runner/configs/linea-mainnet.json @@ -17,9 +17,9 @@ }, "Sync": { "SnapSync": true, - "PivotNumber": 23870000, - "PivotHash": "0x86831e1abd1a1ce41632622cc0ef9f138fbdea36565f2b5edfeaae30074ef190", - "PivotTotalDifficulty": "47740001", + "PivotNumber": 24900000, + "PivotHash": "0x41545430031ba1fb54677ac7edaaa09c5718232ec455da9c149f3d63f033c80c", + "PivotTotalDifficulty": "0", "HeaderStateDistance": 6 }, "JsonRpc": { diff --git a/src/Nethermind/Nethermind.Runner/configs/linea-sepolia.json b/src/Nethermind/Nethermind.Runner/configs/linea-sepolia.json index 8dba21318497..3c7ccf4ada35 100644 --- a/src/Nethermind/Nethermind.Runner/configs/linea-sepolia.json +++ b/src/Nethermind/Nethermind.Runner/configs/linea-sepolia.json @@ -17,8 +17,8 @@ }, "Sync": { "SnapSync": true, - "PivotNumber": 18740000, - "PivotHash": "0x5a7078f32b191d45994522667182abaaf8d934d0f4eb44773c70a04d9fe2d1a5", + "PivotNumber": 19890000, + "PivotHash": "0x8ccc7427b89aca77749fcb3f1c5777201e8e5950e4bb0a095909ab7d92873d9e", "PivotTotalDifficulty": "37331807", "HeaderStateDistance": 6 }, diff --git a/src/Nethermind/Nethermind.Runner/configs/mainnet.json b/src/Nethermind/Nethermind.Runner/configs/mainnet.json index 50935387c034..888e464621a1 100644 --- a/src/Nethermind/Nethermind.Runner/configs/mainnet.json +++ b/src/Nethermind/Nethermind.Runner/configs/mainnet.json @@ -10,8 +10,8 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 23456000, - "PivotHash": "0x6bcae00d5fbb7f52b3cdd0296382b48936f53617d3ff72db97ff76883b3448f4", + "PivotNumber": 23657000, + "PivotHash": "0x9841197759955198a8726749a58082c579c08147f436d8f973c56fe82bb0b931", "PivotTotalDifficulty": "58750003716598352816469", "FastSyncCatchUpHeightDelta": "10000000000", "AncientReceiptsBarrier": 15537394, @@ -38,4 +38,4 @@ "Merge": { "Enabled": true } -} +} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Runner/configs/op-mainnet.json b/src/Nethermind/Nethermind.Runner/configs/op-mainnet.json index 19fd6e366f15..1b9888229448 100644 --- a/src/Nethermind/Nethermind.Runner/configs/op-mainnet.json +++ b/src/Nethermind/Nethermind.Runner/configs/op-mainnet.json @@ -15,8 +15,8 @@ "FastSyncCatchUpHeightDelta": "10000000000", "AncientBodiesBarrier": 105235063, "AncientReceiptsBarrier": 105235063, - "PivotNumber": 142300000, - "PivotHash": "0x2daa6fce13f9fbabdcdf465de3ade2abff236c64faf6a6bdd73893c8c06fb069" + "PivotNumber": 142910000, + "PivotHash": "0x0bb18551efe5c041345ae90cd3e86f3f9a1ff8d1022b9faf617010867c84994b" }, "Discovery": { "DiscoveryVersion": "V5" diff --git a/src/Nethermind/Nethermind.Runner/configs/op-sepolia.json b/src/Nethermind/Nethermind.Runner/configs/op-sepolia.json index af9ed95a130c..ac81375ccf85 100644 --- a/src/Nethermind/Nethermind.Runner/configs/op-sepolia.json +++ b/src/Nethermind/Nethermind.Runner/configs/op-sepolia.json @@ -13,8 +13,8 @@ "FastSync": true, "SnapSync": true, "FastSyncCatchUpHeightDelta": "10000000000", - "PivotNumber": 34200000, - "PivotHash": "0x307cf167cdadca341bd03c2b837110eab022d755d3f6fdef8f34a445d4562a9d" + "PivotNumber": 34800000, + "PivotHash": "0xe1682ee31297d2d1c8431a3ba2e9fba56ad95e2b9533f8d327095fd3d080a926" }, "Discovery": { "DiscoveryVersion": "V5" diff --git a/src/Nethermind/Nethermind.Runner/configs/sepolia.json b/src/Nethermind/Nethermind.Runner/configs/sepolia.json index 239ac4fe77b4..ba158eff855e 100644 --- a/src/Nethermind/Nethermind.Runner/configs/sepolia.json +++ b/src/Nethermind/Nethermind.Runner/configs/sepolia.json @@ -5,7 +5,6 @@ "GenesisHash": "0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9", "BaseDbPath": "nethermind_db/sepolia", "LogFileName": "sepolia.log", - "StaticNodesPath": "Data/static-nodes-sepolia.json", "MemoryHint": 1024000000 }, "TxPool": { @@ -18,8 +17,8 @@ "FastSync": true, "SnapSync": true, "UseGethLimitsInFastBlocks": true, - "PivotNumber": 9293000, - "PivotHash": "0x3d0f3b35a13d60cf1159f017e7c5680f507c963b1994192863b4e507f9f0256b", + "PivotNumber": 9489000, + "PivotHash": "0x0701715fe2509f8a91b39b64ec85eec8aeba14dce5dfca1a04a492962bc32198", "PivotTotalDifficulty": "17000018015853232", "FastSyncCatchUpHeightDelta": 10000000000, "AncientReceiptsBarrier": 1450409, diff --git a/src/Nethermind/Nethermind.Runner/configs/sepolia_archive.json b/src/Nethermind/Nethermind.Runner/configs/sepolia_archive.json index dc9fc01936e4..c39b2ba8fce3 100644 --- a/src/Nethermind/Nethermind.Runner/configs/sepolia_archive.json +++ b/src/Nethermind/Nethermind.Runner/configs/sepolia_archive.json @@ -5,7 +5,6 @@ "GenesisHash": "0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9", "BaseDbPath": "nethermind_db/sepolia_archive", "LogFileName": "sepolia.log", - "StaticNodesPath": "Data/static-nodes-sepolia.json", "MemoryHint": 1024000000 }, "TxPool": { diff --git a/src/Nethermind/Nethermind.Runner/configs/worldchain-mainnet.json b/src/Nethermind/Nethermind.Runner/configs/worldchain-mainnet.json index f73ea4b53ecc..6884eb77e242 100644 --- a/src/Nethermind/Nethermind.Runner/configs/worldchain-mainnet.json +++ b/src/Nethermind/Nethermind.Runner/configs/worldchain-mainnet.json @@ -13,8 +13,8 @@ "FastSync": true, "SnapSync": true, "FastSyncCatchUpHeightDelta": "10000000000", - "PivotNumber": 20430000, - "PivotHash": "0xc7f119c1482b823611ba492cc0092e3818b31016a959821236158e59d6bfa9c7" + "PivotNumber": 21040000, + "PivotHash": "0x14d35cdb573e4481eb718d15c1467d63118bfe9d30b83566a676d8c0247fc342" }, "Discovery": { "DiscoveryVersion": "V5" diff --git a/src/Nethermind/Nethermind.Runner/configs/worldchain-sepolia.json b/src/Nethermind/Nethermind.Runner/configs/worldchain-sepolia.json index 074d64137167..a938a000d049 100644 --- a/src/Nethermind/Nethermind.Runner/configs/worldchain-sepolia.json +++ b/src/Nethermind/Nethermind.Runner/configs/worldchain-sepolia.json @@ -13,8 +13,8 @@ "FastSync": true, "SnapSync": true, "FastSyncCatchUpHeightDelta": "10000000000", - "PivotNumber": 19830000, - "PivotHash": "0x25cce6bca17d76872fc0fb9dfe41046d14f7498453d43690324a53c795e73e13" + "PivotNumber": 20430000, + "PivotHash": "0xf74c53163dd35e1332ad5f4ad92240d0284b5802846684043b2296e3a11b42b0" }, "Discovery": { "DiscoveryVersion": "V5" diff --git a/src/Nethermind/Nethermind.Runner/packages.lock.json b/src/Nethermind/Nethermind.Runner/packages.lock.json index c926560bb69c..c563194f57dc 100644 --- a/src/Nethermind/Nethermind.Runner/packages.lock.json +++ b/src/Nethermind/Nethermind.Runner/packages.lock.json @@ -1830,11 +1830,13 @@ "nethermind.txpool": { "type": "Project", "dependencies": { + "Collections.Pooled": "[1.0.82, )", "Nethermind.Config": "[1.35.0-unstable, )", "Nethermind.Core": "[1.35.0-unstable, )", "Nethermind.Crypto": "[1.35.0-unstable, )", "Nethermind.Db": "[1.35.0-unstable, )", "Nethermind.Evm": "[1.35.0-unstable, )", + "Nethermind.Network.Contract": "[1.35.0-unstable, )", "Nethermind.State": "[1.35.0-unstable, )", "NonBlocking": "[2.1.2, )" } @@ -1916,6 +1918,12 @@ "resolved": "2.1.5.1529", "contentHash": "PUnwltfxU0V/rIHLlcVyCOmpaFCgYPDfBlp32F33qW5ZldjcWpDt3tEZAmFvX0Rt/Va05djdHkKH/QzXyDhpTg==" }, + "Collections.Pooled": { + "type": "CentralTransitive", + "requested": "[1.0.82, )", + "resolved": "1.0.82", + "contentHash": "o14V0k8bg+EPMwaVJcFbLNrkyDPaqNx6fWTTjJLgVWJLu939kVIAAWKaV5lCwZ7LM/tv59igi65rVWRyjILc0Q==" + }, "CommunityToolkit.HighPerformance": { "type": "CentralTransitive", "requested": "[8.4.0, )", diff --git a/src/Nethermind/Nethermind.Serialization.Json/DoubleConverter.cs b/src/Nethermind/Nethermind.Serialization.Json/DoubleConverter.cs index 46541c689b16..9828ac29f5c6 100644 --- a/src/Nethermind/Nethermind.Serialization.Json/DoubleConverter.cs +++ b/src/Nethermind/Nethermind.Serialization.Json/DoubleConverter.cs @@ -48,7 +48,7 @@ public override double[] Read( { throw new JsonException(); } - using ArrayPoolList values = new ArrayPoolList(16); + using ArrayPoolListRef values = new(16); while (reader.Read() && reader.TokenType == JsonTokenType.Number) { values.Add(reader.GetDouble()); diff --git a/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs b/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs index cd7d544be18e..787c0ad40c3c 100644 --- a/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs +++ b/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs @@ -9,6 +9,7 @@ using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; using Nethermind.Core.Collections; @@ -60,7 +61,7 @@ public string Serialize(T value, bool indented = false) private static JsonSerializerOptions CreateOptions(bool indented, IEnumerable converters = null, int maxDepth = DefaultMaxDepth) { - var options = new JsonSerializerOptions + var result = new JsonSerializerOptions { WriteIndented = indented, NewLine = "\n", @@ -95,13 +96,14 @@ private static JsonSerializerOptions CreateOptions(bool indented, IEnumerable()); - - return options; + result.Converters.AddRange(_additionalConverters); + result.Converters.AddRange(converters ?? Array.Empty()); + return result; } private static readonly List _additionalConverters = new(); @@ -113,6 +115,20 @@ public static void AddConverter(JsonConverter converter) JsonOptionsIndented = CreateOptions(indented: true); } + private static bool _strictHexFormat; + public static bool StrictHexFormat + { + get => _strictHexFormat; + set + { + if (_strictHexFormat == value) + return; + _strictHexFormat = value; + JsonOptions = CreateOptions(indented: false); + JsonOptionsIndented = CreateOptions(indented: true); + } + } + public static JsonSerializerOptions JsonOptions { get; private set; } = CreateOptions(indented: false); public static JsonSerializerOptions JsonOptionsIndented { get; private set; } = CreateOptions(indented: true); diff --git a/src/Nethermind/Nethermind.Core/Crypto/Hash256Converter.cs b/src/Nethermind/Nethermind.Serialization.Json/Hash256Converter.cs similarity index 77% rename from src/Nethermind/Nethermind.Core/Crypto/Hash256Converter.cs rename to src/Nethermind/Nethermind.Serialization.Json/Hash256Converter.cs index ed0de28c7ea8..2f74049a80ff 100644 --- a/src/Nethermind/Nethermind.Core/Crypto/Hash256Converter.cs +++ b/src/Nethermind/Nethermind.Serialization.Json/Hash256Converter.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +#nullable enable using System; using System.Text.Json.Serialization; using System.Text.Json; @@ -10,12 +11,20 @@ namespace Nethermind.Serialization.Json; public class Hash256Converter : JsonConverter { + private readonly bool _strictHexFormat; + + public Hash256Converter(bool strictHexFormat = false) + { + _strictHexFormat = strictHexFormat; + } + public override Hash256? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - byte[]? bytes = ByteArrayConverter.Convert(ref reader); + + byte[]? bytes = ByteArrayConverter.Convert(ref reader, _strictHexFormat); return bytes is null ? null : new Hash256(bytes); } @@ -30,7 +39,7 @@ public override void Write( // Methods needed to ser/de dictionary keys public override Hash256 ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - byte[]? bytes = ByteArrayConverter.Convert(ref reader); + byte[]? bytes = ByteArrayConverter.Convert(ref reader, _strictHexFormat); return bytes is null ? null! : new Hash256(bytes); } diff --git a/src/Nethermind/Nethermind.Core/Crypto/ValueHash256Converter.cs b/src/Nethermind/Nethermind.Serialization.Json/ValueHash256Converter.cs similarity index 74% rename from src/Nethermind/Nethermind.Core/Crypto/ValueHash256Converter.cs rename to src/Nethermind/Nethermind.Serialization.Json/ValueHash256Converter.cs index 0f4f0c65757e..18282391b58d 100644 --- a/src/Nethermind/Nethermind.Core/Crypto/ValueHash256Converter.cs +++ b/src/Nethermind/Nethermind.Serialization.Json/ValueHash256Converter.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +#nullable enable using System; using System.Text.Json; using System.Text.Json.Serialization; @@ -10,12 +11,19 @@ namespace Nethermind.Serialization.Json; public class ValueHash256Converter : JsonConverter { + private readonly bool _strictHexFormat; + + public ValueHash256Converter(bool strictHexFormat = false) + { + _strictHexFormat = strictHexFormat; + } + public override ValueHash256 Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - byte[]? bytes = ByteArrayConverter.Convert(ref reader); + byte[]? bytes = ByteArrayConverter.Convert(ref reader, _strictHexFormat); return bytes is null ? null : new ValueHash256(bytes); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs index 642f55829cbc..4bf4179fce24 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs @@ -9,15 +9,16 @@ namespace Nethermind.Serialization.Rlp; public class BlockBodyDecoder : IRlpValueDecoder, IRlpStreamDecoder { private readonly TxDecoder _txDecoder = TxDecoder.Instance; - private readonly HeaderDecoder _headerDecoder = new(); + private readonly IHeaderDecoder _headerDecoder; private readonly WithdrawalDecoder _withdrawalDecoderDecoder = new(); private static BlockBodyDecoder? _instance = null; public static BlockBodyDecoder Instance => _instance ??= new BlockBodyDecoder(); // Cant set to private because of `Rlp.RegisterDecoder`. - public BlockBodyDecoder() + public BlockBodyDecoder(IHeaderDecoder headerDecoder = null) { + _headerDecoder = headerDecoder ?? new HeaderDecoder(); } public int GetLength(BlockBody item, RlpBehaviors rlpBehaviors) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 50704bb6b423..55d42c6854a7 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -10,7 +10,7 @@ namespace Nethermind.Serialization.Rlp public class BlockDecoder(IHeaderDecoder headerDecoder) : IRlpValueDecoder, IRlpStreamDecoder { private readonly IHeaderDecoder _headerDecoder = headerDecoder ?? throw new ArgumentNullException(nameof(headerDecoder)); - private readonly BlockBodyDecoder _blockBodyDecoder = BlockBodyDecoder.Instance; + private readonly BlockBodyDecoder _blockBodyDecoder = new BlockBodyDecoder(headerDecoder); public BlockDecoder() : this(new HeaderDecoder()) { } @@ -69,7 +69,7 @@ public int GetLength(Block? item, RlpBehaviors rlpBehaviors) int sequenceLength = decoderContext.ReadSequenceLength(); int blockCheck = decoderContext.Position + sequenceLength; - BlockHeader header = Rlp.Decode(ref decoderContext); + BlockHeader header = _headerDecoder.Decode(ref decoderContext); BlockBody body = _blockBodyDecoder.DecodeUnwrapped(ref decoderContext, blockCheck); Block block = new(header, body) @@ -102,7 +102,7 @@ public void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehaviors = Rl (int contentLength, int txsLength, int unclesLength, int? withdrawalsLength) = GetContentLength(item, rlpBehaviors); stream.StartSequence(contentLength); - stream.Encode(item.Header); + _headerDecoder.Encode(stream, item.Header); stream.StartSequence(txsLength); for (int i = 0; i < item.Transactions.Length; i++) { @@ -126,7 +126,7 @@ public void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehaviors = Rl } } - public static ReceiptRecoveryBlock? DecodeToReceiptRecoveryBlock(MemoryManager? memoryManager, Memory memory, RlpBehaviors rlpBehaviors) + public ReceiptRecoveryBlock? DecodeToReceiptRecoveryBlock(MemoryManager? memoryManager, Memory memory, RlpBehaviors rlpBehaviors) { Rlp.ValueDecoderContext decoderContext = new Rlp.ValueDecoderContext(memory, true); @@ -139,7 +139,7 @@ public void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehaviors = Rl int sequenceLength = decoderContext.ReadSequenceLength(); int blockCheck = decoderContext.Position + sequenceLength; - BlockHeader header = Rlp.Decode(ref decoderContext); + BlockHeader header = _headerDecoder.Decode(ref decoderContext); int contentLength = decoderContext.ReadSequenceLength(); int transactionCount = decoderContext.PeekNumberOfItemsRemaining(decoderContext.Position + contentLength); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/CompactLogEntryDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/CompactLogEntryDecoder.cs index 2ce1c1348c74..7154922b8146 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/CompactLogEntryDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/CompactLogEntryDecoder.cs @@ -27,7 +27,7 @@ public class CompactLogEntryDecoder : IRlpDecoder Address? address = rlpStream.DecodeAddress(); long sequenceLength = rlpStream.ReadSequenceLength(); long untilPosition = rlpStream.Position + sequenceLength; - using ArrayPoolList topics = new((int)(sequenceLength * 2 / Rlp.LengthOfKeccakRlp)); + using ArrayPoolListRef topics = new((int)(sequenceLength * 2 / Rlp.LengthOfKeccakRlp)); while (rlpStream.Position < untilPosition) { topics.Add(rlpStream.DecodeZeroPrefixKeccak()); @@ -53,7 +53,7 @@ public class CompactLogEntryDecoder : IRlpDecoder Address? address = decoderContext.DecodeAddress(); long sequenceLength = decoderContext.ReadSequenceLength(); long untilPosition = decoderContext.Position + sequenceLength; - using ArrayPoolList topics = new((int)(sequenceLength * 2 / Rlp.LengthOfKeccakRlp)); + using ArrayPoolListRef topics = new((int)(sequenceLength * 2 / Rlp.LengthOfKeccakRlp)); while (decoderContext.Position < untilPosition) { topics.Add(decoderContext.DecodeZeroPrefixKeccak()); @@ -95,7 +95,7 @@ public static Hash256[] DecodeTopics(Rlp.ValueDecoderContext valueDecoderContext { long sequenceLength = valueDecoderContext.ReadSequenceLength(); long untilPosition = valueDecoderContext.Position + sequenceLength; - using ArrayPoolList topics = new((int)(sequenceLength * 2 / Rlp.LengthOfKeccakRlp)); + using ArrayPoolListRef topics = new((int)(sequenceLength * 2 / Rlp.LengthOfKeccakRlp)); while (valueDecoderContext.Position < untilPosition) { topics.Add(valueDecoderContext.DecodeZeroPrefixKeccak()); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/CompactReceiptStorageDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/CompactReceiptStorageDecoder.cs index 9833f5b881dc..b6dea59d6ecc 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/CompactReceiptStorageDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/CompactReceiptStorageDecoder.cs @@ -47,7 +47,7 @@ public CompactReceiptStorageDecoder() int sequenceLength = rlpStream.ReadSequenceLength(); int lastCheck = sequenceLength + rlpStream.Position; - using ArrayPoolList logEntries = new(sequenceLength * 2 / Rlp.LengthOfAddressRlp); + using ArrayPoolListRef logEntries = new(sequenceLength * 2 / Rlp.LengthOfAddressRlp); while (rlpStream.Position < lastCheck) { @@ -96,7 +96,7 @@ public CompactReceiptStorageDecoder() int lastCheck = sequenceLength + decoderContext.Position; // Don't know the size exactly, I'll just assume its just an address and add some margin - using ArrayPoolList logEntries = new(sequenceLength * 2 / Rlp.LengthOfAddressRlp); + using ArrayPoolListRef logEntries = new(sequenceLength * 2 / Rlp.LengthOfAddressRlp); while (decoderContext.Position < lastCheck) { logEntries.Add(CompactLogEntryDecoder.Decode(ref decoderContext, RlpBehaviors.AllowExtraBytes)); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/KeyValueStoreRlpExtensions.cs b/src/Nethermind/Nethermind.Serialization.Rlp/KeyValueStoreRlpExtensions.cs index b78f1a223fbd..a078240c27af 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/KeyValueStoreRlpExtensions.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/KeyValueStoreRlpExtensions.cs @@ -14,23 +14,24 @@ namespace Nethermind.Serialization.Rlp; public static class KeyValueStoreRlpExtensions { [SkipLocalsInit] - public static TItem? Get(this IReadOnlyKeyValueStore db, long blockNumber, ValueHash256 hash, IRlpStreamDecoder decoder, - ClockCache cache = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true) where TItem : class + public static TItem? Get(this IReadOnlyKeyValueStore db, long blockNumber, Hash256AsKey hash, IRlpStreamDecoder decoder, out bool fromCache, + ClockCache cache = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true) where TItem : class { Span dbKey = stackalloc byte[40]; - KeyValueStoreExtensions.GetBlockNumPrefixedKey(blockNumber, hash, dbKey); - return Get(db, hash, dbKey, decoder, cache, rlpBehaviors, shouldCache); + KeyValueStoreExtensions.GetBlockNumPrefixedKey(blockNumber, in hash.Value.ValueHash256, dbKey); + return Get(db, hash, dbKey, decoder, out fromCache, cache, rlpBehaviors, shouldCache); } - public static TItem? Get(this IReadOnlyKeyValueStore db, ValueHash256 key, IRlpStreamDecoder decoder, ClockCache cache = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true) where TItem : class - { - return Get(db, key, key.Bytes, decoder, cache, rlpBehaviors, shouldCache); - } + public static TItem? Get(this IReadOnlyKeyValueStore db, Hash256AsKey key, IRlpStreamDecoder decoder, ClockCache cache = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true) where TItem : class + => Get(db, key, key.Value.Bytes, decoder, out _, cache, rlpBehaviors, shouldCache); + + public static TItem? Get(this IReadOnlyKeyValueStore db, Hash256AsKey key, IRlpStreamDecoder decoder, out bool fromCache, ClockCache cache = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true) where TItem : class + => Get(db, key, key.Value.Bytes, decoder, out fromCache, cache, rlpBehaviors, shouldCache); public static TItem? Get(this IReadOnlyKeyValueStore db, long key, IRlpStreamDecoder? decoder, ClockCache? cache = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true) where TItem : class { ReadOnlySpan keyDb = key.ToBigEndianSpanWithoutLeadingZeros(out _); - return Get(db, key, keyDb, decoder, cache, rlpBehaviors, shouldCache); + return Get(db, key, keyDb, decoder, out _, cache, rlpBehaviors, shouldCache); } public static TItem? Get( @@ -38,6 +39,7 @@ public static class KeyValueStoreRlpExtensions TCacheKey cacheKey, ReadOnlySpan key, IRlpStreamDecoder decoder, + out bool fromCache, ClockCache cache = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true @@ -45,18 +47,22 @@ public static class KeyValueStoreRlpExtensions where TCacheKey : struct, IEquatable { TItem item = cache?.Get(cacheKey); - if (item is null) + if (item is not null) { - if (decoder is IRlpValueDecoder valueDecoder) - { - item = db is IReadOnlyNativeKeyValueStore native - ? Get(native, key, valueDecoder, rlpBehaviors) - : Get(db, key, valueDecoder, rlpBehaviors); - } - else - { - item = Get(db, key, decoder, rlpBehaviors); - } + fromCache = true; + return item; + } + + fromCache = false; + if (decoder is IRlpValueDecoder valueDecoder) + { + item = db is IReadOnlyNativeKeyValueStore native + ? Get(native, key, valueDecoder, rlpBehaviors) + : Get(db, key, valueDecoder, rlpBehaviors); + } + else + { + item = Get(db, key, decoder, rlpBehaviors); } if (shouldCache && cache is not null && item is not null) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/RlpDecoderExtensions.cs b/src/Nethermind/Nethermind.Serialization.Rlp/RlpDecoderExtensions.cs index 0f0c17e366c3..b371f2d0d370 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/RlpDecoderExtensions.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/RlpDecoderExtensions.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Nethermind.Core.Buffers; +using Nethermind.Core.Collections; namespace Nethermind.Serialization.Rlp { @@ -130,6 +131,27 @@ public static NettyRlpStream EncodeToNewNettyStream(this IRlpStreamDecoder return rlpStream; } + public static NettyRlpStream EncodeToNewNettyStream(this IRlpStreamDecoder decoder, in ArrayPoolListRef items, RlpBehaviors behaviors = RlpBehaviors.None) + { + int totalLength = 0; + for (int i = 0; i < items.Count; i++) + { + totalLength += decoder.GetLength(items[i], behaviors); + } + + int bufferLength = Rlp.LengthOfSequence(totalLength); + + NettyRlpStream rlpStream = new(NethermindBuffers.Default.Buffer(bufferLength)); + rlpStream.StartSequence(totalLength); + + for (int i = 0; i < items.Count; i++) + { + decoder.Encode(rlpStream, items[i], behaviors); + } + + return rlpStream; + } + public static SpanSource EncodeToSpanSource(this IRlpStreamDecoder decoder, T? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None, ICappedArrayPool? bufferPool = null) { diff --git a/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs b/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs index f9f627152f87..b3f6275ddece 100644 --- a/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs +++ b/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs @@ -30,10 +30,10 @@ public static class ShutterCrypto public readonly ref struct EncryptedMessage { - public readonly byte VersionId { get; init; } - public readonly G2 C1 { get; init; } - public readonly ReadOnlySpan C2 { get; init; } - public readonly ReadOnlySpan C3 { get; init; } + public byte VersionId { get; init; } + public G2 C1 { get; init; } + public ReadOnlySpan C2 { get; init; } + public ReadOnlySpan C3 { get; init; } } public class ShutterCryptoException(string message, Exception? innerException = null) : Exception(message, innerException); @@ -101,7 +101,7 @@ public static EncryptedMessage DecodeEncryptedMessage(ReadOnlySpan bytes) public static void RecoverSigma(out Span sigma, EncryptedMessage encryptedMessage, G1Affine decryptionKey) { - using ArrayPoolList buf = new(GT.Sz, GT.Sz); + using ArrayPoolListRef buf = new(GT.Sz, GT.Sz); GT p = new(buf.AsSpan()); p.MillerLoop(encryptedMessage.C1.ToAffine(), decryptionKey); sigma = Hash2(p); // key @@ -143,7 +143,7 @@ public static void ComputeBlockKeys(Span blockKeys, ReadOnlySpan sig public static void ComputeIdentity(G1 p, scoped ReadOnlySpan bytes) { int len = bytes.Length + 1; - using ArrayPoolList buf = new(len, len); + using ArrayPoolListRef buf = new(len, len); Span preimage = buf.AsSpan(); preimage[0] = 0x1; bytes.CopyTo(preimage[1..]); @@ -152,7 +152,7 @@ public static void ComputeIdentity(G1 p, scoped ReadOnlySpan bytes) public static Span Hash2(GT p) { - using ArrayPoolList buf = new(577, 577); + using ArrayPoolListRef buf = new(577, 577); Span preimage = buf.AsSpan(); preimage[0] = 0x2; p.FinalExp().ToBendian().CopyTo(preimage[1..]); @@ -167,7 +167,7 @@ public static void GTExp(ref GT x, UInt256 exp) } exp -= 1; - using ArrayPoolList buf = new(GT.Sz, GT.Sz); + using ArrayPoolListRef buf = new(GT.Sz, GT.Sz); x.Fp12.CopyTo(buf.AsSpan()); GT a = new(buf.AsSpan()); for (; exp > 0; exp >>= 1) @@ -184,7 +184,7 @@ public static void GTExp(ref GT x, UInt256 exp) public static bool CheckDecryptionKey(G1Affine decryptionKey, G2Affine eonPublicKey, G1Affine identity) { int len = GT.Sz * 2; - using ArrayPoolList buf = new(len, len); + using ArrayPoolListRef buf = new(len, len); GT p1 = new(buf.AsSpan()[..GT.Sz]); p1.MillerLoop(G2Affine.Generator(stackalloc long[G2Affine.Sz]), decryptionKey); GT p2 = new(buf.AsSpan()[GT.Sz..]); @@ -222,7 +222,7 @@ public static bool CheckValidatorRegistrySignatures(BlsSigner.AggregatedPublicKe public static EncryptedMessage Encrypt(ReadOnlySpan msg, G1 identity, G2 eonKey, ReadOnlySpan sigma) { int len = PaddedLength(msg); - using ArrayPoolList buf = new(len, len); + using ArrayPoolListRef buf = new(len, len); ComputeR(sigma, msg, out UInt256 r); ReadOnlySpan msgBlocks = PadAndSplit(buf.AsSpan(), msg); Span c3 = new byte[msgBlocks.Length]; @@ -266,7 +266,7 @@ private static void UnpadAndJoin(ref Span blocks) byte n = blocks[^1]; - if (n == 0 || n > 32) + if (n is 0 or > 32) { throw new ShutterCryptoException("Invalid padding length"); } @@ -276,7 +276,7 @@ private static void UnpadAndJoin(ref Span blocks) private static Span ComputeC2(scoped ReadOnlySpan sigma, UInt256 r, G1 identity, G2 eonKey) { - using ArrayPoolList buf = new(GT.Sz, GT.Sz); + using ArrayPoolListRef buf = new(GT.Sz, GT.Sz); GT p = new(buf.AsSpan()); p.MillerLoop(eonKey, identity); GTExp(ref p, r); @@ -306,7 +306,7 @@ private static Span PadAndSplit(Span paddedBytes, scoped ReadOnlySpa private static void ComputeR(scoped ReadOnlySpan sigma, scoped ReadOnlySpan msg, out UInt256 r) { int len = 32 + msg.Length; - using ArrayPoolList buf = new(len, len); + using ArrayPoolListRef buf = new(len, len); Span preimage = buf.AsSpan(); sigma.CopyTo(preimage); msg.CopyTo(preimage[32..]); @@ -338,7 +338,7 @@ internal static Hash256 GenerateHash(ulong instanceId, ulong eon, ulong slot, ul private static void Hash3(ReadOnlySpan bytes, out UInt256 res) { int len = bytes.Length + 1; - using ArrayPoolList buf = new(len, len); + using ArrayPoolListRef buf = new(len, len); Span preimage = buf.AsSpan(); preimage[0] = 0x3; bytes.CopyTo(preimage[1..]); @@ -349,7 +349,7 @@ private static void Hash3(ReadOnlySpan bytes, out UInt256 res) private static ValueHash256 Hash4(ReadOnlySpan bytes) { int len = bytes.Length + 1; - using ArrayPoolList buf = new(len, len); + using ArrayPoolListRef buf = new(len, len); Span preimage = buf.AsSpan(); preimage[0] = 0x4; bytes.CopyTo(preimage[1..]); @@ -358,11 +358,11 @@ private static ValueHash256 Hash4(ReadOnlySpan bytes) private readonly struct SlotDecryptionIdentites { - public readonly ulong InstanceID { get; init; } - public readonly ulong Eon { get; init; } - public readonly ulong Slot { get; init; } - public readonly ulong TxPointer { get; init; } - public readonly IEnumerable> IdentityPreimages { get; init; } + public ulong InstanceID { get; init; } + public ulong Eon { get; init; } + public ulong Slot { get; init; } + public ulong TxPointer { get; init; } + public IEnumerable> IdentityPreimages { get; init; } } } diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs index 29a82c734184..36bdd07c53d1 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs @@ -79,7 +79,7 @@ public void Missing_dependent_property() { var loader = new ChainSpecFileLoader(new EthereumJsonSerializer(), LimboTraceLogger.Instance); string path = Path.Combine(TestContext.CurrentContext.WorkDirectory, - $"../../../../{Assembly.GetExecutingAssembly().GetName().Name}/Specs/holesky_missing_deposit_contract.json"); + $"../../../../{Assembly.GetExecutingAssembly().GetName().Name}/Specs/hoodi_no_deposit_contract.json"); InvalidDataException? exception = Assert.Throws(() => loader.LoadEmbeddedOrFromFile(path)); using (Assert.EnterMultipleScope()) { @@ -191,10 +191,10 @@ public void Sepolia_loads_properly(ForkActivation forkActivation) } IReleaseSpec postCancunSpec = provider.GetSpec((2, SepoliaSpecProvider.CancunTimestamp)); - VerifyCancunSpecificsForMainnetAndHoleskyAndSepolia(postCancunSpec); + VerifyCancunSpecificsForMainnetAndSepolia(postCancunSpec); IReleaseSpec postPragueSpec = provider.GetSpec((2, SepoliaSpecProvider.PragueTimestamp)); - VerifyPragueSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postPragueSpec); + VerifyPragueSpecificsForMainnetHoodiAndSepolia(provider.ChainId, postPragueSpec); IReleaseSpec postOsakaSpec = provider.GetSpec((2, SepoliaSpecProvider.OsakaTimestamp)); IReleaseSpec postBPO1Spec = provider.GetSpec((2, SepoliaSpecProvider.BPO1Timestamp)); @@ -202,62 +202,7 @@ public void Sepolia_loads_properly(ForkActivation forkActivation) VerifyOsakaSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postOsakaSpec, postBPO1Spec, postBPO2Spec); } - public static IEnumerable HoleskyActivations - { - get - { - yield return new TestCaseData(new ForkActivation(0, HoleskySpecProvider.GenesisTimestamp)) { TestName = "Genesis" }; - yield return new TestCaseData(new ForkActivation(1, HoleskySpecProvider.ShanghaiTimestamp)) { TestName = "Shanghai" }; - yield return new TestCaseData(new ForkActivation(3, HoleskySpecProvider.ShanghaiTimestamp + 24)) { TestName = "Post Shanghai" }; - yield return new TestCaseData(new ForkActivation(4, HoleskySpecProvider.CancunTimestamp - 1)) { TestName = "Before Cancun" }; - yield return new TestCaseData(new ForkActivation(5, HoleskySpecProvider.CancunTimestamp)) { TestName = "Cancun" }; - yield return new TestCaseData(new ForkActivation(6, HoleskySpecProvider.CancunTimestamp + 24)) { TestName = "Post Cancun" }; - yield return new TestCaseData(new ForkActivation(7, HoleskySpecProvider.PragueTimestamp - 1)) { TestName = "Before Prague" }; - yield return new TestCaseData(new ForkActivation(8, HoleskySpecProvider.PragueTimestamp)) { TestName = "Prague" }; - yield return new TestCaseData(new ForkActivation(9, HoleskySpecProvider.OsakaTimestamp - 1)) { TestName = "Before Osaka" }; - yield return new TestCaseData(new ForkActivation(10, HoleskySpecProvider.OsakaTimestamp)) { TestName = "Osaka" }; - yield return new TestCaseData(new ForkActivation(11, HoleskySpecProvider.BPO1Timestamp - 1)) { TestName = "Before BPO1" }; - yield return new TestCaseData(new ForkActivation(12, HoleskySpecProvider.BPO1Timestamp)) { TestName = "BPO1" }; - yield return new TestCaseData(new ForkActivation(13, HoleskySpecProvider.BPO2Timestamp - 1)) { TestName = "Before BPO2" }; - yield return new TestCaseData(new ForkActivation(14, HoleskySpecProvider.BPO2Timestamp)) { TestName = "BPO2" }; - yield return new TestCaseData(new ForkActivation(15, HoleskySpecProvider.BPO2Timestamp + 100000000)) { TestName = "Future BPO2" }; - } - } - - [TestCaseSource(nameof(HoleskyActivations))] - public void Holesky_loads_properly(ForkActivation forkActivation) - { - ChainSpec chainSpec = LoadChainSpecFromChainFolder("holesky"); - ChainSpecBasedSpecProvider provider = new(chainSpec); - ISpecProvider hardCodedSpec = HoleskySpecProvider.Instance; - - CompareSpecs(hardCodedSpec, provider, forkActivation); - using (Assert.EnterMultipleScope()) - { - Assert.That(provider.TerminalTotalDifficulty, Is.EqualTo(hardCodedSpec.TerminalTotalDifficulty)); - Assert.That(provider.GenesisSpec.Eip1559TransitionBlock, Is.Zero); - Assert.That(provider.GenesisSpec.DifficultyBombDelay, Is.Zero); - Assert.That(provider.ChainId, Is.EqualTo(BlockchainIds.Holesky)); - Assert.That(provider.NetworkId, Is.EqualTo(BlockchainIds.Holesky)); - } - - IReleaseSpec postCancunSpec = provider.GetSpec((2, HoleskySpecProvider.CancunTimestamp)); - VerifyCancunSpecificsForMainnetAndHoleskyAndSepolia(postCancunSpec); - - IReleaseSpec postPragueSpec = provider.GetSpec((2, HoleskySpecProvider.PragueTimestamp)); - VerifyPragueSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postPragueSpec); - - IReleaseSpec postOsakaSpec = provider.GetSpec((2, HoleskySpecProvider.OsakaTimestamp)); - IReleaseSpec postBPO1Spec = provider.GetSpec((2, HoleskySpecProvider.BPO1Timestamp)); - IReleaseSpec postBPO2Spec = provider.GetSpec((2, HoleskySpecProvider.BPO2Timestamp)); - VerifyOsakaSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postOsakaSpec, postBPO1Spec, postBPO2Spec); - - // because genesis time for holesky is set 5 minutes before the launch of the chain. this test fails. - //GetTransitionTimestamps(chainSpec.Parameters).Should().AllSatisfy( - // t => ValidateSlotByTimestamp(t, HoleskySpecProvider.GenesisTimestamp).Should().BeTrue()); - } - - private static void VerifyCancunSpecificsForMainnetAndHoleskyAndSepolia(IReleaseSpec spec) + private static void VerifyCancunSpecificsForMainnetAndSepolia(IReleaseSpec spec) { using (Assert.EnterMultipleScope()) { @@ -268,7 +213,7 @@ private static void VerifyCancunSpecificsForMainnetAndHoleskyAndSepolia(IRelease } } - private static void VerifyPragueSpecificsForMainnetHoleskyHoodiAndSepolia(ulong chainId, IReleaseSpec spec) + private static void VerifyPragueSpecificsForMainnetHoodiAndSepolia(ulong chainId, IReleaseSpec spec) { using (Assert.EnterMultipleScope()) { @@ -303,7 +248,7 @@ private static void VerifyPragueSpecificsForMainnetHoleskyHoodiAndSepolia(ulong private static void VerifyOsakaSpecificsForMainnetHoleskyHoodiAndSepolia(ulong chainId, IReleaseSpec postOsakaSpec, IReleaseSpec postBPO1Spec, IReleaseSpec postBPO2Spec) { - VerifyPragueSpecificsForMainnetHoleskyHoodiAndSepolia(chainId, postOsakaSpec); + VerifyPragueSpecificsForMainnetHoodiAndSepolia(chainId, postOsakaSpec); using (Assert.EnterMultipleScope()) { Assert.That(postBPO1Spec.BlobBaseFeeUpdateFraction, Is.EqualTo((UInt256)8346193)); @@ -355,10 +300,10 @@ public void Hoodi_loads_properly(ForkActivation forkActivation) } IReleaseSpec postCancunSpec = provider.GetSpec((2, HoodiSpecProvider.CancunTimestamp)); - VerifyCancunSpecificsForMainnetAndHoleskyAndSepolia(postCancunSpec); + VerifyCancunSpecificsForMainnetAndSepolia(postCancunSpec); IReleaseSpec postPragueSpec = provider.GetSpec((2, HoodiSpecProvider.PragueTimestamp)); - VerifyPragueSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postPragueSpec); + VerifyPragueSpecificsForMainnetHoodiAndSepolia(provider.ChainId, postPragueSpec); IReleaseSpec postOsakaSpec = provider.GetSpec((2, HoodiSpecProvider.OsakaTimestamp)); IReleaseSpec postBPO1Spec = provider.GetSpec((2, HoodiSpecProvider.BPO1Timestamp)); @@ -379,9 +324,10 @@ public static IEnumerable ChiadoActivations yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.CancunTimestamp)) { TestName = "Cancun" }; yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.PragueTimestamp - 1)) { TestName = "Before Prague" }; yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.PragueTimestamp)) { TestName = "Prague" }; - yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.OsakaTimestamp - 1)) { TestName = "Before Osaka" }; - yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.OsakaTimestamp)) { TestName = "Osaka" }; - yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.OsakaTimestamp + 100000000)) { TestName = "Future" }; + yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.PragueTimestamp + 100000000)) { TestName = "Future" }; + // yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.OsakaTimestamp - 1)) { TestName = "Before Osaka" }; + // yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.OsakaTimestamp)) { TestName = "Osaka" }; + // yield return new TestCaseData((ForkActivation)(1, ChiadoSpecProvider.OsakaTimestamp + 100000000)) { TestName = "Future" }; } } @@ -407,12 +353,12 @@ public void Chiado_loads_properly(ForkActivation forkActivation) IReleaseSpec? postCancunSpec = provider.GetSpec((1, ChiadoSpecProvider.CancunTimestamp)); IReleaseSpec? prePragueSpec = provider.GetSpec((1, ChiadoSpecProvider.PragueTimestamp - 1)); IReleaseSpec? postPragueSpec = provider.GetSpec((1, ChiadoSpecProvider.PragueTimestamp)); - IReleaseSpec? postOsakaSpec = provider.GetSpec((1, ChiadoSpecProvider.OsakaTimestamp)); + // IReleaseSpec? postOsakaSpec = provider.GetSpec((1, ChiadoSpecProvider.OsakaTimestamp)); VerifyGnosisShanghaiSpecifics(preShanghaiSpec, postShanghaiSpec); VerifyGnosisCancunSpecifics(postCancunSpec); VerifyGnosisPragueSpecifics(prePragueSpec, postPragueSpec, ChiadoSpecProvider.FeeCollector); - VerifyGnosisOsakaSpecifics(postOsakaSpec, ChiadoSpecProvider.FeeCollector); + // VerifyGnosisOsakaSpecifics(postOsakaSpec, ChiadoSpecProvider.FeeCollector); using (Assert.EnterMultipleScope()) { @@ -650,8 +596,8 @@ public void Mainnet_loads_properly(ForkActivation forkActivation) IReleaseSpec postCancunSpec = provider.GetSpec(MainnetSpecProvider.CancunActivation); IReleaseSpec postPragueSpec = provider.GetSpec(MainnetSpecProvider.PragueActivation); - VerifyCancunSpecificsForMainnetAndHoleskyAndSepolia(postCancunSpec); - VerifyPragueSpecificsForMainnetHoleskyHoodiAndSepolia(provider.ChainId, postPragueSpec); + VerifyCancunSpecificsForMainnetAndSepolia(postCancunSpec); + VerifyPragueSpecificsForMainnetHoodiAndSepolia(provider.ChainId, postPragueSpec); } [Flags] diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecLoaderTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecLoaderTests.cs index 7021af806999..9731d5ea03aa 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecLoaderTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecLoaderTests.cs @@ -94,32 +94,6 @@ public void Can_load_sepolia() chainSpec.ShanghaiTimestamp.Should().Be(1677557088); } - [Test] - public void Can_load_holesky() - { - string path = Path.Combine(TestContext.CurrentContext.WorkDirectory, "../../../../", "Chains/holesky.json"); - ChainSpec chainSpec = LoadChainSpec(path); - - Assert.That(chainSpec.NetworkId, Is.EqualTo(17000), $"{nameof(chainSpec.NetworkId)}"); - Assert.That(chainSpec.Name, Is.EqualTo("Holesky Testnet"), $"{nameof(chainSpec.Name)}"); - Assert.That(chainSpec.DataDir, Is.EqualTo("holesky"), $"{nameof(chainSpec.DataDir)}"); - Assert.That(chainSpec.SealEngineType, Is.EqualTo(SealEngineType.Ethash), "engine"); - - chainSpec.DaoForkBlockNumber.Should().Be(null); - chainSpec.TangerineWhistleBlockNumber.Should().Be(0); - chainSpec.SpuriousDragonBlockNumber.Should().Be(0); - chainSpec.ByzantiumBlockNumber.Should().Be(0); - chainSpec.ConstantinopleBlockNumber.Should().Be(0); - chainSpec.ConstantinopleFixBlockNumber.Should().Be(0); - chainSpec.IstanbulBlockNumber.Should().Be(0); - chainSpec.BerlinBlockNumber.Should().Be(0); - chainSpec.LondonBlockNumber.Should().Be(0); - chainSpec.ShanghaiTimestamp.Should().Be(HoleskySpecProvider.ShanghaiTimestamp); - chainSpec.ShanghaiTimestamp.Should().Be(HoleskySpecProvider.Instance.TimestampFork); - // chainSpec.CancunTimestamp.Should().Be(HoleskySpecProvider.CancunTimestamp); - } - - [Test] public void Can_load_hoodi() { diff --git a/src/Nethermind/Nethermind.Specs.Test/Nethermind.Specs.Test.csproj b/src/Nethermind/Nethermind.Specs.Test/Nethermind.Specs.Test.csproj index 5763bf5296e1..79f401ccb4ee 100644 --- a/src/Nethermind/Nethermind.Specs.Test/Nethermind.Specs.Test.csproj +++ b/src/Nethermind/Nethermind.Specs.Test/Nethermind.Specs.Test.csproj @@ -1,4 +1,4 @@ - + @@ -21,10 +21,9 @@ PreserveNewest - - - - + + PreserveNewest + PreserveNewest diff --git a/src/Nethermind/Nethermind.Specs.Test/Specs/holesky_missing_deposit_contract.json b/src/Nethermind/Nethermind.Specs.Test/Specs/holesky_missing_deposit_contract.json deleted file mode 100644 index a494c60f15ed..000000000000 --- a/src/Nethermind/Nethermind.Specs.Test/Specs/holesky_missing_deposit_contract.json +++ /dev/null @@ -1,1077 +0,0 @@ -{ - "name": "Holesky Testnet", - "dataDir": "holesky", - "engine": { - "Ethash": {} - }, - "params": { - "accountStartNonce": "0x0", - "chainID": "0x4268", - "networkID": "0x4268", - "eip140Transition": "0x0", - "eip145Transition": "0x0", - "eip150Transition": "0x0", - "eip155Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "eip1283Transition": "0x0", - "eip1283DisableTransition": "0x0", - "eip152Transition": "0x0", - "eip1108Transition": "0x0", - "eip1344Transition": "0x0", - "eip1884Transition": "0x0", - "eip2028Transition": "0x0", - "eip2200Transition": "0x0", - "eip2565Transition": "0x0", - "eip2929Transition": "0x0", - "eip2930Transition": "0x0", - "eip1559Transition": "0x0", - "eip3198Transition": "0x0", - "eip3529Transition": "0x0", - "eip3541Transition": "0x0", - "beaconChainGenesisTimestamp": "0x65156994", - "eip3651TransitionTimestamp": "0x6516eac0", - "eip3855TransitionTimestamp": "0x6516eac0", - "eip3860TransitionTimestamp": "0x6516eac0", - "eip4895TransitionTimestamp": "0x6516eac0", - "eip1153TransitionTimestamp": "0x65C36AC0", - "eip4788TransitionTimestamp": "0x65C36AC0", - "eip4844TransitionTimestamp": "0x65C36AC0", - "eip5656TransitionTimestamp": "0x65C36AC0", - "eip6780TransitionTimestamp": "0x65C36AC0", - "eip2537TransitionTimestamp": "0x67BCEAC0", - "eip2935TransitionTimestamp": "0x67BCEAC0", - "eip6110TransitionTimestamp": "0x67BCEAC0", - "eip7002TransitionTimestamp": "0x67BCEAC0", - "eip7251TransitionTimestamp": "0x67BCEAC0", - "eip7623TransitionTimestamp": "0x67BCEAC0", - "eip7702TransitionTimestamp": "0x67BCEAC0", - "terminalTotalDifficulty": "0x0", - "gasLimitBoundDivisor": "0x400", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x0", - "maximumExtraDataSize": "0xffff", - "minGasLimit": "0x1388", - "registrar": "0x0000000000000000000000000000000000000000", - "MergeForkIdTransition": "0x0", - "blobSchedule": [ - { - "timestamp": "0x67BCEAC0", - "target": 6, - "max": 9, - "baseFeeUpdateFraction": "0x4c6964" - } - ] - }, - "genesis": { - "seal": { - "ethereum": { - "nonce": "0x1234", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x01", - "author": "0x0000000000000000000000000000000000000000", - "extraData": "", - "gasLimit": "0x17D7840", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "0x65156994" - }, - "nodes": [ - "enode://ac906289e4b7f12df423d654c5a962b6ebe5b3a74cc9e06292a85221f9a64a6f1cfdd6b714ed6dacef51578f92b34c60ee91e9ede9c7f8fadc4d347326d95e2b@146.190.13.128:30303", - "enode://a3435a0155a3e837c02f5e7f5662a2f1fbc25b48e4dc232016e1c51b544cb5b4510ef633ea3278c0e970fa8ad8141e2d4d0f9f95456c537ff05fdf9b31c15072@178.128.136.233:30303" - ], - "accounts": { - "0x0000000000000000000000000000000000000000": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000001": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000002": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000003": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000004": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000005": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000006": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000007": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000008": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000009": { - "balance": "1" - }, - "0x000000000000000000000000000000000000000a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000000b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000000c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000000d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000000e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000000f": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000010": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000011": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000012": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000013": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000014": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000015": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000016": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000017": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000018": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000019": { - "balance": "1" - }, - "0x000000000000000000000000000000000000001a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000001b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000001c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000001d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000001e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000001f": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000020": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000021": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000022": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000023": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000024": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000025": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000026": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000027": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000028": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000029": { - "balance": "1" - }, - "0x000000000000000000000000000000000000002a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000002b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000002c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000002d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000002e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000002f": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000030": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000031": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000032": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000033": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000034": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000035": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000036": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000037": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000038": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000039": { - "balance": "1" - }, - "0x000000000000000000000000000000000000003a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000003b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000003c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000003d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000003e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000003f": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000040": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000041": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000042": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000043": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000044": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000045": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000046": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000047": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000048": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000049": { - "balance": "1" - }, - "0x000000000000000000000000000000000000004a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000004b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000004c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000004d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000004e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000004f": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000050": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000051": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000052": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000053": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000054": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000055": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000056": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000057": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000058": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000059": { - "balance": "1" - }, - "0x000000000000000000000000000000000000005a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000005b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000005c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000005d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000005e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000005f": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000060": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000061": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000062": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000063": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000064": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000065": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000066": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000067": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000068": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000069": { - "balance": "1" - }, - "0x000000000000000000000000000000000000006a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000006b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000006c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000006d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000006e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000006f": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000070": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000071": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000072": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000073": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000074": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000075": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000076": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000077": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000078": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000079": { - "balance": "1" - }, - "0x000000000000000000000000000000000000007a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000007b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000007c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000007d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000007e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000007f": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000080": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000081": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000082": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000083": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000084": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000085": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000086": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000087": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000088": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000089": { - "balance": "1" - }, - "0x000000000000000000000000000000000000008a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000008b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000008c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000008d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000008e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000008f": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000090": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000091": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000092": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000093": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000094": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000095": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000096": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000097": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000098": { - "balance": "1" - }, - "0x0000000000000000000000000000000000000099": { - "balance": "1" - }, - "0x000000000000000000000000000000000000009a": { - "balance": "1" - }, - "0x000000000000000000000000000000000000009b": { - "balance": "1" - }, - "0x000000000000000000000000000000000000009c": { - "balance": "1" - }, - "0x000000000000000000000000000000000000009d": { - "balance": "1" - }, - "0x000000000000000000000000000000000000009e": { - "balance": "1" - }, - "0x000000000000000000000000000000000000009f": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a0": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a1": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a2": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a3": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a4": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a5": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a6": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a7": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a8": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000a9": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000aa": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ab": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ac": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ad": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ae": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000af": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b0": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b1": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b2": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b3": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b4": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b5": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b6": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b7": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b8": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000b9": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ba": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000bb": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000bc": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000bd": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000be": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000bf": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c0": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c1": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c2": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c3": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c4": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c5": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c6": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c7": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c8": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000c9": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ca": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000cb": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000cc": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000cd": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ce": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000cf": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d0": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d1": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d2": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d3": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d4": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d5": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d6": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d7": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d8": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000d9": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000da": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000db": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000dc": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000dd": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000de": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000df": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e0": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e1": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e2": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e3": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e4": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e5": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e6": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e7": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e8": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000e9": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ea": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000eb": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ec": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ed": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ee": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ef": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f0": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f1": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f2": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f3": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f4": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f5": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f6": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f7": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f8": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000f9": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000fa": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000fb": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000fc": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000fd": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000fe": { - "balance": "1" - }, - "0x00000000000000000000000000000000000000ff": { - "balance": "1" - }, - "0x4242424242424242424242424242424242424242": { - "balance": "0", - "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a26469706673582212201dd26f37a621703009abf16e77e69c93dc50c79db7f6cc37543e3e0e3decdc9764736f6c634300060b0033", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000022": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", - "0x0000000000000000000000000000000000000000000000000000000000000023": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", - "0x0000000000000000000000000000000000000000000000000000000000000024": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", - "0x0000000000000000000000000000000000000000000000000000000000000025": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", - "0x0000000000000000000000000000000000000000000000000000000000000026": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", - "0x0000000000000000000000000000000000000000000000000000000000000027": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", - "0x0000000000000000000000000000000000000000000000000000000000000028": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", - "0x0000000000000000000000000000000000000000000000000000000000000029": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", - "0x000000000000000000000000000000000000000000000000000000000000002a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", - "0x000000000000000000000000000000000000000000000000000000000000002b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", - "0x000000000000000000000000000000000000000000000000000000000000002c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", - "0x000000000000000000000000000000000000000000000000000000000000002d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", - "0x000000000000000000000000000000000000000000000000000000000000002e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", - "0x000000000000000000000000000000000000000000000000000000000000002f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", - "0x0000000000000000000000000000000000000000000000000000000000000030": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", - "0x0000000000000000000000000000000000000000000000000000000000000031": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", - "0x0000000000000000000000000000000000000000000000000000000000000032": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", - "0x0000000000000000000000000000000000000000000000000000000000000033": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", - "0x0000000000000000000000000000000000000000000000000000000000000034": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", - "0x0000000000000000000000000000000000000000000000000000000000000035": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", - "0x0000000000000000000000000000000000000000000000000000000000000036": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", - "0x0000000000000000000000000000000000000000000000000000000000000037": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", - "0x0000000000000000000000000000000000000000000000000000000000000038": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", - "0x0000000000000000000000000000000000000000000000000000000000000039": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", - "0x000000000000000000000000000000000000000000000000000000000000003a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", - "0x000000000000000000000000000000000000000000000000000000000000003b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", - "0x000000000000000000000000000000000000000000000000000000000000003c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", - "0x000000000000000000000000000000000000000000000000000000000000003d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", - "0x000000000000000000000000000000000000000000000000000000000000003e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", - "0x000000000000000000000000000000000000000000000000000000000000003f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", - "0x0000000000000000000000000000000000000000000000000000000000000040": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" - } - }, - "0x0000006916a87b82333f4245046623b23794C65C": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x0be949928Ff199c9EBA9E110db210AA5C94EFAd0": { - "balance": "0x7c13bc4b2c133c56000000" - }, - "0x0C100000006d7b5e23a1eAEE637f28cA32Cd5b31": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x0C35317B7a96C454E2CB3d1A255D775Ab112cCc8": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x0d731cfabC5574329823F26d488416451d2ea376": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x0e79065B5F11b5BD1e62B935A600976ffF3754B9": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x105083929bF9bb22C26cB1777Ec92661170D4285": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x10F5d45854e038071485AC9e402308cF80D2d2fE": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x1268AD189526AC0b386faF06eFfC46779c340eE6": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x12Cba59f5A74DB81a12ff63C349Bd82CBF6007C2": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x1446D7f6dF00380F246d8211dE7f0FaBC4Fd248C": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x164e38a375247A784A81d420201AA8fe4E513921": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x1B7aA44088a0eA95bdc65fef6E5071E946Bf7d8f": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x222222222222cF64a76AE3d36859958c864fDA2c": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x2f14582947E292a2eCd20C430B46f2d27CFE213c": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x2f2c75B5Dd5D246194812b00eEb3B09c2c66e2eE": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x341c40b94bf2afbfa42573cb78f16ee15a056238": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x34f845773D4364999f2fbC7AA26ABDeE902cBb46": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x3C75594181e03E8ECD8468A0037F058a9dAfad79": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x462396E69dBfa455F405f4DD82F3014Af8003B72": { - "balance": "0xa56fa5b99019a5c8000000" - }, - "0x49Df3CCa2670eB0D591146B16359fe336e476F29": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x4D0b04b405c6b62C7cFC3aE54759747e2C0b4662": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x4D496CcC28058B1D74B7a19541663E21154f9c84": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x509a7667aC8D0320e36172c192506a6188aA84f6": { - "balance": "0x7c13bc4b2c133c56000000" - }, - "0x5180db0237291A6449DdA9ed33aD90a38787621c": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x52730f347dEf6BA09adfF62EaC60D5fEe8205BC4": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x5EAC0fBd3dfef8aE3efa3c5dc1aa193bc6033dFd": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x6a7aA9b882d50Bb7bc5Da1a244719C99f12F06a3": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x6Cc9397c3B38739daCbfaA68EaD5F5D77Ba5F455": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x762cA62ca2549ad806763B3Aa1eA317c429bDBDa": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x778F5F13C4Be78A3a4d7141BCB26999702f407CF": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x875D25Ee4bC604C71BaF6236a8488F22399BED4b": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x8dF7878d3571BEF5e5a744F96287C8D20386d75A": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x9E415A096fF77650dc925dEA546585B4adB322B6": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xA0766B65A4f7B1da79a1AF79aC695456eFa28644": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xA29B144A449E414A472c60C7AAf1aaFfE329021D": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xa55395566b0b54395B3246f96A0bDc4b8a483df9": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xAC9ba72fb61aA7c31A95df0A8b6ebA6f41EF875e": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xB0498C15879db2eE5471d4926c5fAA25C9a09683": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xB19Fb4c1f280327e60Ed37b1Dc6EE77533539314": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xC21cB9C99C316d1863142F7dD86dd5496D81A8D6": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xc473d412dc52e349862209924c8981b2ee420768": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xC48E23C5F6e1eA0BaEf6530734edC3968f79Af2e": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xc6e2459991BfE27cca6d86722F35da23A1E4Cb97": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xD3994e4d3202dD23c8497d7F75bF1647d1DA1bb1": { - "balance": "0x19D971E4FE8401E74000000" - }, - "0xDCA6e9B48Ea86AeBFDf9929949124042296b6e34": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xe0a2Bd4258D2768837BAa26A28fE71Dc079f84c7": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xEA28d002042fd9898D0Db016be9758eeAFE35C1E": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xEfA7454f1116807975A4750B46695E967850de5D": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xFBFd6Fa9F73Ac6A058E01259034C28001BEf8247": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xe0991E844041bE6F11B99da5b114b6bCf84EBd57": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x15E719b6AcAf1E4411Bf0f9576CB1D0dB161DdFc": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x346D827a75F98F0A7a324Ff80b7C3F90252E8baC": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x73b2e0E54510239E22cC936F0b4a6dE1acf0AbdE": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xBb977B2EE8a111D788B3477D242078d0B837E72b": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x834Dbf5A03e29c25bc55459cCe9c021EeBE676Ad": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xD1F77E4C1C45186e8653C489F90e008a73597296": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xb04aeF2a3d2D86B01006cCD4339A2e943d9c6480": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xC9CA2bA9A27De1Db589d8c33Ab8EDFa2111b31fb": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x4BC656B34De23896fa6069C9862F355b740401aF": { - "balance": "0x084595161401484a000000" - } - } -} diff --git a/src/Nethermind/Chains/holesky.json b/src/Nethermind/Nethermind.Specs.Test/Specs/hoodi_no_deposit_contract.json similarity index 81% rename from src/Nethermind/Chains/holesky.json rename to src/Nethermind/Nethermind.Specs.Test/Specs/hoodi_no_deposit_contract.json index 10364a90ced1..52a476237965 100644 --- a/src/Nethermind/Chains/holesky.json +++ b/src/Nethermind/Nethermind.Specs.Test/Specs/hoodi_no_deposit_contract.json @@ -1,23 +1,31 @@ { - "name": "Holesky Testnet", - "dataDir": "holesky", + "name": "Hoodi Testnet", + "dataDir": "hoodi", "engine": { "Ethash": {} }, "params": { + "gasLimitBoundDivisor": "0x400", + "registrar": "0x0000000000000000000000000000000000000000", "accountStartNonce": "0x0", - "chainID": "0x4268", - "networkID": "0x4268", - "eip140Transition": "0x0", - "eip145Transition": "0x0", + "maximumExtraDataSize": "0xffff", + "minGasLimit": "0x1388", + "chainID": "0x88bb0", + "networkID": "0x88bb0", + "MergeForkIdTransition": "0x0", + "maxCodeSize": "0x6000", + "maxCodeSizeTransition": "0x0", "eip150Transition": "0x0", - "eip155Transition": "0x0", + "eip158Transition": "0x0", "eip160Transition": "0x0", "eip161abcTransition": "0x0", "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip140Transition": "0x0", "eip211Transition": "0x0", "eip214Transition": "0x0", "eip658Transition": "0x0", + "eip145Transition": "0x0", "eip1014Transition": "0x0", "eip1052Transition": "0x0", "eip1283Transition": "0x0", @@ -35,63 +43,55 @@ "eip3198Transition": "0x0", "eip3529Transition": "0x0", "eip3541Transition": "0x0", - "beaconChainGenesisTimestamp": "0x65156994", - "eip3651TransitionTimestamp": "0x6516eac0", - "eip3855TransitionTimestamp": "0x6516eac0", - "eip3860TransitionTimestamp": "0x6516eac0", - "eip4895TransitionTimestamp": "0x6516eac0", - "eip1153TransitionTimestamp": "0x65C36AC0", - "eip4788TransitionTimestamp": "0x65C36AC0", - "eip4844TransitionTimestamp": "0x65C36AC0", - "eip5656TransitionTimestamp": "0x65C36AC0", - "eip6780TransitionTimestamp": "0x65C36AC0", - "eip2537TransitionTimestamp": "0x67BCEAC0", - "eip2935TransitionTimestamp": "0x67BCEAC0", - "eip6110TransitionTimestamp": "0x67BCEAC0", - "eip7002TransitionTimestamp": "0x67BCEAC0", - "eip7251TransitionTimestamp": "0x67BCEAC0", - "eip7623TransitionTimestamp": "0x67BCEAC0", - "eip7702TransitionTimestamp": "0x67BCEAC0", - "eip7594TransitionTimestamp": "0x68dceac0", - "eip7823TransitionTimestamp": "0x68dceac0", - "eip7825TransitionTimestamp": "0x68dceac0", - "eip7883TransitionTimestamp": "0x68dceac0", - "eip7918TransitionTimestamp": "0x68dceac0", - "eip7934TransitionTimestamp": "0x68dceac0", - "eip7939TransitionTimestamp": "0x68dceac0", - "eip7951TransitionTimestamp": "0x68dceac0", - "depositContractAddress": "0x4242424242424242424242424242424242424242", + "mergeForkIdTransition": "0x0", "terminalTotalDifficulty": "0x0", - "gasLimitBoundDivisor": "0x400", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x0", - "maximumExtraDataSize": "0xffff", - "minGasLimit": "0x1388", - "registrar": "0x0000000000000000000000000000000000000000", - "MergeForkIdTransition": "0x0", + "eip4895TransitionTimestamp": "0x0", + "eip3855TransitionTimestamp": "0x0", + "eip3651TransitionTimestamp": "0x0", + "eip3860TransitionTimestamp": "0x0", + "eip4844TransitionTimestamp": "0x0", + "eip4788TransitionTimestamp": "0x0", + "eip1153TransitionTimestamp": "0x0", + "eip5656TransitionTimestamp": "0x0", + "eip6780TransitionTimestamp": "0x0", "blobSchedule": [ { "name": "prague", - "timestamp": "0x67bceac0", + "timestamp": "0x67e41118", "target": 6, "max": 9, "baseFeeUpdateFraction": "0x4c6964" }, { "name": "bpo1", - "timestamp": "0x68e46ac0", + "timestamp": "0x690b9118", "target": 10, "max": 15, "baseFeeUpdateFraction": "0x7f5a51" }, { "name": "bpo2", - "timestamp": "0x68ed6ac0", + "timestamp": "0x69149118", "target": 14, "max": 21, "baseFeeUpdateFraction": "0xb24b3f" } - ] + ], + "eip2537TransitionTimestamp": "0x67e41118", + "eip2935TransitionTimestamp": "0x67e41118", + "eip6110TransitionTimestamp": "0x67e41118", + "eip7002TransitionTimestamp": "0x67e41118", + "eip7251TransitionTimestamp": "0x67e41118", + "eip7702TransitionTimestamp": "0x67e41118", + "eip7623TransitionTimestamp": "0x67e41118", + "eip7594TransitionTimestamp": "0x69011118", + "eip7823TransitionTimestamp": "0x69011118", + "eip7825TransitionTimestamp": "0x69011118", + "eip7883TransitionTimestamp": "0x69011118", + "eip7918TransitionTimestamp": "0x69011118", + "eip7934TransitionTimestamp": "0x69011118", + "eip7939TransitionTimestamp": "0x69011118", + "eip7951TransitionTimestamp": "0x69011118" }, "genesis": { "seal": { @@ -102,15 +102,11 @@ }, "difficulty": "0x01", "author": "0x0000000000000000000000000000000000000000", - "extraData": "", - "gasLimit": "0x17D7840", + "timestamp": "0x67d80ec0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "0x65156994" + "extraData": "", + "gasLimit": "0x2255100" }, - "nodes": [ - "enode://ac906289e4b7f12df423d654c5a962b6ebe5b3a74cc9e06292a85221f9a64a6f1cfdd6b714ed6dacef51578f92b34c60ee91e9ede9c7f8fadc4d347326d95e2b@146.190.13.128:30303", - "enode://a3435a0155a3e837c02f5e7f5662a2f1fbc25b48e4dc232016e1c51b544cb5b4510ef633ea3278c0e970fa8ad8141e2d4d0f9f95456c537ff05fdf9b31c15072@178.128.136.233:30303" - ], "accounts": { "0x0000000000000000000000000000000000000000": { "balance": "1" @@ -880,9 +876,9 @@ "0x00000000000000000000000000000000000000ff": { "balance": "1" }, - "0x4242424242424242424242424242424242424242": { + "0x00000000219ab540356cBB839Cbe05303d7705Fa": { "balance": "0", - "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a26469706673582212201dd26f37a621703009abf16e77e69c93dc50c79db7f6cc37543e3e0e3decdc9764736f6c634300060b0033", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033", "storage": { "0x0000000000000000000000000000000000000000000000000000000000000022": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", "0x0000000000000000000000000000000000000000000000000000000000000023": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", @@ -917,185 +913,258 @@ "0x0000000000000000000000000000000000000000000000000000000000000040": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" } }, + "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02": { + "balance": "0", + "nonce": "1", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500" + }, + "0x0000F90827F1C53a10cb7A02335B175320002935": { + "balance": "0", + "nonce": "1", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500" + }, + "0x00000961Ef480Eb55e80D19ad83579A64c007002": { + "balance": "0", + "nonce": "1", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + }, + "0x0000BBdDc7CE488642fb579F8B00f3a590007251": { + "balance": "0", + "nonce": "1", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + }, + "0x9A27D0c715D3f2Af2fAc39a41C49ed35004a3Bcf": { + "balance": "500000000000000000000000000" + }, + "0x610866c6089768dA95524bcc4cE7dB61eDa3931c": { + "balance": "200000000000000000000000000" + }, + "0x462396E69dBfa455F405f4DD82F3014Af8003B72": { + "balance": "200000000000000000000000000" + }, + "0x1B7aA44088a0eA95bdc65fef6E5071E946Bf7d8f": { + "balance": "100000000000000000000000000" + }, + "0x4D496CcC28058B1D74B7a19541663E21154f9c84": { + "balance": "100000000000000000000000000" + }, + "0x6Cc9397c3B38739daCbfaA68EaD5F5D77Ba5F455": { + "balance": "100000000000000000000000000" + }, + "0xfc7AF49b80ACF041744366B02272019723C94F9c": { + "balance": "100000000000000000000000000" + }, + "0x0C100000006d7b5e23a1eAEE637f28cA32Cd5b31": { + "balance": "100000000000000000000000000" + }, + "0x2f14582947E292a2eCd20C430B46f2d27CFE213c": { + "balance": "100000000000000000000000000" + }, + "0x73b2e0E54510239E22cC936F0b4a6dE1acf0AbdE": { + "balance": "100000000000000000000000000" + }, + "0x778F5F13C4Be78A3a4d7141BCB26999702f407CF": { + "balance": "100000000000000000000000000" + }, + "0xc6e2459991BfE27cca6d86722F35da23A1E4Cb97": { + "balance": "100000000000000000000000000" + }, + "0x03B1F066125897834e46aBd7DC6415D8759F8372": { + "balance": "100000000000000000000000000" + }, + "0x10F5d45854e038071485AC9e402308cF80D2d2fE": { + "balance": "100000000000000000000000000" + }, + "0x2f2c75B5Dd5D246194812b00eEb3B09c2c66e2eE": { + "balance": "100000000000000000000000000" + }, "0x0000006916a87b82333f4245046623b23794C65C": { - "balance": "0x52b7d2dcc80cd2e4000000" + "balance": "100000000000000000000000000" + }, + "0xB19Fb4c1f280327e60Ed37b1Dc6EE77533539314": { + "balance": "100000000000000000000000000" + }, + "0x5F10DA9B68F06C76f3c624F8b2eE3b2A2698351b": { + "balance": "100000000000000000000000000" + }, + "0x509a7667aC8D0320e36172c192506a6188aA84f6": { + "balance": "100000000000000000000000000" + }, + "0x6a7aA9b882d50Bb7bc5Da1a244719C99f12F06a3": { + "balance": "100000000000000000000000000" + }, + "0xFBFd6Fa9F73Ac6A058E01259034C28001BEf8247": { + "balance": "100000000000000000000000000" }, "0x0be949928Ff199c9EBA9E110db210AA5C94EFAd0": { - "balance": "0x7c13bc4b2c133c56000000" + "balance": "10000000000000000000000000" }, - "0x0C100000006d7b5e23a1eAEE637f28cA32Cd5b31": { - "balance": "0x52b7d2dcc80cd2e4000000" + "0xDA29BB71669f46F2a779b4b62f03644A84eE3479": { + "balance": "10000000000000000000000000" + }, + "0xDbf640DeA047FA7ee746D01a31782386f827CFC1": { + "balance": "10000000000000000000000000" + }, + "0x9029c772dde847622df1553ed9d9bdb7812e4f93": { + "balance": "1000000000000000000000000" + }, + "0xC564AF154621Ee8D0589758d535511aEc8f67b40": { + "balance": "10000000000000000000000000" + }, + "0x8dF7878d3571BEF5e5a744F96287C8D20386d75A": { + "balance": "1000000000000000000000000" }, "0x0C35317B7a96C454E2CB3d1A255D775Ab112cCc8": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x0d731cfabC5574329823F26d488416451d2ea376": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x0e79065B5F11b5BD1e62B935A600976ffF3754B9": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x105083929bF9bb22C26cB1777Ec92661170D4285": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x10F5d45854e038071485AC9e402308cF80D2d2fE": { - "balance": "0x52b7d2dcc80cd2e4000000" + "balance": "1000000000000000000000000" }, "0x1268AD189526AC0b386faF06eFfC46779c340eE6": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x12Cba59f5A74DB81a12ff63C349Bd82CBF6007C2": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x1446D7f6dF00380F246d8211dE7f0FaBC4Fd248C": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x164e38a375247A784A81d420201AA8fe4E513921": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x1B7aA44088a0eA95bdc65fef6E5071E946Bf7d8f": { - "balance": "0x52b7d2dcc80cd2e4000000" + "balance": "1000000000000000000000000" }, "0x222222222222cF64a76AE3d36859958c864fDA2c": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x2f14582947E292a2eCd20C430B46f2d27CFE213c": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x2f2c75B5Dd5D246194812b00eEb3B09c2c66e2eE": { - "balance": "0x52b7d2dcc80cd2e4000000" + "balance": "1000000000000000000000000" }, "0x341c40b94bf2afbfa42573cb78f16ee15a056238": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x34f845773D4364999f2fbC7AA26ABDeE902cBb46": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x3C75594181e03E8ECD8468A0037F058a9dAfad79": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x462396E69dBfa455F405f4DD82F3014Af8003B72": { - "balance": "0xa56fa5b99019a5c8000000" + "balance": "1000000000000000000000000" }, "0x49Df3CCa2670eB0D591146B16359fe336e476F29": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x4D0b04b405c6b62C7cFC3aE54759747e2C0b4662": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x4D496CcC28058B1D74B7a19541663E21154f9c84": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x509a7667aC8D0320e36172c192506a6188aA84f6": { - "balance": "0x7c13bc4b2c133c56000000" + "balance": "1000000000000000000000000" }, "0x5180db0237291A6449DdA9ed33aD90a38787621c": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x52730f347dEf6BA09adfF62EaC60D5fEe8205BC4": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x5EAC0fBd3dfef8aE3efa3c5dc1aa193bc6033dFd": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x6a7aA9b882d50Bb7bc5Da1a244719C99f12F06a3": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0x6Cc9397c3B38739daCbfaA68EaD5F5D77Ba5F455": { - "balance": "0x52b7d2dcc80cd2e4000000" + "balance": "1000000000000000000000000" }, "0x762cA62ca2549ad806763B3Aa1eA317c429bDBDa": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x778F5F13C4Be78A3a4d7141BCB26999702f407CF": { - "balance": "0x52b7d2dcc80cd2e4000000" + "balance": "1000000000000000000000000" }, "0x875D25Ee4bC604C71BaF6236a8488F22399BED4b": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x8dF7878d3571BEF5e5a744F96287C8D20386d75A": { - "balance": "0x52b7d2dcc80cd2e4000000" + "balance": "1000000000000000000000000" }, "0x9E415A096fF77650dc925dEA546585B4adB322B6": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xA0766B65A4f7B1da79a1AF79aC695456eFa28644": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xA29B144A449E414A472c60C7AAf1aaFfE329021D": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xa55395566b0b54395B3246f96A0bDc4b8a483df9": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xAC9ba72fb61aA7c31A95df0A8b6ebA6f41EF875e": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xB0498C15879db2eE5471d4926c5fAA25C9a09683": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xB19Fb4c1f280327e60Ed37b1Dc6EE77533539314": { - "balance": "0x52b7d2dcc80cd2e4000000" + "balance": "1000000000000000000000000" }, "0xC21cB9C99C316d1863142F7dD86dd5496D81A8D6": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xc473d412dc52e349862209924c8981b2ee420768": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xC48E23C5F6e1eA0BaEf6530734edC3968f79Af2e": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xc6e2459991BfE27cca6d86722F35da23A1E4Cb97": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xD3994e4d3202dD23c8497d7F75bF1647d1DA1bb1": { - "balance": "0x19D971E4FE8401E74000000" + "balance": "1000000000000000000000000" }, "0xDCA6e9B48Ea86AeBFDf9929949124042296b6e34": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xe0a2Bd4258D2768837BAa26A28fE71Dc079f84c7": { - "balance": "0x52b7d2dcc80cd2e4000000" + "balance": "1000000000000000000000000" }, "0xEA28d002042fd9898D0Db016be9758eeAFE35C1E": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xEfA7454f1116807975A4750B46695E967850de5D": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xFBFd6Fa9F73Ac6A058E01259034C28001BEf8247": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xe0991E844041bE6F11B99da5b114b6bCf84EBd57": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x15E719b6AcAf1E4411Bf0f9576CB1D0dB161DdFc": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x346D827a75F98F0A7a324Ff80b7C3F90252E8baC": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x73b2e0E54510239E22cC936F0b4a6dE1acf0AbdE": { - "balance": "0x52b7d2dcc80cd2e4000000" - }, - "0xBb977B2EE8a111D788B3477D242078d0B837E72b": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0x834Dbf5A03e29c25bc55459cCe9c021EeBE676Ad": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xD1F77E4C1C45186e8653C489F90e008a73597296": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xb04aeF2a3d2D86B01006cCD4339A2e943d9c6480": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, "0xC9CA2bA9A27De1Db589d8c33Ab8EDFa2111b31fb": { - "balance": "0xd3c21bcecceda1000000" + "balance": "1000000000000000000000000" }, - "0x4BC656B34De23896fa6069C9862F355b740401aF": { - "balance": "0x084595161401484a000000" + "0xC48E23C5F6e1eA0BaEf6530734edC3968f79Af2e": { + "balance": "1000000000000000000000000" + }, + "0xeb742E43956E0ea89a2dB6bf6237C79EE4645981": { + "balance": "1000000000000000000000000" + }, + "0x26ecc0885bAB76EB602888dF8415bdf32d0F62bD": { + "balance": "1000000000000000000000000" + }, + "0xe4955e107EA530B4C2db8A323DF0A143D8A82B9C": { + "balance": "1000000000000000000000000" + }, + "0x45DcA0746fc56b400B291f5D9657a79464E8F152": { + "balance": "1000000000000000000000000" + }, + "0x404Ffd9dD65428e096d9a7B837924263Ef40FB31": { + "balance": "1000000000000000000000000" + }, + "0x11ecb03299080fc8d5c1b2fe0c0cd34890d5e1b5": { + "balance": "1000000000000000000000000" + }, + "0xD4BB555d3B0D7fF17c606161B44E372689C14F4B": { + "balance": "1000000000000000000000000" + }, + "0x9B383f8e4Cd5d3DD5F9006B6A508960A1e730375": { + "balance": "1000000000000000000000000" + }, + "0x22819830D4fe783d658841939Ad6D75b00A2B78c": { + "balance": "1000000000000000000000000" + }, + "0x0e79065B5F11b5BD1e62B935A600976ffF3754B9": { + "balance": "1000000000000000000000000" + }, + "0x767f7576944D321374921DF138589a30e3C5030d": { + "balance": "1000000000000000000000000" + }, + "0x9B491F043189af6d31d3D4b1A7cA1cF72f40C52a": { + "balance": "1000000000000000000000000" } - } + }, + "nodes": [ + "enode://2112dd3839dd752813d4df7f40936f06829fc54c0e051a93967c26e5f5d27d99d886b57b4ffcc3c475e930ec9e79c56ef1dbb7d86ca5ee83a9d2ccf36e5c240c@134.209.138.84:30303", + "enode://60203fcb3524e07c5df60a14ae1c9c5b24023ea5d47463dfae051d2c9f3219f309657537576090ca0ae641f73d419f53d8e8000d7a464319d4784acd7d2abc41@209.38.124.160:30303", + "enode://8ae4a48101b2299597341263da0deb47cc38aa4d3ef4b7430b897d49bfa10eb1ccfe1655679b1ed46928ef177fbf21b86837bd724400196c508427a6f41602cd@134.199.184.23:30303" + ] } diff --git a/src/Nethermind/Nethermind.Specs/ChiadoSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChiadoSpecProvider.cs index 9ef94e5708c2..006775826f26 100644 --- a/src/Nethermind/Nethermind.Specs/ChiadoSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChiadoSpecProvider.cs @@ -15,7 +15,7 @@ public class ChiadoSpecProvider : ISpecProvider public const ulong ShanghaiTimestamp = 0x646e0e4c; public const ulong CancunTimestamp = 0x65ba8e4c; public const ulong PragueTimestamp = 0x67c96e4c; - public const ulong OsakaTimestamp = 0x68f74e4c; + public const ulong OsakaTimestamp = ulong.MaxValue - 1; public static readonly Address FeeCollector = new("0x1559000000000000000000000000000000000000"); diff --git a/src/Nethermind/Nethermind.Specs/HoleskySpecProvider.cs b/src/Nethermind/Nethermind.Specs/HoleskySpecProvider.cs deleted file mode 100644 index 26c889c4690d..000000000000 --- a/src/Nethermind/Nethermind.Specs/HoleskySpecProvider.cs +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Threading; -using Nethermind.Core; -using Nethermind.Core.Specs; -using Nethermind.Int256; -using Nethermind.Specs.Forks; - -namespace Nethermind.Specs; - -public class HoleskySpecProvider : ISpecProvider -{ - public const ulong GenesisTimestamp = 0x65156994; - public const ulong ShanghaiTimestamp = 0x6516eac0; - public const ulong CancunTimestamp = 0x65C36AC0; - public const ulong PragueTimestamp = 0x67BCEAC0; - public const ulong OsakaTimestamp = 0x68dceac0; - public const ulong BPO1Timestamp = 0x68e46ac0; - public const ulong BPO2Timestamp = 0x68ed6ac0; - - private static IReleaseSpec? _prague; - - private static IReleaseSpec Prague => LazyInitializer.EnsureInitialized(ref _prague, - static () => new Prague { DepositContractAddress = Eip6110Constants.HoleskyDepositContractAddress }); - - private HoleskySpecProvider() { } - - IReleaseSpec ISpecProvider.GetSpecInternal(ForkActivation forkActivation) - { - return forkActivation.Timestamp switch - { - null or < ShanghaiTimestamp => GenesisSpec, - < CancunTimestamp => Shanghai.Instance, - < PragueTimestamp => Cancun.Instance, - < OsakaTimestamp => Prague, - < BPO1Timestamp => Osaka.Instance, - < BPO2Timestamp => BPO1.Instance, - _ => BPO2.Instance - }; - } - - public void UpdateMergeTransitionInfo(long? blockNumber, UInt256? terminalTotalDifficulty = null) - { - if (blockNumber is not null) - MergeBlockNumber = (ForkActivation)blockNumber; - if (terminalTotalDifficulty is not null) - TerminalTotalDifficulty = terminalTotalDifficulty; - } - - public ulong NetworkId => BlockchainIds.Holesky; - public ulong ChainId => NetworkId; - public long? DaoBlockNumber => null; - public ulong? BeaconChainGenesisTimestamp => GenesisTimestamp; - public ForkActivation? MergeBlockNumber { get; private set; } = (0, GenesisTimestamp); - public ulong TimestampFork => ShanghaiTimestamp; - public UInt256? TerminalTotalDifficulty { get; private set; } = 0; - public IReleaseSpec GenesisSpec { get; } = London.Instance; - public ForkActivation[] TransitionActivations { get; } = - [ - (1, ShanghaiTimestamp), - (2, CancunTimestamp), - (3, PragueTimestamp), - (4, OsakaTimestamp), - (5, BPO1Timestamp), - (6, BPO2Timestamp), - ]; - - public static readonly HoleskySpecProvider Instance = new(); -} diff --git a/src/Nethermind/Nethermind.State.Test/PatriciaTreeBulkSetterTests.cs b/src/Nethermind/Nethermind.State.Test/PatriciaTreeBulkSetterTests.cs index 61f260bbd2d3..5e2d1b68dc13 100644 --- a/src/Nethermind/Nethermind.State.Test/PatriciaTreeBulkSetterTests.cs +++ b/src/Nethermind/Nethermind.State.Test/PatriciaTreeBulkSetterTests.cs @@ -266,7 +266,7 @@ public void BulkSet(List<(Hash256 key, byte[] value)> existingItems, List<(Hash2 pTree.Commit(); - using ArrayPoolList entries = new ArrayPoolList(items.Count); + using ArrayPoolListRef entries = new(items.Count); foreach (var valueTuple in items) { entries.Add(new PatriciaTree.BulkSetEntry(valueTuple.key, valueTuple.value)); @@ -317,7 +317,7 @@ public void BulkSetRootHashUpdated(List<(Hash256 key, byte[] value)> existingIte pTree.UpdateRootHash(); - using ArrayPoolList entries = new ArrayPoolList(items.Count); + using ArrayPoolListRef entries = new(items.Count); foreach (var valueTuple in items) { entries.Add(new PatriciaTree.BulkSetEntry(valueTuple.key, valueTuple.value)); @@ -350,7 +350,7 @@ public void BulkSetPreSorted(List<(Hash256 key, byte[] value)> existingItems, Li pTree.Commit(); - using ArrayPoolList entries = new ArrayPoolList(items.Count); + using ArrayPoolListRef entries = new(items.Count); foreach (var valueTuple in items) { entries.Add(new PatriciaTree.BulkSetEntry(valueTuple.key, valueTuple.value)); @@ -411,7 +411,7 @@ public void BulkSetOneByOne(List<(Hash256 key, byte[] value)> existingItems, Lis long sw = Stopwatch.GetTimestamp(); foreach (var valueTuple in items) { - using ArrayPoolList entries = new ArrayPoolList(items.Count); + using ArrayPoolListRef entries = new(items.Count); entries.Add(new PatriciaTree.BulkSetEntry(valueTuple.key, valueTuple.value)); pTree.BulkSet(entries, PatriciaTree.Flags.None); } @@ -490,15 +490,24 @@ public void BulkSet_ShouldThrowOnNonUniqueEntries() Random rng = new Random(0); - using ArrayPoolList entries = new ArrayPoolList(3); + using ArrayPoolListRef entries = new(3); entries.Add(new PatriciaTree.BulkSetEntry(new ValueHash256("8818888888888888888888888888888888888888888888888888888888888888"), MakeRandomValue(rng))); entries.Add(new PatriciaTree.BulkSetEntry(new ValueHash256("8828888888888888888888888888888888888888888888888888888888888888"), MakeRandomValue(rng))); entries.Add(new PatriciaTree.BulkSetEntry(new ValueHash256("8848888888888888888888888888888888888888888888888888888888888888"), MakeRandomValue(rng))); entries.Add(new PatriciaTree.BulkSetEntry(new ValueHash256("8848888888888888888888888888888888888888888888888888888888888888"), MakeRandomValue(rng))); entries.Add(new PatriciaTree.BulkSetEntry(new ValueHash256("8858888888888888888888888888888888888888888888888888888888888888"), MakeRandomValue(rng))); - var act = () => pTree.BulkSet(entries); - act.Should().Throw(); + bool thrown = false; + try + { + pTree.BulkSet(entries); + } + catch (InvalidOperationException) + { + thrown = true; + } + + thrown.Should().BeTrue(); } public static IEnumerable BucketSortTestCase() diff --git a/src/Nethermind/Nethermind.State.Test/Proofs/AccountProofCollectorTests.cs b/src/Nethermind/Nethermind.State.Test/Proofs/AccountProofCollectorTests.cs index 16d54fd6d7f0..0f029804f994 100644 --- a/src/Nethermind/Nethermind.State.Test/Proofs/AccountProofCollectorTests.cs +++ b/src/Nethermind/Nethermind.State.Test/Proofs/AccountProofCollectorTests.cs @@ -307,8 +307,8 @@ public void Storage_proofs_have_keys_set() AccountProofCollector accountProofCollector = new(TestItem.AddressA, new[] { Bytes.FromHexString("0x0000000000000000000000000000000000000000000000000000000000000000"), Bytes.FromHexString("0x0000000000000000000000000000000000000000000000000000000000000001") }); tree.Accept(accountProofCollector, tree.RootHash); AccountProof proof = accountProofCollector.BuildResult(); - Assert.That(proof.StorageProofs[0].Key.ToHexString(true), Is.EqualTo("0x0000000000000000000000000000000000000000000000000000000000000000")); - Assert.That(proof.StorageProofs[1].Key.ToHexString(true), Is.EqualTo("0x0000000000000000000000000000000000000000000000000000000000000001")); + Assert.That(proof.StorageProofs![0].Key!, Is.EqualTo("0x0")); + Assert.That(proof.StorageProofs![1].Key!, Is.EqualTo("0x1")); } [Test] @@ -750,7 +750,7 @@ public void Chaotic_test() { byte[] indexBytes = new byte[32]; addressesWithStorage[i].StorageCells[j].Index.ToBigEndian(indexBytes.AsSpan()); - accountProof.StorageProofs[j].Key.ToHexString().Should().Be(indexBytes.ToHexString(), $"{i} {j}"); + accountProof.StorageProofs[j].Key!.Should().Be(indexBytes.ToHexString(true, true), $"{i} {j}"); TrieNode node = new(NodeType.Unknown, accountProof.StorageProofs[j].Proof.Last()); node.ResolveNode(null, TreePath.Empty); diff --git a/src/Nethermind/Nethermind.State.Test/Repositories/ChainLevelInfoRepositoryTests.cs b/src/Nethermind/Nethermind.State.Test/Repositories/ChainLevelInfoRepositoryTests.cs index 23ad781b9528..4ed2515fa5ba 100644 --- a/src/Nethermind/Nethermind.State.Test/Repositories/ChainLevelInfoRepositoryTests.cs +++ b/src/Nethermind/Nethermind.State.Test/Repositories/ChainLevelInfoRepositoryTests.cs @@ -27,7 +27,7 @@ public void TestMultiGet() repository.PersistLevel(10, level10); } - using IOwnedReadOnlyList levels = repository.MultiLoadLevel([1, 10]); + using IOwnedReadOnlyList levels = repository.MultiLoadLevel(new ArrayPoolListRef(2, 1, 10)); levels[0].Should().BeEquivalentTo(level1); levels[1].Should().BeEquivalentTo(level10); } diff --git a/src/Nethermind/Nethermind.State/OverridableEnv/OverridableCodeInfoRepository.cs b/src/Nethermind/Nethermind.State/OverridableEnv/OverridableCodeInfoRepository.cs index e583df015878..d8ac3c455f1d 100644 --- a/src/Nethermind/Nethermind.State/OverridableEnv/OverridableCodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.State/OverridableEnv/OverridableCodeInfoRepository.cs @@ -15,7 +15,7 @@ namespace Nethermind.State.OverridableEnv; public class OverridableCodeInfoRepository(ICodeInfoRepository codeInfoRepository, IWorldState worldState) : IOverridableCodeInfoRepository { - private readonly Dictionary _codeOverrides = new(); + private readonly Dictionary _codeOverrides = new(); private readonly Dictionary _precompileOverrides = new(); public ICodeInfo GetCachedCodeInfo(Address codeSource, bool followDelegation, IReleaseSpec vmSpec, out Address? delegationAddress) @@ -23,9 +23,16 @@ public ICodeInfo GetCachedCodeInfo(Address codeSource, bool followDelegation, IR delegationAddress = null; if (_precompileOverrides.TryGetValue(codeSource, out var precompile)) return precompile.codeInfo; - return _codeOverrides.TryGetValue(codeSource, out ICodeInfo result) - ? result - : codeInfoRepository.GetCachedCodeInfo(codeSource, followDelegation, vmSpec, out delegationAddress); + if (_codeOverrides.TryGetValue(codeSource, out var result)) + { + return !result.codeInfo.IsEmpty && + ICodeInfoRepository.TryGetDelegatedAddress(result.codeInfo.CodeSpan, out delegationAddress) && + followDelegation + ? GetCachedCodeInfo(delegationAddress, false, vmSpec, out Address? _) + : result.codeInfo; + } + + return codeInfoRepository.GetCachedCodeInfo(codeSource, followDelegation, vmSpec, out delegationAddress); } public void InsertCode(ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) => @@ -36,23 +43,31 @@ public void SetCodeOverride( Address key, ICodeInfo value) { - _codeOverrides[key] = value; + _codeOverrides[key] = (value, ValueKeccak.Compute(value.CodeSpan)); } public void MovePrecompile(IReleaseSpec vmSpec, Address precompileAddr, Address targetAddr) { _precompileOverrides[targetAddr] = (this.GetCachedCodeInfo(precompileAddr, vmSpec), precompileAddr); - _codeOverrides[precompileAddr] = new CodeInfo(worldState.GetCode(precompileAddr)); + _codeOverrides[precompileAddr] = (new CodeInfo(worldState.GetCode(precompileAddr)), worldState.GetCodeHash(precompileAddr)); } public void SetDelegation(Address codeSource, Address authority, IReleaseSpec spec) => codeInfoRepository.SetDelegation(codeSource, authority, spec); - public bool TryGetDelegation(Address address, IReleaseSpec vmSpec, [NotNullWhen(true)] out Address? delegatedAddress) => - codeInfoRepository.TryGetDelegation(address, vmSpec, out delegatedAddress); + public bool TryGetDelegation(Address address, IReleaseSpec vmSpec, + [NotNullWhen(true)] out Address? delegatedAddress) + { + delegatedAddress = null; + return _codeOverrides.TryGetValue(address, out var result) + ? ICodeInfoRepository.TryGetDelegatedAddress(result.codeInfo.CodeSpan, out delegatedAddress) + : codeInfoRepository.TryGetDelegation(address, vmSpec, out delegatedAddress); + } + - public ValueHash256 GetExecutableCodeHash(Address address, IReleaseSpec spec) => - codeInfoRepository.GetExecutableCodeHash(address, spec); + public ValueHash256 GetExecutableCodeHash(Address address, IReleaseSpec spec) => _codeOverrides.TryGetValue(address, out var result) + ? result.codeHash + : codeInfoRepository.GetExecutableCodeHash(address, spec); public void ResetOverrides() { diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index 725a4b465f4a..6e01ca39f2f0 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -349,10 +349,10 @@ static void ReportMetrics(int writes, int skipped) /// Current block number public void CommitTrees(IBlockCommitter blockCommitter) { - // Note: These all runs in about 0.4ms. So the little overhead like attempting to sort the tasks - // may make it worst. Always check on mainnet. + // Note: These all run in about 0.4ms. So the little overhead like attempting to sort the tasks + // may make it worse. Always check on mainnet. - using ArrayPoolList commitTask = new ArrayPoolList(_storages.Count); + using ArrayPoolListRef commitTask = new(_storages.Count); foreach (KeyValuePair storage in _storages) { storage.Value.EnsureStorageTree(); // Cannot be called concurrently @@ -656,10 +656,10 @@ private byte[] LoadFromTreeStorage(StorageCell storageCell) } else { - using ArrayPoolList bulkWrite = new(BlockChange.EstimatedSize); + using ArrayPoolListRef bulkWrite = new(BlockChange.EstimatedSize); Span keyBuf = stackalloc byte[32]; - foreach (var kvp in BlockChange) + foreach (KeyValuePair kvp in BlockChange) { byte[] after = kvp.Value.After; if (!Bytes.AreEqual(kvp.Value.Before, after) || kvp.Value.IsInitialValue) diff --git a/src/Nethermind/Nethermind.State/Proofs/AccountProofCollector.cs b/src/Nethermind/Nethermind.State/Proofs/AccountProofCollector.cs index 2a6e21558038..fbe3410dbe75 100644 --- a/src/Nethermind/Nethermind.State/Proofs/AccountProofCollector.cs +++ b/src/Nethermind/Nethermind.State/Proofs/AccountProofCollector.cs @@ -82,7 +82,7 @@ private AccountProofCollector(ReadOnlySpan hashedAddress, IEnumerable public class StorageProof { - public byte[]? Key { get; set; } + public string? Key { get; set; } public byte[][]? Proof { get; set; } [JsonConverter(typeof(ProofStorageValueConverter))] diff --git a/src/Nethermind/Nethermind.State/Repositories/ChainLevelInfoRepository.cs b/src/Nethermind/Nethermind.State/Repositories/ChainLevelInfoRepository.cs index 51ac2184c3c3..d221fe2c009c 100644 --- a/src/Nethermind/Nethermind.State/Repositories/ChainLevelInfoRepository.cs +++ b/src/Nethermind/Nethermind.State/Repositories/ChainLevelInfoRepository.cs @@ -79,7 +79,7 @@ void LocalPersistLevel() public ChainLevelInfo? LoadLevel(long number) => _blockInfoDb.Get(number, Rlp.GetStreamDecoder(), _blockInfoCache); - public IOwnedReadOnlyList MultiLoadLevel(IReadOnlyList blockNumbers) + public IOwnedReadOnlyList MultiLoadLevel(in ArrayPoolListRef blockNumbers) { byte[][] keys = new byte[blockNumbers.Count][]; for (var i = 0; i < blockNumbers.Count; i++) @@ -89,10 +89,10 @@ void LocalPersistLevel() KeyValuePair[] data = _blockInfoDb[keys]; - return data.Select((kv) => + return data.Select(kv => { if (kv.Value == null || kv.Value.Length == 0) return null; - var rlpValueContext = kv.Value.AsRlpValueContext(); + Rlp.ValueDecoderContext rlpValueContext = kv.Value.AsRlpValueContext(); return _decoder.Decode(ref rlpValueContext, RlpBehaviors.AllowExtraBytes); }) .ToPooledList(data.Length); diff --git a/src/Nethermind/Nethermind.State/Repositories/IChainLevelInfoRepository.cs b/src/Nethermind/Nethermind.State/Repositories/IChainLevelInfoRepository.cs index b2f28ce55e32..fbe63c1a2691 100644 --- a/src/Nethermind/Nethermind.State/Repositories/IChainLevelInfoRepository.cs +++ b/src/Nethermind/Nethermind.State/Repositories/IChainLevelInfoRepository.cs @@ -13,6 +13,6 @@ public interface IChainLevelInfoRepository void PersistLevel(long number, ChainLevelInfo level, BatchWrite? batch = null); BatchWrite StartBatch(); ChainLevelInfo? LoadLevel(long number); - IOwnedReadOnlyList MultiLoadLevel(IReadOnlyList blockNumbers); + IOwnedReadOnlyList MultiLoadLevel(in ArrayPoolListRef blockNumbers); } } diff --git a/src/Nethermind/Nethermind.State/SnapServer/AccountCollector.cs b/src/Nethermind/Nethermind.State/SnapServer/AccountCollector.cs index fcb495fdc5c2..fc749f8b29c3 100644 --- a/src/Nethermind/Nethermind.State/SnapServer/AccountCollector.cs +++ b/src/Nethermind/Nethermind.State/SnapServer/AccountCollector.cs @@ -23,7 +23,7 @@ public int Collect(in ValueHash256 path, SpanSource value) return 32 + 1; } - Rlp.ValueDecoderContext ctx = new Rlp.ValueDecoderContext(value.Span); + Rlp.ValueDecoderContext ctx = new(value.Span); Account accnt = AccountDecoder.Instance.Decode(ref ctx); Accounts.Add(new PathWithAccount(path, accnt)); return 32 + AccountDecoder.Slim.GetLength(accnt); diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 9664284e4ad6..5abb93111b21 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -749,10 +749,10 @@ private void FlushToTree() int writes = 0; int skipped = 0; - using ArrayPoolList bulkWrite = new(_blockChanges.Count); - foreach (var key in _blockChanges.Keys) + using ArrayPoolListRef bulkWrite = new(_blockChanges.Count); + foreach (AddressAsKey key in _blockChanges.Keys) { - ref var change = ref CollectionsMarshal.GetValueRefOrNullRef(_blockChanges, key); + ref ChangeTrace change = ref CollectionsMarshal.GetValueRefOrNullRef(_blockChanges, key); if (change.Before != change.After) { change.Before = change.After; diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs index b122394b7069..4b2d33f62f8a 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs @@ -777,6 +777,14 @@ public void GetNodeData_returns_cached_trie_nodes() ctx.SyncServer.GetNodeData(new[] { nodeKey }, CancellationToken.None, NodeDataType.All).Should().BeEquivalentTo(new[] { TestItem.KeccakB.BytesToArray() }); } + [Test] + public void Correctly_clips_lowestBlock() + { + Context ctx = new(); + ctx.BlockTree.GetLowestBlock().Returns(5); + ctx.SyncServer.LowestBlock.Should().Be(0); + } + private class Context { public Context() diff --git a/src/Nethermind/Nethermind.Synchronization/Blocks/BlockDownloader.cs b/src/Nethermind/Nethermind.Synchronization/Blocks/BlockDownloader.cs index 07da3a289046..1cd795a36f79 100644 --- a/src/Nethermind/Nethermind.Synchronization/Blocks/BlockDownloader.cs +++ b/src/Nethermind/Nethermind.Synchronization/Blocks/BlockDownloader.cs @@ -162,6 +162,8 @@ private void BlockTreeOnNewHeadBlock(object? sender, BlockEventArgs e) if (cancellation.IsCancellationRequested) return null; // check before every heavy operation if (headers is null || headers.Count <= 1) return null; + if (_logger.IsTrace) _logger.Trace($"Prepared request from block {headers[0].Number} to {headers[^1].Number}"); + if (previousStartingHeaderNumber == headers[0].Number) { // When the block is suggested right between a `NewPayload` and `ForkChoiceUpdatedHandler` the block is not added because it was added already @@ -381,7 +383,7 @@ public SyncResponseHandlingResult HandleResponse(BlocksRequest response, PeerInf response.OwnedBodies?.Disown(); SyncResponseHandlingResult result = SyncResponseHandlingResult.OK; - using ArrayPoolList blocks = new ArrayPoolList(response.BodiesRequests?.Count ?? 0); + using ArrayPoolListRef blocks = new(response.BodiesRequests?.Count ?? 0); int bodiesCount = 0; int receiptsCount = 0; diff --git a/src/Nethermind/Nethermind.Synchronization/ISyncServer.cs b/src/Nethermind/Nethermind.Synchronization/ISyncServer.cs index 3ee241c79eac..72f5d6295fea 100644 --- a/src/Nethermind/Nethermind.Synchronization/ISyncServer.cs +++ b/src/Nethermind/Nethermind.Synchronization/ISyncServer.cs @@ -26,5 +26,6 @@ public interface ISyncServer : IDisposable ulong NetworkId { get; } BlockHeader Genesis { get; } BlockHeader? Head { get; } + long LowestBlock { get; } } } diff --git a/src/Nethermind/Nethermind.Synchronization/ParallelSync/MultiSyncModeSelector.cs b/src/Nethermind/Nethermind.Synchronization/ParallelSync/MultiSyncModeSelector.cs index 6c5683ef153b..dd9a6d3ee210 100644 --- a/src/Nethermind/Nethermind.Synchronization/ParallelSync/MultiSyncModeSelector.cs +++ b/src/Nethermind/Nethermind.Synchronization/ParallelSync/MultiSyncModeSelector.cs @@ -420,8 +420,7 @@ private bool ShouldBeInFastSyncMode(Snapshot best) (nameof(postPivotPeerAvailable), postPivotPeerAvailable), (nameof(notReachedFullSyncTransition), notReachedFullSyncTransition), (nameof(notInAStickyFullSync), notInAStickyFullSync), - (nameof(stateNotDownloadedYet), stateNotDownloadedYet), - (nameof(longRangeCatchUp), longRangeCatchUp), + ($"{nameof(stateNotDownloadedYet)}||${longRangeCatchUp}", stateNotDownloadedYet || longRangeCatchUp), (nameof(notNeedToWaitForHeaders), notNeedToWaitForHeaders)); } @@ -608,8 +607,7 @@ private bool ShouldBeInStateSyncMode(Snapshot best) (nameof(hasFastSyncBeenActive), hasFastSyncBeenActive), (nameof(hasAnyPostPivotPeer), hasAnyPostPivotPeer), ($"{nameof(notInFastSync)}||{nameof(stickyStateNodes)}", notInFastSync || stickyStateNodes), - (nameof(stateNotDownloadedYet), stateNotDownloadedYet), - (nameof(longRangeCatchUp), longRangeCatchUp), + ($"{nameof(stateNotDownloadedYet)}||{nameof(longRangeCatchUp)}", stateNotDownloadedYet || longRangeCatchUp), (nameof(notInAStickyFullSync), notInAStickyFullSync), (nameof(notNeedToWaitForHeaders), notNeedToWaitForHeaders)); } diff --git a/src/Nethermind/Nethermind.Synchronization/Reporting/SyncReport.cs b/src/Nethermind/Nethermind.Synchronization/Reporting/SyncReport.cs index 871232804e6c..17c71464dc03 100644 --- a/src/Nethermind/Nethermind.Synchronization/Reporting/SyncReport.cs +++ b/src/Nethermind/Nethermind.Synchronization/Reporting/SyncReport.cs @@ -28,7 +28,7 @@ public class SyncReport : ISyncReport private const int NoProgressStateSyncReportFrequency = 30; private const int SyncAllocatedPeersReportFrequency = 30; private const int SyncFullPeersReportFrequency = 120; - private static readonly TimeSpan _defaultReportingIntervals = TimeSpan.FromSeconds(10); + private static readonly TimeSpan _defaultReportingIntervals = TimeSpan.FromSeconds(1); public SyncReport(ISyncPeerPool syncPeerPool, INodeStatsManager nodeStatsManager, ISyncConfig syncConfig, IPivot pivot, ILogManager logManager, ITimerFactory? timerFactory = null, double tickTime = 1000) { diff --git a/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProviderHelper.cs b/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProviderHelper.cs index f72011435e22..cd30e4a66495 100644 --- a/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProviderHelper.cs +++ b/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProviderHelper.cs @@ -57,7 +57,7 @@ public static (AddRangeResult result, bool moreChildrenToRight, List codeHashes = new(); bool hasExtraStorage = false; - using ArrayPoolList entries = new ArrayPoolList(accounts.Count); + using ArrayPoolListRef entries = new(accounts.Count); for (var index = 0; index < accounts.Count; index++) { PathWithAccount account = accounts[index]; @@ -156,7 +156,7 @@ public static (AddRangeResult result, bool moreChildrenToRight) AddStorageRange( return (result, true); } - using ArrayPoolList entries = new ArrayPoolList(slots.Count); + using ArrayPoolListRef entries = new(slots.Count); for (var index = 0; index < slots.Count; index++) { PathWithStorageSlot slot = slots[index]; @@ -172,9 +172,9 @@ public static (AddRangeResult result, bool moreChildrenToRight) AddStorageRange( return (AddRangeResult.DifferentRootHash, true); } - // This will work if all StorageRange requests share the same AccountWithPath object which seems to be the case. - // If this is not true, StorageRange request should be extended with a lock object. - // That lock object should be shared between all other StorageRange requests for same account. + // This will work if all StorageRange requests share the same AccountWithPath object, which seems to be the case. + // If this is not true, the StorageRange request should be extended with a lock object. + // That lock object should be shared between all other StorageRange requests for the same account. lock (account.Account) { StitchBoundaries(sortedBoundaryList, tree.TrieStore); diff --git a/src/Nethermind/Nethermind.Synchronization/SyncServer.cs b/src/Nethermind/Nethermind.Synchronization/SyncServer.cs index 689a09b53e7c..7a006f1965a8 100644 --- a/src/Nethermind/Nethermind.Synchronization/SyncServer.cs +++ b/src/Nethermind/Nethermind.Synchronization/SyncServer.cs @@ -124,6 +124,8 @@ public BlockHeader? Head } } + public long LowestBlock => Math.Min(Head?.Number ?? 0, _blockTree.GetLowestBlock()); + public int GetPeerCount() => _pool.PeerCount; private readonly Guid _sealValidatorUserGuid = Guid.NewGuid(); diff --git a/src/Nethermind/Nethermind.Taiko.Test/TaikoExtendedEthModuleTests.cs b/src/Nethermind/Nethermind.Taiko.Test/TaikoExtendedEthModuleTests.cs index 7d73311c7355..291276de31da 100644 --- a/src/Nethermind/Nethermind.Taiko.Test/TaikoExtendedEthModuleTests.cs +++ b/src/Nethermind/Nethermind.Taiko.Test/TaikoExtendedEthModuleTests.cs @@ -78,4 +78,37 @@ public void TestL1OriginById_WithBuildPayloadArgsId() rpc.taiko_l1OriginByID(0).Result.Data.Should().Be(origin); } + + [Test] + public void TestL1OriginById_ValueHash256_EvenLengthHex() + { + IL1OriginStore originStore = Substitute.For(); + TaikoExtendedEthModule rpc = new TaikoExtendedEthModule(new SyncConfig(), originStore); + int expectedLengthInChars = ValueHash256.Length * 2 + 2; + + // Create odd length hash values + var l2BlockHash = new ValueHash256("0x35a48c5b3ee5b1b2a365fcd1aa68c738d1c06474578087a78fa79dd45de6214"); + var l1BlockHash = new ValueHash256("0x2daf7e4b06ca2d3a82c775d9e9ad0c973545a608684146cda0df5f7d71188a5"); + + L1Origin origin = new L1Origin(0, l2BlockHash, 1, l1BlockHash, null); + originStore.ReadL1Origin((UInt256)0).Returns(origin); + + var result = rpc.taiko_l1OriginByID(0).Result.Data; + result.Should().Be(origin); + + // Serialize the RPC result and verify hash values have even-length hex string + var serializer = new Serialization.Json.EthereumJsonSerializer(); + var json = serializer.Serialize(result); + + // Parse the JSON to extract the hash values + var jsonDoc = System.Text.Json.JsonDocument.Parse(json); + var l2BlockHashString = jsonDoc.RootElement.GetProperty("l2BlockHash").GetString(); + var l1BlockHashString = jsonDoc.RootElement.GetProperty("l1BlockHash").GetString(); + + l2BlockHashString.Should().NotBeNull(); + l2BlockHashString!.Length.Should().Be(expectedLengthInChars); + + l1BlockHashString.Should().NotBeNull(); + l1BlockHashString!.Length.Should().Be(expectedLengthInChars); + } } diff --git a/src/Nethermind/Nethermind.Taiko/BlockTransactionExecutors/BlockInvalidTxExecutor.cs b/src/Nethermind/Nethermind.Taiko/BlockTransactionExecutors/BlockInvalidTxExecutor.cs index ec2bb591edcb..a5c00d14471f 100644 --- a/src/Nethermind/Nethermind.Taiko/BlockTransactionExecutors/BlockInvalidTxExecutor.cs +++ b/src/Nethermind/Nethermind.Taiko/BlockTransactionExecutors/BlockInvalidTxExecutor.cs @@ -32,7 +32,7 @@ public TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processing block.Transactions[0].IsAnchorTx = true; - using ArrayPoolList correctTransactions = new(block.Transactions.Length); + using ArrayPoolListRef correctTransactions = new(block.Transactions.Length); for (int i = 0; i < block.Transactions.Length; i++) { diff --git a/src/Nethermind/Nethermind.Taiko/L1Origin.cs b/src/Nethermind/Nethermind.Taiko/L1Origin.cs index c145af481bac..4f9a64aba201 100644 --- a/src/Nethermind/Nethermind.Taiko/L1Origin.cs +++ b/src/Nethermind/Nethermind.Taiko/L1Origin.cs @@ -6,18 +6,18 @@ namespace Nethermind.Taiko; -public class L1Origin(UInt256 blockId, ValueHash256? l2BlockHash, long l1BlockHeight, Hash256 l1BlockHash, int[]? buildPayloadArgsId) +public class L1Origin(UInt256 blockId, ValueHash256? l2BlockHash, long l1BlockHeight, ValueHash256 l1BlockHash, int[]? buildPayloadArgsId) { public UInt256 BlockId { get; set; } = blockId; public ValueHash256? L2BlockHash { get; set; } = l2BlockHash; public long L1BlockHeight { get; set; } = l1BlockHeight; - public Hash256 L1BlockHash { get; set; } = l1BlockHash; + public ValueHash256 L1BlockHash { get; set; } = l1BlockHash; // Taiko uses int-like serializer public int[]? BuildPayloadArgsId { get; set; } = buildPayloadArgsId; /// /// IsPreconfBlock returns true if the L1Origin is for a preconfirmation block. - /// + /// public bool IsPreconfBlock => L1BlockHeight == 0; } diff --git a/src/Nethermind/Nethermind.Taiko/L1OriginStore.cs b/src/Nethermind/Nethermind.Taiko/L1OriginStore.cs index 2ab8778f43f1..8d0c7cada915 100644 --- a/src/Nethermind/Nethermind.Taiko/L1OriginStore.cs +++ b/src/Nethermind/Nethermind.Taiko/L1OriginStore.cs @@ -23,7 +23,7 @@ public class L1OriginStore([KeyFilter(L1OriginStore.L1OriginDbName)] IDb db, IRl Span keyBytes = stackalloc byte[UInt256BytesLength]; blockId.ToBigEndian(keyBytes); - return db.Get(new ValueHash256(keyBytes), decoder); + return db.Get(new Hash256(keyBytes), decoder); } public void WriteL1Origin(UInt256 blockId, L1Origin l1Origin) diff --git a/src/Nethermind/Nethermind.Taiko/Rpc/TaikoEngineRpcModule.cs b/src/Nethermind/Nethermind.Taiko/Rpc/TaikoEngineRpcModule.cs index d50e5657eca2..9863c81ab540 100644 --- a/src/Nethermind/Nethermind.Taiko/Rpc/TaikoEngineRpcModule.cs +++ b/src/Nethermind/Nethermind.Taiko/Rpc/TaikoEngineRpcModule.cs @@ -258,7 +258,7 @@ struct Batch(ulong maxBytes, int transactionsListCapacity, IRlpStreamDecoder Transactions { get; } = new ArrayPoolList(transactionsListCapacity); + public ArrayPoolList Transactions { get; } = new(transactionsListCapacity); public bool TryAddTx(Transaction tx) { diff --git a/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs b/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs index f5267e2b0d22..ded8904a96ce 100644 --- a/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs +++ b/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using FluentAssertions; using Nethermind.Core; @@ -601,6 +602,17 @@ public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteF { _inBatched[key.ToArray()] = value; } + + public void Merge(ReadOnlySpan key, ReadOnlySpan value, WriteFlags flags = WriteFlags.None) + { + var keyArr = key.ToArray(); + _inBatched[keyArr] = (_inBatched.GetValueOrDefault(keyArr) ?? []).Concat(value.ToArray()).ToArray(); + } + + public void Clear() + { + _inBatched.Clear(); + } } } diff --git a/src/Nethermind/Nethermind.Trie.Test/VisitingTests.cs b/src/Nethermind/Nethermind.Trie.Test/VisitingTests.cs index 69e9ae819878..01b562104123 100644 --- a/src/Nethermind/Nethermind.Trie.Test/VisitingTests.cs +++ b/src/Nethermind/Nethermind.Trie.Test/VisitingTests.cs @@ -23,11 +23,11 @@ namespace Nethermind.Trie.Test; public class VisitingTests { [TestCaseSource(nameof(GetOptions))] - public void Visitors_state(VisitingOptions options) + public void Visitors_state(VisitingOptions options, INodeStorage.KeyScheme scheme) { MemDb memDb = new(); - using TrieStore trieStore = TestTrieStoreFactory.Build(memDb, Prune.WhenCacheReaches(1.MB()), Persist.EveryBlock, LimboLogs.Instance); + using ITrieStore trieStore = TestTrieStoreFactory.Build(new NodeStorage(memDb, scheme), LimboLogs.Instance); PatriciaTree patriciaTree = new(trieStore, LimboLogs.Instance); Span raw = stackalloc byte[32]; @@ -62,11 +62,11 @@ public void Visitors_state(VisitingOptions options) } [TestCaseSource(nameof(GetOptions))] - public void Visitors_storage(VisitingOptions options) + public void Visitors_storage(VisitingOptions options, INodeStorage.KeyScheme scheme) { MemDb memDb = new(); - using TrieStore trieStore = TestTrieStoreFactory.Build(memDb, Prune.WhenCacheReaches(1.MB()), Persist.EveryBlock, LimboLogs.Instance); + using ITrieStore trieStore = TestTrieStoreFactory.Build(new NodeStorage(memDb, scheme), LimboLogs.Instance); byte[] value = Enumerable.Range(1, 32).Select(static i => (byte)i).ToArray(); Hash256 stateRootHash = Keccak.Zero; @@ -109,8 +109,11 @@ public void Visitors_storage(VisitingOptions options) stateTree.Accept(visitor, stateTree.RootHash, options); + int totalPath = 0; + foreach (var path in visitor.LeafPaths) { + totalPath++; if (path.Length == 64) { AssertPath(path); @@ -127,6 +130,8 @@ public void Visitors_storage(VisitingOptions options) } } + totalPath.Should().Be(4160); + return; static void AssertPath(ReadOnlySpan path) @@ -142,13 +147,23 @@ private static IEnumerable GetOptions() { yield return new TestCaseData(new VisitingOptions { - }).SetName("Default"); + }, INodeStorage.KeyScheme.HalfPath).SetName("Default"); + + yield return new TestCaseData(new VisitingOptions + { + }, INodeStorage.KeyScheme.Hash).SetName("Default Hash"); + + yield return new TestCaseData(new VisitingOptions + { + MaxDegreeOfParallelism = Environment.ProcessorCount, + FullScanMemoryBudget = 1.MiB(), + }, INodeStorage.KeyScheme.HalfPath).SetName("Parallel"); yield return new TestCaseData(new VisitingOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, FullScanMemoryBudget = 1.MiB(), - }).SetName("Parallel"); + }, INodeStorage.KeyScheme.Hash).SetName("Parallel Hash"); } public class AppendingVisitor(bool expectAccount) : ITreeVisitor diff --git a/src/Nethermind/Nethermind.Trie/BatchedTrieVisitor.cs b/src/Nethermind/Nethermind.Trie/BatchedTrieVisitor.cs index 45e9c9607d76..83b496552b3b 100644 --- a/src/Nethermind/Nethermind.Trie/BatchedTrieVisitor.cs +++ b/src/Nethermind/Nethermind.Trie/BatchedTrieVisitor.cs @@ -146,9 +146,9 @@ public void Start( try { - using ArrayPoolList tasks = Enumerable.Range(0, trieVisitContext.MaxDegreeOfParallelism) + using ArrayPoolListRef tasks = Enumerable.Range(0, trieVisitContext.MaxDegreeOfParallelism) .Select(_ => Task.Run(BatchedThread)) - .ToPooledList(trieVisitContext.MaxDegreeOfParallelism); + .ToPooledListRef(trieVisitContext.MaxDegreeOfParallelism); Task.WaitAll(tasks.AsSpan()); } @@ -169,7 +169,7 @@ public void Start( { Interlocked.Add(ref _currentPointer, -_partitionCount); - GC.Collect(); // Simulate GC collect of standard visitor + GC.Collect(); // Simulate GC collect of a standard visitor } partitionIdx %= _partitionCount; @@ -256,7 +256,7 @@ public void Start( } - void QueueNextNodes(ArrayPoolList<(TrieNode, TNodeContext, SmallTrieVisitContext)> batchResult) + void QueueNextNodes(ref ArrayPoolListRef<(TrieNode, TNodeContext, SmallTrieVisitContext)> batchResult) { // Reverse order is important so that higher level appear at the end of the stack. TreePath emptyPath = TreePath.Empty; @@ -267,11 +267,19 @@ void QueueNextNodes(ArrayPoolList<(TrieNode, TNodeContext, SmallTrieVisitContext { // Inline node. Seems rare, so its fine to create new list for this. Does not have a keccak // to queue, so we'll just process it inline. - using ArrayPoolList<(TrieNode, TNodeContext, SmallTrieVisitContext)> recursiveResult = new(1); - trieNode.ResolveNode(_resolver, emptyPath); - Interlocked.Increment(ref _activeJobs); - AcceptResolvedNode(trieNode, nodeContext, _resolver, ctx, recursiveResult); - QueueNextNodes(recursiveResult); + ArrayPoolListRef<(TrieNode, TNodeContext, SmallTrieVisitContext)> recursiveResult = new(1); + try + { + trieNode.ResolveNode(_resolver, emptyPath); + Interlocked.Increment(ref _activeJobs); + AcceptResolvedNode(trieNode, nodeContext, _resolver, ctx, ref recursiveResult); + QueueNextNodes(ref recursiveResult); + } + finally + { + recursiveResult.Dispose(); + } + continue; } @@ -290,87 +298,93 @@ void QueueNextNodes(ArrayPoolList<(TrieNode, TNodeContext, SmallTrieVisitContext Interlocked.Decrement(ref _activeJobs); } - private void BatchedThread() { - using ArrayPoolList<(TrieNode, TNodeContext, SmallTrieVisitContext)> nextToProcesses = new(_maxBatchSize); - using ArrayPoolList resolveOrdering = new(_maxBatchSize); - ArrayPoolList<(TrieNode, TNodeContext, SmallTrieVisitContext)>? currentBatch; - TreePath emptyPath = TreePath.Empty; - while ((currentBatch = GetNextBatch()) is not null) + ArrayPoolListRef<(TrieNode, TNodeContext, SmallTrieVisitContext)> nextToProcesses = new(_maxBatchSize); + try { - // Storing the idx separately as the ordering is important to reduce memory (approximate dfs ordering) - // but the path ordering is important for read amplification - resolveOrdering.Clear(); - for (int i = 0; i < currentBatch.Count; i++) + using ArrayPoolListRef resolveOrdering = new(_maxBatchSize); + TreePath emptyPath = TreePath.Empty; + while (GetNextBatch() is { } currentBatch) { - (TrieNode? cur, TNodeContext _, SmallTrieVisitContext ctx) = currentBatch[i]; - - cur.ResolveKey(_resolver, ref emptyPath); - - if (cur.FullRlp.IsNotNull) continue; - if (cur.Keccak is null) - ThrowUnableToResolve(ctx); + // Storing the idx separately as the ordering is important to reduce memory (approximate dfs ordering) + // but the path ordering is important for read amplification + resolveOrdering.Clear(); + for (int i = 0; i < currentBatch.Count; i++) + { + (TrieNode? cur, TNodeContext _, SmallTrieVisitContext ctx) = currentBatch[i]; - resolveOrdering.Add(i); - } + cur.ResolveKey(_resolver, ref emptyPath); - // This innocent looking sort is surprisingly effective when batch size is large enough. The sort itself - // take about 0.1% of the time, so not very cpu intensive in this case. - resolveOrdering - .AsSpan() - .Sort((item1, item2) => currentBatch[item1].Item1.Keccak.CompareTo(currentBatch[item2].Item1.Keccak)); + if (cur.FullRlp.IsNotNull) continue; + if (cur.Keccak is null) + ThrowUnableToResolve(ctx); - ReadFlags flags = ReadFlags.None; - if (resolveOrdering.Count > _readAheadThreshold) - { - flags = ReadFlags.HintReadAhead; - } + resolveOrdering.Add(i); + } - // This loop is about 60 to 70% of the time spent. If you set very high memory budget, this drop to about 50MB. - for (int i = 0; i < resolveOrdering.Count; i++) - { - int idx = resolveOrdering[i]; + // This innocent looking sort is surprisingly effective when batch size is large enough. The sort itself + // take about 0.1% of the time, so not very cpu intensive in this case. + resolveOrdering + .AsSpan() + .Sort((item1, item2) => + currentBatch[item1].Item1.Keccak.CompareTo(currentBatch[item2].Item1.Keccak)); - (TrieNode nodeToResolve, TNodeContext nodeContext, SmallTrieVisitContext ctx) = currentBatch[idx]; - try + ReadFlags flags = ReadFlags.None; + if (resolveOrdering.Count > _readAheadThreshold) { - Hash256 theKeccak = nodeToResolve.Keccak; - nodeToResolve.ResolveNode(_resolver, emptyPath, flags); - nodeToResolve.Keccak = theKeccak; // The resolve may set a key which clear the keccak + flags = ReadFlags.HintReadAhead; } - catch (TrieException) + + // This loop is about 60 to 70% of the time spent. If you set very high memory budget, this drop to about 50MB. + for (int i = 0; i < resolveOrdering.Count; i++) { - _visitor.VisitMissingNode(nodeContext, nodeToResolve.Keccak); - } - } + int idx = resolveOrdering[i]; - // Going in reverse to reduce memory - for (int i = currentBatch.Count - 1; i >= 0; i--) - { - (TrieNode nodeToResolve, TNodeContext nodeContext, SmallTrieVisitContext ctx) = currentBatch[i]; + (TrieNode nodeToResolve, TNodeContext nodeContext, SmallTrieVisitContext ctx) = currentBatch[idx]; + try + { + Hash256 theKeccak = nodeToResolve.Keccak; + nodeToResolve.ResolveNode(_resolver, emptyPath, flags); + nodeToResolve.Keccak = theKeccak; // The resolve may set a key which clear the keccak + } + catch (TrieException) + { + _visitor.VisitMissingNode(nodeContext, nodeToResolve.Keccak); + } + } - nextToProcesses.Clear(); - if (nodeToResolve.FullRlp.IsNull) + // Going in reverse to reduce memory + for (int i = currentBatch.Count - 1; i >= 0; i--) { - // Still need to decrement counter - QueueNextNodes(nextToProcesses); - return; // missing node + (TrieNode nodeToResolve, TNodeContext nodeContext, SmallTrieVisitContext ctx) = currentBatch[i]; + + nextToProcesses.Clear(); + if (nodeToResolve.FullRlp.IsNull) + { + // Still need to decrement counter + QueueNextNodes(ref nextToProcesses); + return; // missing node + } + + AcceptResolvedNode(nodeToResolve, nodeContext, _resolver, ctx, ref nextToProcesses); + QueueNextNodes(ref nextToProcesses); } - AcceptResolvedNode(nodeToResolve, nodeContext, _resolver, ctx, nextToProcesses); - QueueNextNodes(nextToProcesses); + currentBatch.Dispose(); } - - currentBatch.Dispose(); + } + finally + { + nextToProcesses.Dispose(); } return; - static void ThrowUnableToResolve(in SmallTrieVisitContext ctx) + void ThrowUnableToResolve(in SmallTrieVisitContext ctx) { throw new TrieException( - $"Unable to resolve node without Keccak. ctx: {ctx.Level}, {ctx.ExpectAccounts}, {ctx.IsStorage}"); + $"Unable to resolve node without Keccak. ctx: {ctx.Level}, {_visitor.ExpectAccounts}, {ctx.IsStorage}"); } } @@ -378,7 +392,7 @@ static void ThrowUnableToResolve(in SmallTrieVisitContext ctx) /// Like `Accept`, but does not execute its children. Instead it return the next trie to visit in the list /// `nextToVisit`. Also, it assume the node is already resolved. /// - internal void AcceptResolvedNode(TrieNode node, in TNodeContext nodeContext, ITrieNodeResolver nodeResolver, SmallTrieVisitContext trieVisitContext, IList<(TrieNode, TNodeContext, SmallTrieVisitContext)> nextToVisit) + internal void AcceptResolvedNode(TrieNode node, in TNodeContext nodeContext, ITrieNodeResolver nodeResolver, SmallTrieVisitContext trieVisitContext, ref ArrayPoolListRef<(TrieNode, TNodeContext, SmallTrieVisitContext)> nextToVisit) { // Note: The path is not maintained here, its just for a placeholder. This code is only used for BatchedTrieVisitor // which should only be used with hash keys. @@ -422,8 +436,6 @@ internal void AcceptResolvedNode(TrieNode node, in TNodeContext nodeContext, ITr if (_visitor.ShouldVisit(childContext, child.Keccak!)) { trieVisitContext.Level++; - - nextToVisit.Add((child, childContext, trieVisitContext)); } @@ -434,11 +446,11 @@ internal void AcceptResolvedNode(TrieNode node, in TNodeContext nodeContext, ITr { _visitor.VisitLeaf(nodeContext, node); - if (!trieVisitContext.IsStorage && trieVisitContext.ExpectAccounts) // can combine these conditions + if (!trieVisitContext.IsStorage && _visitor.ExpectAccounts) // can combine these conditions { TNodeContext childContext = nodeContext.Add(node.Key!); - Rlp.ValueDecoderContext decoderContext = new Rlp.ValueDecoderContext(node.Value.Span); + Rlp.ValueDecoderContext decoderContext = new(node.Value.Span); if (!_accountDecoder.TryDecodeStruct(ref decoderContext, out AccountStruct account)) { throw new InvalidDataException("Non storage leaf should be an account"); @@ -473,18 +485,11 @@ internal void AcceptResolvedNode(TrieNode node, in TNodeContext nodeContext, ITr } [StructLayout(LayoutKind.Sequential, Pack = 1)] - private readonly struct Job + private readonly struct Job(ValueHash256 key, TNodeContext nodeContext, SmallTrieVisitContext context) { - public readonly ValueHash256 Key; - public readonly TNodeContext NodeContext; - public readonly SmallTrieVisitContext Context; - - public Job(ValueHash256 key, TNodeContext nodeContext, SmallTrieVisitContext context) - { - Key = key; - NodeContext = nodeContext; - Context = context; - } + public readonly ValueHash256 Key = key; + public readonly TNodeContext NodeContext = nodeContext; + public readonly SmallTrieVisitContext Context = context; } } diff --git a/src/Nethermind/Nethermind.Trie/PatriciaTree.BulkSet.cs b/src/Nethermind/Nethermind.Trie/PatriciaTree.BulkSet.cs index 60da45d3a357..f0e496c12376 100644 --- a/src/Nethermind/Nethermind.Trie/PatriciaTree.BulkSet.cs +++ b/src/Nethermind/Nethermind.Trie/PatriciaTree.BulkSet.cs @@ -62,13 +62,13 @@ public byte GetPathNibbble(int index) /// /// /// - public void BulkSet(ArrayPoolList entries, Flags flags = Flags.None) + public void BulkSet(in ArrayPoolListRef entries, Flags flags = Flags.None) { if (entries.Count == 0) return; - using ArrayPoolList sortBuffer = new ArrayPoolList(entries.Count, entries.Count); + using ArrayPoolListRef sortBuffer = new(entries.Count, entries.Count); - Context ctx = new Context() + Context ctx = new() { originalSortBufferArray = sortBuffer.UnsafeGetInternalArray(), originalEntriesArray = entries.UnsafeGetInternalArray(), @@ -166,8 +166,7 @@ private struct Context int nonNullChildCount = 0; if (entries.Length >= MinEntriesToParallelizeThreshold && nibMask == FullBranch && !flags.HasFlag(Flags.DoNotParallelize)) { - (int startIdx, int count, int nibble, TreePath appendedPath, TrieNode? currentChild, TrieNode? newChild)[] jobs = - new (int startIdx, int count, int nibble, TreePath appendedPath, TrieNode? currentChild, TrieNode? newChild)[TrieNode.BranchesCount]; + var jobs = new (int startIdx, int count, int nibble, TreePath appendedPath, TrieNode? currentChild, TrieNode? newChild)[TrieNode.BranchesCount]; Context closureCtx = ctx; BulkSetEntry[] originalEntriesArray = (flipCount % 2 == 0) ? ctx.originalEntriesArray : ctx.originalSortBufferArray; @@ -262,8 +261,7 @@ private struct Context return node; } - private TrieNode? BulkSetOne(Stack traverseStack, in BulkSetEntry entry, ref TreePath path, - TrieNode? node) + private TrieNode? BulkSetOne(Stack traverseStack, in BulkSetEntry entry, ref TreePath path, TrieNode? node) { Span nibble = stackalloc byte[64]; Nibbles.BytesToNibbleBytes(entry.Path.BytesAsSpan, nibble); diff --git a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs index 1e14a0875b53..f2b46f75d619 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs @@ -591,17 +591,16 @@ private void SaveSnapshot() int count = _commitSetQueue?.Count ?? 0; if (count == 0) return; - using ArrayPoolList candidateSets = DetermineCommitSetToPersistInSnapshot(count); + using ArrayPoolListRef candidateSets = DetermineCommitSetToPersistInSnapshot(count); bool shouldTrackPastKey = // Its disabled _pastKeyTrackingEnabled && // Full pruning need to visit all node, so can't delete anything. - (_deleteOldNodes - // If more than one candidate set, its a reorg, we can't remove node as persisted node may not be canonical - ? candidateSets.Count == 1 - // For archice node, it is safe to remove canon key from cache as it will just get re-loaded. - : true); + + // If more than one candidate set, its a reorg, we can't remove node as persisted node may not be canonical + // For archice node, it is safe to remove canon key from cache as it will just get re-loaded. + (!_deleteOldNodes || candidateSets.Count == 1); if (shouldTrackPastKey) { @@ -634,9 +633,9 @@ private void SaveSnapshot() if (_logger.IsDebug) _logger.Debug($"Found no candidate for elevated pruning (sets: {_commitSetQueue.Count}, earliest: {uselessFrontSet?.BlockNumber}, newest kept: {LatestCommittedBlockNumber}, reorg depth {_maxDepth})"); } - private ArrayPoolList DetermineCommitSetToPersistInSnapshot(int count) + private ArrayPoolListRef DetermineCommitSetToPersistInSnapshot(int count) { - ArrayPoolList? candidateSets = null; + ArrayPoolListRef candidateSets = new(count); try { @@ -650,8 +649,7 @@ private ArrayPoolList DetermineCommitSetToPersistInSnapshot(int } } - using ArrayPoolList toAddBack = new(count); - candidateSets = new(count); + using ArrayPoolListRef toAddBack = new(count); while (_commitSetQueue.TryDequeue(out BlockCommitSet frontSet)) { if (frontSet.BlockNumber == lastBlockBeforeRorgBoundary || (_persistenceStrategy.ShouldPersist(frontSet.BlockNumber) && frontSet.BlockNumber < lastBlockBeforeRorgBoundary)) @@ -673,7 +671,7 @@ private ArrayPoolList DetermineCommitSetToPersistInSnapshot(int } catch { - candidateSets?.Dispose(); + candidateSets.Dispose(); throw; } } @@ -773,7 +771,7 @@ private void PrunePersistedNodes() if (_logger.IsWarn) _logger.Debug($"Pruning persisted nodes {PersistedMemoryUsedByDirtyCache / 1.MB()} MB, Pruning {shardCountToPrune} shards starting from shard {_lastPrunedShardIdx}"); long start = Stopwatch.GetTimestamp(); - using ArrayPoolList pruneTask = new(shardCountToPrune); + using ArrayPoolListRef pruneTask = new(shardCountToPrune); for (int i = 0; i < shardCountToPrune; i++) { @@ -1070,7 +1068,7 @@ private void PersistOnShutdown() { if (_commitSetQueue?.IsEmpty ?? true) return; - using ArrayPoolList candidateSets = DetermineCommitSetToPersistInSnapshot(_commitSetQueue.Count); + using ArrayPoolListRef candidateSets = DetermineCommitSetToPersistInSnapshot(_commitSetQueue.Count); if (candidateSets.Count == 0 && _commitSetQueue.TryDequeue(out BlockCommitSet anyCommmitSet)) { // No commitset to persist, likely as not enough block was processed to reached prune boundary diff --git a/src/Nethermind/Nethermind.Trie/VisitContext.cs b/src/Nethermind/Nethermind.Trie/VisitContext.cs index 72c8e0d46cfa..5d8f3f0c0c7e 100644 --- a/src/Nethermind/Nethermind.Trie/VisitContext.cs +++ b/src/Nethermind/Nethermind.Trie/VisitContext.cs @@ -62,7 +62,6 @@ public SmallTrieVisitContext(TrieVisitContext trieVisitContext) private byte _flags = 0; private const byte StorageFlag = 1; - private const byte ExpectAccountsFlag = 2; public bool IsStorage { @@ -79,21 +78,5 @@ internal set } } } - - public bool ExpectAccounts - { - readonly get => (_flags & ExpectAccountsFlag) == ExpectAccountsFlag; - internal set - { - if (value) - { - _flags = (byte)(_flags | ExpectAccountsFlag); - } - else - { - _flags = (byte)(_flags & ~ExpectAccountsFlag); - } - } - } } } diff --git a/src/Nethermind/Nethermind.TxPool.Test/SimpleRetryCacheTests.cs b/src/Nethermind/Nethermind.TxPool.Test/SimpleRetryCacheTests.cs new file mode 100644 index 000000000000..3d03515b3280 --- /dev/null +++ b/src/Nethermind/Nethermind.TxPool.Test/SimpleRetryCacheTests.cs @@ -0,0 +1,160 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Logging; +using NSubstitute; +using NUnit.Framework; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Nethermind.TxPool.Test; + +[TestFixture] +public class SimpleRetryCacheTests +{ + public readonly struct ResourceRequestMessage : INew + { + public int Resource { get; init; } + public static ResourceRequestMessage New(int resourceId) => new() { Resource = resourceId }; + } + + public interface ITestHandler : IMessageHandler; + + + private CancellationTokenSource _cancellationTokenSource; + private RetryCache cache; + + private readonly int Timeout = 3000; + + [SetUp] + public void Setup() + { + _cancellationTokenSource = new CancellationTokenSource(); + cache = new(TestLogManager.Instance, token: _cancellationTokenSource.Token); + } + + [TearDown] + public void TearDown() + { + _cancellationTokenSource?.Cancel(); + _cancellationTokenSource?.Dispose(); + } + + [Test] + public void Announced_SameResourceDifferentNode_ReturnsEnqueued() + { + AnnounceResult result1 = cache.Announced(1, Substitute.For()); + AnnounceResult result2 = cache.Announced(1, Substitute.For()); + + Assert.That(result1, Is.EqualTo(AnnounceResult.New)); + Assert.That(result2, Is.EqualTo(AnnounceResult.Enqueued)); + } + + [Test] + public async Task Announced_AfterTimeout_ExecutesRetryRequests() + { + ITestHandler request1 = Substitute.For(); + ITestHandler request2 = Substitute.For(); + + cache.Announced(1, request1); + cache.Announced(1, request2); + + await Task.Delay(Timeout, _cancellationTokenSource.Token); + + request1.DidNotReceive().HandleMessage(Arg.Any()); + request2.Received(1).HandleMessage(Arg.Any()); + } + + [Test] + public async Task Announced_MultipleResources_ExecutesAllRetryRequestsExceptInititalOne() + { + ITestHandler request1 = Substitute.For(); + ITestHandler request2 = Substitute.For(); + ITestHandler request3 = Substitute.For(); + ITestHandler request4 = Substitute.For(); + + cache.Announced(1, request1); + cache.Announced(1, request2); + cache.Announced(2, request3); + cache.Announced(2, request4); + + await Task.Delay(Timeout, _cancellationTokenSource.Token); + + request1.Received(0).HandleMessage(Arg.Any()); + request2.Received(1).HandleMessage(Arg.Any()); + request3.Received(0).HandleMessage(Arg.Any()); + request4.Received(1).HandleMessage(Arg.Any()); + } + + [Test] + public void Received_RemovesResourceFromRetryQueue() + { + cache.Announced(1, Substitute.For()); + cache.Received(1); + + AnnounceResult result = cache.Announced(1, Substitute.For()); + Assert.That(result, Is.EqualTo(AnnounceResult.New)); + } + + [Test] + public async Task Received_BeforeTimeout_PreventsRetryExecution() + { + ITestHandler request = Substitute.For(); + + cache.Announced(1, request); + cache.Announced(1, request); + cache.Received(1); + + await Task.Delay(Timeout, _cancellationTokenSource.Token); + + request.DidNotReceive().HandleMessage(ResourceRequestMessage.New(1)); + } + + [Test] + public async Task RetryExecution_HandlesExceptions() + { + ITestHandler faultyRequest = Substitute.For(); + ITestHandler normalRequest = Substitute.For(); + + faultyRequest.When(x => x.HandleMessage(Arg.Any())).Do(x => throw new InvalidOperationException("Test exception")); + + cache.Announced(1, Substitute.For()); + cache.Announced(1, faultyRequest); + cache.Announced(1, normalRequest); + + await Task.Delay(Timeout, _cancellationTokenSource.Token); + + normalRequest.Received(1).HandleMessage(Arg.Any()); + } + + [Test] + public async Task CancellationToken_StopsProcessing() + { + ITestHandler request = Substitute.For(); + + cache.Announced(1, request); + _cancellationTokenSource.Cancel(); + await Task.Delay(Timeout); + + request.DidNotReceive().HandleMessage(Arg.Any()); + } + + [Test] + public async Task Announced_AfterRetryInProgress_ReturnsNew() + { + cache.Announced(1, Substitute.For()); + + await Task.Delay(Timeout, _cancellationTokenSource.Token); + + AnnounceResult result = cache.Announced(1, Substitute.For()); + Assert.That(result, Is.EqualTo(AnnounceResult.New)); + } + + [Test] + public void Received_NonExistentResource_DoesNotThrow() + { + Assert.That(() => cache.Received(999), Throws.Nothing); + } +} diff --git a/src/Nethermind/Nethermind.TxPool.Test/TxBroadcasterTests.cs b/src/Nethermind/Nethermind.TxPool.Test/TxBroadcasterTests.cs index aafc8b2602d8..3c575ea5f35e 100644 --- a/src/Nethermind/Nethermind.TxPool.Test/TxBroadcasterTests.cs +++ b/src/Nethermind/Nethermind.TxPool.Test/TxBroadcasterTests.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -23,7 +23,6 @@ using Nethermind.Logging; using Nethermind.Network; using Nethermind.Network.P2P; -using Nethermind.Network.P2P.Subprotocols.Eth; using Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V65.Messages; using Nethermind.Network.P2P.Subprotocols.Eth.V67; @@ -520,10 +519,11 @@ public void should_broadcast_full_local_tx_immediately_after_receiving_it() Substitute.For(), RunImmediatelyScheduler.Instance, Substitute.For(), - Substitute.For(), Substitute.For(), Substitute.For(), - Substitute.For()); + Substitute.For(), + Substitute.For(), + Substitute.For()); _broadcaster.AddPeer(eth68Handler); Transaction localTx = Build.A.Transaction @@ -548,7 +548,6 @@ public void should_broadcast_hash_of_blob_local_tx_to_eth68_peers_immediately_af Substitute.For(), RunImmediatelyScheduler.Instance, Substitute.For(), - Substitute.For(), Substitute.For(), Substitute.For(), Substitute.For()); @@ -561,10 +560,11 @@ public void should_broadcast_hash_of_blob_local_tx_to_eth68_peers_immediately_af Substitute.For(), RunImmediatelyScheduler.Instance, Substitute.For(), - Substitute.For(), Substitute.For(), Substitute.For(), - Substitute.For()); + Substitute.For(), + Substitute.For(), + Substitute.For()); Transaction localTx = Build.A.Transaction .WithShardBlobTxTypeAndFields() @@ -602,10 +602,11 @@ public void should_broadcast_full_local_tx_up_to_max_size_and_only_announce_if_l Substitute.For(), RunImmediatelyScheduler.Instance, Substitute.For(), - Substitute.For(), Substitute.For(), Substitute.For(), - Substitute.For()); + Substitute.For(), + Substitute.For(), + Substitute.For()); Transaction localTx = Build.A.Transaction .WithData(new byte[txSize]) @@ -656,10 +657,11 @@ public void should_check_tx_policy_for_broadcast(bool canGossipTransactions, boo Substitute.For(), RunImmediatelyScheduler.Instance, Substitute.For(), - Substitute.For(), Substitute.For(), Substitute.For(), - Substitute.For()); + Substitute.For(), + Substitute.For(), + Substitute.For()); _broadcaster.AddPeer(eth68Handler); Transaction localTx = Build.A.Transaction diff --git a/src/Nethermind/Nethermind.TxPool/AcceptTxResult.cs b/src/Nethermind/Nethermind.TxPool/AcceptTxResult.cs index 3517b76f9e79..97c80befa18c 100644 --- a/src/Nethermind/Nethermind.TxPool/AcceptTxResult.cs +++ b/src/Nethermind/Nethermind.TxPool/AcceptTxResult.cs @@ -122,6 +122,7 @@ public AcceptTxResult(int id, string code, string? message = null) } public static implicit operator bool(AcceptTxResult result) => result.Id == Accepted.Id; + public static implicit operator AcceptTxResult(bool result) => result ? Accepted : Invalid; public AcceptTxResult WithMessage(string message) => new(Id, Code, message); public static bool operator ==(AcceptTxResult a, AcceptTxResult b) => a.Equals(b); public static bool operator !=(AcceptTxResult a, AcceptTxResult b) => !(a == b); diff --git a/src/Nethermind/Nethermind.TxPool/BlobTxStorage.cs b/src/Nethermind/Nethermind.TxPool/BlobTxStorage.cs index 80a8d6f097ee..cc76e38f619e 100644 --- a/src/Nethermind/Nethermind.TxPool/BlobTxStorage.cs +++ b/src/Nethermind/Nethermind.TxPool/BlobTxStorage.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Nethermind.Core; +using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Db; @@ -78,7 +79,7 @@ public void Delete(in ValueHash256 hash, in UInt256 timestamp) _lightBlobTxsDb.Remove(hash.BytesAsSpan); } - public void AddBlobTransactionsFromBlock(long blockNumber, IList blockBlobTransactions) + public void AddBlobTransactionsFromBlock(long blockNumber, in ArrayPoolListRef blockBlobTransactions) { if (blockBlobTransactions.Count == 0) { @@ -144,7 +145,7 @@ private void EncodeAndSaveTx(Transaction transaction, IDb db, Span txHashP db.PutSpan(txHashPrefixed, rlpStream.AsSpan()); } - private void EncodeAndSaveTxs(IList blockBlobTransactions, IDb db, long blockNumber) + private void EncodeAndSaveTxs(in ArrayPoolListRef blockBlobTransactions, IDb db, long blockNumber) { using NettyRlpStream rlpStream = _txDecoder.EncodeToNewNettyStream(blockBlobTransactions!, RlpBehaviors.InMempoolForm); db.PutSpan(blockNumber.ToBigEndianSpanWithoutLeadingZeros(out _), rlpStream.AsSpan()); diff --git a/src/Nethermind/Nethermind.TxPool/IBlobTxStorage.cs b/src/Nethermind/Nethermind.TxPool/IBlobTxStorage.cs index 7adb61d7c05d..77823ab13d3f 100644 --- a/src/Nethermind/Nethermind.TxPool/IBlobTxStorage.cs +++ b/src/Nethermind/Nethermind.TxPool/IBlobTxStorage.cs @@ -3,12 +3,13 @@ using System.Collections.Generic; using Nethermind.Core; +using Nethermind.Core.Collections; namespace Nethermind.TxPool; public interface IBlobTxStorage : ITxStorage { bool TryGetBlobTransactionsFromBlock(long blockNumber, out Transaction[]? blockBlobTransactions); - void AddBlobTransactionsFromBlock(long blockNumber, IList blockBlobTransactions); + void AddBlobTransactionsFromBlock(long blockNumber, in ArrayPoolListRef blockBlobTransactions); void DeleteBlobTransactionsFromBlock(long blockNumber); } diff --git a/src/Nethermind/Nethermind.TxPool/ITxPool.cs b/src/Nethermind/Nethermind.TxPool/ITxPool.cs index f4d703bf45b4..bdee1f2f4483 100644 --- a/src/Nethermind/Nethermind.TxPool/ITxPool.cs +++ b/src/Nethermind/Nethermind.TxPool/ITxPool.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -7,6 +7,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Int256; +using Nethermind.Network.Contract.Messages; namespace Nethermind.TxPool { @@ -38,6 +39,7 @@ public interface ITxPool void AddPeer(ITxPoolPeer peer); void RemovePeer(PublicKey nodeId); bool ContainsTx(Hash256 hash, TxType txType); + AnnounceResult AnnounceTx(ValueHash256 txhash, IMessageHandler retryHandler); AcceptTxResult SubmitTx(Transaction tx, TxHandlingOptions handlingOptions); bool RemoveTransaction(Hash256? hash); Transaction? GetBestTx(); diff --git a/src/Nethermind/Nethermind.TxPool/Nethermind.TxPool.csproj b/src/Nethermind/Nethermind.TxPool/Nethermind.TxPool.csproj index 7c667ed34e8a..2f5a3bab0bf0 100644 --- a/src/Nethermind/Nethermind.TxPool/Nethermind.TxPool.csproj +++ b/src/Nethermind/Nethermind.TxPool/Nethermind.TxPool.csproj @@ -6,6 +6,7 @@ + @@ -14,6 +15,7 @@ + diff --git a/src/Nethermind/Nethermind.TxPool/NullBlobTxStorage.cs b/src/Nethermind/Nethermind.TxPool/NullBlobTxStorage.cs index 6eeaf9df3538..a3ed71f0067a 100644 --- a/src/Nethermind/Nethermind.TxPool/NullBlobTxStorage.cs +++ b/src/Nethermind/Nethermind.TxPool/NullBlobTxStorage.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Nethermind.Core; +using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Int256; @@ -32,7 +33,7 @@ public bool TryGetBlobTransactionsFromBlock(long blockNumber, out Transaction[]? return false; } - public void AddBlobTransactionsFromBlock(long blockNumber, IList blockBlobTransactions) { } + public void AddBlobTransactionsFromBlock(long blockNumber, in ArrayPoolListRef blockBlobTransactions) { } public void DeleteBlobTransactionsFromBlock(long blockNumber) { } } diff --git a/src/Nethermind/Nethermind.TxPool/NullTxPool.cs b/src/Nethermind/Nethermind.TxPool/NullTxPool.cs index 31ae2339fdb8..49d519cc02c5 100644 --- a/src/Nethermind/Nethermind.TxPool/NullTxPool.cs +++ b/src/Nethermind/Nethermind.TxPool/NullTxPool.cs @@ -1,12 +1,13 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Int256; +using Nethermind.Network.Contract.Messages; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Nethermind.TxPool { @@ -86,6 +87,7 @@ public bool TryGetBlobAndProofV1(byte[] blobVersionedHash, public UInt256 GetLatestPendingNonce(Address address) => 0; + public AnnounceResult AnnounceTx(ValueHash256 txhash, IMessageHandler retryHandler) => AnnounceResult.New; public event EventHandler NewDiscovered { diff --git a/src/Nethermind/Nethermind.TxPool/RetryCache.cs b/src/Nethermind/Nethermind.TxPool/RetryCache.cs new file mode 100644 index 000000000000..6cd69a89cd67 --- /dev/null +++ b/src/Nethermind/Nethermind.TxPool/RetryCache.cs @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Collections.Pooled; +using Nethermind.Core; +using Nethermind.Core.Caching; +using Nethermind.Logging; +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Nethermind.TxPool; + +public class RetryCache + where TMessage : INew + where TResourceId : struct, IEquatable +{ + private readonly int _timeoutMs; + private readonly int _checkMs; + + private readonly ConcurrentDictionary>> _retryRequests = new(); + private readonly ConcurrentQueue<(TResourceId ResourceId, DateTimeOffset ExpiresAfter)> _expiringQueue = new(); + private readonly ClockKeyCache _requestingResources; + private readonly ILogger _logger; + + public RetryCache(ILogManager logManager, int timeoutMs = 2500, int requestingCacheSize = 1024, CancellationToken token = default) + { + _logger = logManager.GetClassLogger(); + + _timeoutMs = timeoutMs; + _checkMs = _timeoutMs / 5; + _requestingResources = new(requestingCacheSize); + + Task.Run(async () => + { + PeriodicTimer timer = new(TimeSpan.FromMilliseconds(_checkMs)); + + while (await timer.WaitForNextTickAsync(token)) + { + while (!token.IsCancellationRequested && _expiringQueue.TryPeek(out (TResourceId ResourceId, DateTimeOffset ExpiresAfter) item) && item.ExpiresAfter <= DateTimeOffset.UtcNow) + { + _expiringQueue.TryDequeue(out item); + + if (_retryRequests.TryRemove(item.ResourceId, out PooledSet>? requests)) + { + using (requests) + { + if (requests.Count > 0) + { + _requestingResources.Set(item.ResourceId); + } + + if (_logger.IsTrace) _logger.Trace($"Sending retry requests for {item.ResourceId} after timeout"); + + + foreach (IMessageHandler retryHandler in requests) + { + try + { + retryHandler.HandleMessage(TMessage.New(item.ResourceId)); + } + catch (Exception ex) + { + if (_logger.IsTrace) _logger.Error($"Failed to send retry request to {retryHandler} for {item.ResourceId}", ex); + } + } + } + } + } + } + }, token); + } + + public AnnounceResult Announced(TResourceId resourceId, IMessageHandler retryHandler) + { + if (!_requestingResources.Contains(resourceId)) + { + bool added = false; + + _retryRequests.AddOrUpdate(resourceId, (resourceId) => + { + if (_logger.IsTrace) _logger.Trace($"Announced {resourceId} by {retryHandler}: NEW"); + + _expiringQueue.Enqueue((resourceId, DateTimeOffset.UtcNow.AddMilliseconds(_timeoutMs))); + added = true; + + return []; + }, (resourceId, dict) => + { + if (_logger.IsTrace) _logger.Trace($"Announced {resourceId} by {retryHandler}: UPDATE"); + + dict.Add(retryHandler); + return dict; + }); + + return added ? AnnounceResult.New : AnnounceResult.Enqueued; + } + + if (_logger.IsTrace) _logger.Trace($"Announced {resourceId} by {retryHandler}, but a retry is in progress already, immidietly firing"); + + return AnnounceResult.New; + } + + public void Received(TResourceId resourceId) + { + if (_logger.IsTrace) _logger.Trace($"Received {resourceId}"); + + _retryRequests.TryRemove(resourceId, out PooledSet>? _); + _requestingResources.Delete(resourceId); + } +} + +public enum AnnounceResult +{ + New, + Enqueued, + PendingRequest +} + diff --git a/src/Nethermind/Nethermind.TxPool/TxPool.cs b/src/Nethermind/Nethermind.TxPool/TxPool.cs index 2707a994d921..d6c8275f1271 100644 --- a/src/Nethermind/Nethermind.TxPool/TxPool.cs +++ b/src/Nethermind/Nethermind.TxPool/TxPool.cs @@ -1,16 +1,19 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Autofac.Features.AttributeFilters; using CkzgLib; using Nethermind.Core; using Nethermind.Core.Caching; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; +using Nethermind.Core.Messages; using Nethermind.Core.Specs; using Nethermind.Core.Timers; using Nethermind.Crypto; using Nethermind.Int256; using Nethermind.Logging; +using Nethermind.Network.Contract.Messages; using Nethermind.TxPool.Collections; using Nethermind.TxPool.Filters; using System; @@ -21,8 +24,6 @@ using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; -using Autofac.Features.AttributeFilters; -using Nethermind.Core.Messages; using static Nethermind.TxPool.Collections.TxDistinctSortedPool; using ITimer = Nethermind.Core.Timers.ITimer; @@ -36,6 +37,8 @@ namespace Nethermind.TxPool /// public class TxPool : ITxPool, IAsyncDisposable { + private readonly RetryCache _retryCache; + private readonly IIncomingTxFilter[] _preHashFilters; private readonly IIncomingTxFilter[] _postHashFilters; @@ -121,6 +124,7 @@ public TxPool(IEthereumEcdsa ecdsa, _specProvider = _headInfo.SpecProvider; SupportsBlobs = _txPoolConfig.BlobsSupport != BlobsSupportMode.Disabled; _cts = new(); + _retryCache = new RetryCache(logManager, requestingCacheSize: MemoryAllowance.TxHashCacheSize / 10, token: _cts.Token); MemoryAllowance.MemPoolSize = txPoolConfig.Size; @@ -360,7 +364,7 @@ private void ReAddReorganisedTransactions(Block? previousBlock) private void RemoveProcessedTransactions(Block block) { Transaction[] blockTransactions = block.Transactions; - using ArrayPoolList blobTxsToSave = new((int)_specProvider.GetSpec(block.Header).MaxBlobCount); + using ArrayPoolListRef blobTxsToSave = new((int)_specProvider.GetSpec(block.Header).MaxBlobCount); long discoveredForPendingTxs = 0; long discoveredForHashCache = 0; long notInMempoool = 0; @@ -494,6 +498,7 @@ public AcceptTxResult SubmitTx(Transaction tx, TxHandlingOptions handlingOptions // If local tx allow it to be accepted even when syncing !startBroadcast) { + _retryCache.Received(tx.Hash!); return AcceptTxResult.Syncing; } @@ -533,6 +538,11 @@ public AcceptTxResult SubmitTx(Transaction tx, TxHandlingOptions handlingOptions _newHeadLock.ExitReadLock(); } + if (accepted != AcceptTxResult.Invalid) + { + _retryCache.Received(tx.Hash!); + } + if (accepted) { // Clear proper snapshot @@ -558,7 +568,7 @@ private void TryConvertProofVersion(Transaction tx) && tx is { SupportsBlobs: true, NetworkWrapper: ShardBlobNetworkWrapper { Version: ProofVersion.V0 } wrapper } && _headInfo.CurrentProofVersion is ProofVersion.V1) { - using ArrayPoolList cellProofs = new(Ckzg.CellsPerExtBlob * wrapper.Blobs.Length); + using ArrayPoolListRef cellProofs = new(Ckzg.CellsPerExtBlob * wrapper.Blobs.Length); foreach (byte[] blob in wrapper.Blobs) { @@ -572,6 +582,8 @@ private void TryConvertProofVersion(Transaction tx) } } + public AnnounceResult AnnounceTx(ValueHash256 txhash, IMessageHandler retryHandler) => _retryCache.Announced(txhash, retryHandler); + private AcceptTxResult FilterTransactions(Transaction tx, TxHandlingOptions handlingOptions, ref TxFilteringState state) { IIncomingTxFilter[] filters = _preHashFilters; diff --git a/src/Nethermind/Nethermind.Xdc.Test/EpochSwitchManagerTests.cs b/src/Nethermind/Nethermind.Xdc.Test/EpochSwitchManagerTests.cs new file mode 100644 index 000000000000..c45149aaf5ac --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc.Test/EpochSwitchManagerTests.cs @@ -0,0 +1,686 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using FluentAssertions; +using Google.Protobuf.WellKnownTypes; +using MathNet.Numerics.Distributions; +using Nethermind.Blockchain; +using Nethermind.Blockchain.Tracing.GethStyle.Custom.JavaScript; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Serialization.Rlp; +using Nethermind.Specs.ChainSpecStyle; +using Nethermind.Xdc.RLP; +using Nethermind.Xdc.Spec; +using Nethermind.Xdc.Types; +using NSubstitute; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Xdc.Test; +internal class EpochSwitchManagerTests +{ + private static ImmutableArray
SignerAddresses = [TestItem.AddressA, TestItem.AddressB]; + private static ImmutableArray
PenalizedAddresses = [TestItem.AddressC, TestItem.AddressD]; + private static ImmutableArray
StandbyAddresses = [TestItem.AddressE, TestItem.AddressF]; + private static ImmutableArray SignerSignatures = [TestItem.RandomSignatureA, TestItem.RandomSignatureB]; + private XdcBlockHeader GetChainOfBlocks(IBlockTree tree, ISnapshotManager snapManager, IXdcReleaseSpec spec, int length, int startRound = 0) + { + int i = startRound; + XdcBlockHeader block = CreateV2RegenesisBlock(spec); + do + { + if (i != startRound) + { + block = GenNormalBlock(spec, block!); + } + + if ((block.ExtraConsensusData?.CurrentRound ?? 0ul) % (ulong)spec.EpochLength == 0) + { + snapManager.GetSnapshot(block.Hash!).Returns(new Snapshot(block.Number, block.Hash!, [.. StandbyAddresses, .. SignerAddresses])); + } + + tree.FindHeader(block.Hash!).Returns(block); + tree.FindHeader(block.Number).Returns(block); + + } while (i++ < length); + + return block; + } + + private XdcBlockHeader GenNormalBlock(IXdcReleaseSpec spec, XdcBlockHeader? parent) + { + ulong newRound = 0; + Hash256? parentHash = null; + ulong prevRound = 0; + long blockNumber = 0; + if (parent is not null) + { + newRound = 1 + (parent.ExtraConsensusData?.CurrentRound ?? 0); + blockNumber = 1 + parent.Number; + prevRound = parent.ExtraConsensusData?.CurrentRound ?? 0; + parentHash = parent.Hash; + + } + Hash256 newBlockHash = Keccak.Compute(BitConverter.GetBytes(blockNumber).PadLeft(32)); + + + QuorumCertificate qc = new QuorumCertificate(new BlockRoundInfo(parent?.Hash ?? Keccak.Zero, prevRound, parent?.Number ?? 0), SignerSignatures.ToArray(), (ulong)spec.Gap); + ExtraFieldsV2 extraFieldsV2 = new ExtraFieldsV2((ulong)newRound, qc); + + XdcBlockHeader header = Build.A.XdcBlockHeader() + .TestObject; + header.Hash = newBlockHash; + header.Number = blockNumber; + header.ExtraConsensusData = extraFieldsV2; + header.ParentHash = parentHash; + + header.ValidatorsAddress = SignerAddresses; + header.PenaltiesAddress = PenalizedAddresses; + + return header; + } + + private XdcBlockHeader CreateV2RegenesisBlock(IXdcReleaseSpec spec) + { + Address[] signers = [TestItem.AddressA, TestItem.AddressB]; + + var header = (XdcBlockHeader)Build.A.XdcBlockHeader() + .WithNumber((long)spec.SwitchBlock) + .WithExtraData(FillExtraDataForTests(signers)) //2 master nodes + .WithParentHash(Keccak.EmptyTreeHash) + .TestObject; + + header.PenaltiesAddress = [TestItem.AddressC]; + return header; + } + + private byte[] FillExtraDataForTests(Address[] nextEpochCandidates) + { + var length = Address.Size * nextEpochCandidates?.Length ?? 0; + var extraData = new byte[XdcConstants.ExtraVanity + length + XdcConstants.ExtraSeal]; + + for (int i = 0; i < nextEpochCandidates!.Length; i++) + { + Array.Copy(nextEpochCandidates[i].Bytes, 0, extraData, XdcConstants.ExtraVanity + i * Address.Size, Address.Size); + } + + return extraData; + } + + private IEpochSwitchManager _epochSwitchManager; + private IBlockTree _tree = NSubstitute.Substitute.For(); + private ISpecProvider _config = NSubstitute.Substitute.For(); + private ISnapshotManager _snapshotManager = NSubstitute.Substitute.For(); + + [SetUp] + public void Setup() + { + _epochSwitchManager = new EpochSwitchManager(_config, _tree, _snapshotManager); + } + + [Test] + public void IsEpochSwitchAtBlock_ShouldReturnTrue_WhenBlockNumberIsSwitchBlock() + { + // Arrange + var switchBlock = 10ul; + var epochLength = 5ul; + + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)epochLength, + SwitchBlock = switchBlock, + V2Configs = [new V2ConfigParams()] + }; + + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader header = Build.A.XdcBlockHeader() + .TestObject; + + header.Number = (long)switchBlock; + // Act + bool result = _epochSwitchManager.IsEpochSwitchAtBlock(header); + // Assert + + Assert.That(result, Is.True); + } + + [Test] + public void IsEpochSwitchAtBlock_ShouldReturnFalseWhenHeaderExtraDataFails() + { + // Arrange + var switchBlock = 10ul; + var epochLength = 5; + + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)epochLength, + SwitchBlock = switchBlock, + V2Configs = [new V2ConfigParams()] + }; + + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader header = Build.A.XdcBlockHeader() + .TestObject; + + header.Number = (long)switchBlock + 1; + header.ExtraData = Encoding.UTF8.GetBytes("InvalidExtraData"); + + // Act + bool result = _epochSwitchManager.IsEpochSwitchAtBlock(header); + // Assert + Assert.That(result, Is.False); + } + + [Test] + public void IsEpochSwitchAtBlock_ShouldReturnTrue_WhenProposedHeaderNumberIsSwitchBlock() + { + // Arrange + var gapNumber = 0ul; + + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = 101, + V2Configs = [new V2ConfigParams()] + }; + + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 100); + + XdcBlockHeader proposedHeader = Build.A.XdcBlockHeader() + .TestObject; + + proposedHeader.Number = (long)releaseSpec.SwitchBlock; + proposedHeader.ParentHash = chainHead.Hash; + + QuorumCertificate qc = new QuorumCertificate(new BlockRoundInfo(chainHead.Hash!, chainHead.ExtraConsensusData!.CurrentRound, chainHead.Number), SignerSignatures.ToArray(), gapNumber); + ExtraFieldsV2 extraFieldsV2 = new ExtraFieldsV2(chainHead.ExtraConsensusData!.CurrentRound + 1, qc); + proposedHeader.ExtraConsensusData = extraFieldsV2; + + // Act + bool result = _epochSwitchManager.IsEpochSwitchAtBlock(proposedHeader); + // Assert + Assert.That(result, Is.True); + } + + [Test] + public void IsEpochSwitchAtBlock_ShouldReturnTrue_WhenParentRoundIsLessThanEpochStartRound() + { + // Arrange + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()], + SwitchEpoch = 2 + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 99); + + XdcBlockHeader proposedHeader = Build.A.XdcBlockHeader() + .TestObject; + + proposedHeader.Number = (long)chainHead.Number + 1; + proposedHeader.ParentHash = chainHead.Hash; + + QuorumCertificate qc = new QuorumCertificate(new BlockRoundInfo(chainHead.Hash!, chainHead.ExtraConsensusData!.CurrentRound, chainHead.Number), SignerSignatures.ToArray(), 1); + ExtraFieldsV2 extraFieldsV2 = new ExtraFieldsV2(chainHead.ExtraConsensusData!.CurrentRound + 1, qc); + proposedHeader.ExtraConsensusData = extraFieldsV2; + // Act + bool result = _epochSwitchManager.IsEpochSwitchAtBlock(proposedHeader); + // Assert + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.LessThan(extraFieldsV2.CurrentRound)); + + Assert.That(result, Is.True); + } + + [Test] + public void IsEpochSwitchAtBlock_ShouldReturnFalse_WhenParentRoundIsGreaterThanEpochStartRound() + { + // Arrange + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()], + SwitchEpoch = 2 + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 101); + + XdcBlockHeader proposedHeader = Build.A.XdcBlockHeader() + .TestObject; + + proposedHeader.Number = (long)chainHead.Number + 1; + proposedHeader.ParentHash = chainHead.Hash; + + QuorumCertificate qc = new QuorumCertificate(new BlockRoundInfo(chainHead.Hash!, chainHead.ExtraConsensusData!.CurrentRound, chainHead.Number), SignerSignatures.ToArray(), 1); + ExtraFieldsV2 extraFieldsV2 = new ExtraFieldsV2(chainHead.ExtraConsensusData!.CurrentRound - 1, qc); + proposedHeader.ExtraConsensusData = extraFieldsV2; + // Act + bool result = _epochSwitchManager.IsEpochSwitchAtBlock(proposedHeader); + // Assert + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.GreaterThan(extraFieldsV2.CurrentRound)); + + Assert.That(result, Is.False); + } + + [Test] + public void IsEpochSwitchAtBlock_ShouldReturnFalse_WhenParentRoundIsEqualToEpochStartRound() + { + // Arrange + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()], + SwitchEpoch = 2 + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 100); + + XdcBlockHeader proposedHeader = Build.A.XdcBlockHeader() + .TestObject; + + proposedHeader.Number = (long)chainHead.Number + 1; + proposedHeader.ParentHash = chainHead.Hash; + + QuorumCertificate qc = new QuorumCertificate(new BlockRoundInfo(chainHead.Hash!, chainHead.ExtraConsensusData!.CurrentRound, chainHead.Number), SignerSignatures.ToArray(), 1); + ExtraFieldsV2 extraFieldsV2 = new ExtraFieldsV2(chainHead.ExtraConsensusData!.CurrentRound, qc); + proposedHeader.ExtraConsensusData = extraFieldsV2; + // Act + bool result = _epochSwitchManager.IsEpochSwitchAtBlock(proposedHeader); + // Assert + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.EqualTo(extraFieldsV2.CurrentRound)); + + Assert.That(result, Is.False); + } + + [Test] + public void IsEpochSwitchAtRound_ShouldReturnTrue_WhenParentIsSwitchBlock() + { + // Arrange + var switchBlock = 10ul; + var epochLength = 5ul; + var currRound = 2ul; + var headerHash = Keccak.Zero; + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)epochLength, + SwitchBlock = switchBlock, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader parentHeader = Build.A.XdcBlockHeader() + .TestObject; + parentHeader.Hash = headerHash; + parentHeader.Number = (long)switchBlock; + + bool result = _epochSwitchManager.IsEpochSwitchAtRound(currRound, parentHeader); + // Assert + Assert.That(result, Is.True); + } + + [Test] + public void IsEpochSwitchAtRound_ShouldReturnFalse_WhenExtraConsensusDataIsNull() + { + // Arrange + var switchBlock = 10ul; + var epochLength = 5ul; + var headerHash = Keccak.Zero; + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)epochLength, + SwitchBlock = switchBlock, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + XdcBlockHeader parentHeader = Build.A.XdcBlockHeader() + .TestObject; + parentHeader.Hash = headerHash; + parentHeader.Number = (long)switchBlock - 1; + parentHeader.ExtraConsensusData = null; + + bool result = _epochSwitchManager.IsEpochSwitchAtRound(1, parentHeader); + // Assert + Assert.That(result, Is.False); + } + + [Test] + public void IsEpochSwitchAtRound_ShouldReturnFalse_WhenParentRoundIsGreaterThanCurrentRound() + { + // Arrange + var currRound = 42ul; + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 101); + + bool result = _epochSwitchManager.IsEpochSwitchAtRound(currRound, chainHead); + // Assert + Assert.That(result, Is.False); + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.GreaterThan(currRound)); + } + + [Test] + public void IsEpochSwitchAtRound_ShouldReturnTrue_WhenParentRoundIsLessThanEpochStartRound() + { + // Arrange + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()], + SwitchEpoch = 2 + }; + + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + // 99 is chosen so that parent round is less than epoch start round + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 99); + + XdcBlockHeader proposedHeader = Build.A.XdcBlockHeader() + .TestObject; + + proposedHeader.Number = (long)chainHead.Number + 1; + proposedHeader.ParentHash = chainHead.Hash; + + QuorumCertificate qc = new QuorumCertificate(new BlockRoundInfo(chainHead.Hash!, chainHead.ExtraConsensusData!.CurrentRound, chainHead.Number), SignerSignatures.ToArray(), 1); + ExtraFieldsV2 extraFieldsV2 = new ExtraFieldsV2(chainHead.ExtraConsensusData!.CurrentRound + 1, qc); + proposedHeader.ExtraConsensusData = extraFieldsV2; + + ulong currentEpochNumber = (ulong)releaseSpec.SwitchEpoch + extraFieldsV2.CurrentRound / (ulong)releaseSpec.EpochLength; + ulong currentEpochStartRound = currentEpochNumber * (ulong)releaseSpec.EpochLength; + + bool result = _epochSwitchManager.IsEpochSwitchAtRound(extraFieldsV2.CurrentRound, chainHead); + + // Assert + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.EqualTo(extraFieldsV2.CurrentRound - 1)); + Assert.That(currentEpochStartRound, Is.GreaterThan(chainHead.ExtraConsensusData!.CurrentRound)); + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.LessThan(extraFieldsV2.CurrentRound)); + Assert.That(result, Is.True); + } + + [Test] + public void IsEpochSwitchAtRound_ShouldReturnFalse_WhenParentRoundIsEqualToEpochStartRound() + { + // Arrange + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()], + SwitchEpoch = 2 + }; + + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + // 101 is chosen that parent is at epoch and child is not at epoch + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 100); + + XdcBlockHeader proposedHeader = Build.A.XdcBlockHeader() + .TestObject; + + proposedHeader.Number = (long)chainHead.Number + 1; + proposedHeader.ParentHash = chainHead.Hash; + + QuorumCertificate qc = new QuorumCertificate(new BlockRoundInfo(chainHead.Hash!, chainHead.ExtraConsensusData!.CurrentRound, chainHead.Number), SignerSignatures.ToArray(), 1); + ExtraFieldsV2 extraFieldsV2 = new ExtraFieldsV2(chainHead.ExtraConsensusData!.CurrentRound + 1, qc); + proposedHeader.ExtraConsensusData = extraFieldsV2; + + ulong currentEpochNumber = ((ulong)releaseSpec.SwitchEpoch + extraFieldsV2.CurrentRound) / (ulong)releaseSpec.EpochLength; + ulong currentEpochStartRound = currentEpochNumber * (ulong)releaseSpec.EpochLength; + + bool result = _epochSwitchManager.IsEpochSwitchAtRound(extraFieldsV2.CurrentRound, chainHead); + + // Assert + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.EqualTo(extraFieldsV2.CurrentRound - 1)); + Assert.That(currentEpochStartRound, Is.EqualTo(chainHead.ExtraConsensusData!.CurrentRound)); + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.LessThan(extraFieldsV2.CurrentRound)); + Assert.That(result, Is.False); + } + + [Test] + public void IsEpochSwitchAtRound_ShouldReturnFalse_WhenParentRoundIsGreaterThanEpochStartRound() + { + // Arrange + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()], + SwitchEpoch = 2 + }; + + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + // Create chain head at round 101 so that parent round is greater than epoch start round + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 101); + + XdcBlockHeader proposedHeader = Build.A.XdcBlockHeader() + .TestObject; + + proposedHeader.Number = (long)chainHead.Number + 1; + proposedHeader.ParentHash = chainHead.Hash; + + QuorumCertificate qc = new QuorumCertificate(new BlockRoundInfo(chainHead.Hash!, chainHead.ExtraConsensusData!.CurrentRound, chainHead.Number), SignerSignatures.ToArray(), 1); + ExtraFieldsV2 extraFieldsV2 = new ExtraFieldsV2(chainHead.ExtraConsensusData!.CurrentRound + 1, qc); + proposedHeader.ExtraConsensusData = extraFieldsV2; + + ulong currentEpochNumber = ((ulong)releaseSpec.SwitchEpoch + extraFieldsV2.CurrentRound) / (ulong)releaseSpec.EpochLength; + ulong currentEpochStartRound = currentEpochNumber * (ulong)releaseSpec.EpochLength; + + bool result = _epochSwitchManager.IsEpochSwitchAtRound(extraFieldsV2.CurrentRound, chainHead); + + // Assert + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.EqualTo(extraFieldsV2.CurrentRound - 1)); + Assert.That(currentEpochStartRound, Is.LessThan(chainHead.ExtraConsensusData!.CurrentRound)); + Assert.That(chainHead.ExtraConsensusData!.CurrentRound, Is.LessThan(extraFieldsV2.CurrentRound)); + Assert.That(result, Is.False); + } + + [Test] + public void GetEpochSwitchInfo_ShouldReturnNullIfBlockHashIsNotInTree() + { + var switchBlock = 10ul; + var epochLength = 4ul; + var parentHash = Keccak.EmptyTreeHash; + _tree.FindHeader(parentHash).Returns((XdcBlockHeader?)null); + + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)epochLength, + SwitchBlock = switchBlock, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + var result = _epochSwitchManager.GetEpochSwitchInfo(parentHash); + Assert.That(result, Is.Null); + } + + [Test] + public void GetEpochSwitchInfo_ShouldReturnEpochNumbersIfBlockIsAtEpoch_BlockNumber_Is_Zero() + { + ulong blockNumber = 0; + Hash256 hash256 = Keccak.Zero; + + ulong epochLength = 5; + ulong expectedEpochNumber = blockNumber / epochLength; + + Address[] signers = [TestItem.AddressA, TestItem.AddressB]; + EpochSwitchInfo expected = new(signers, [], [], new BlockRoundInfo(hash256, 0, (long)blockNumber)); + + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)epochLength, + SwitchBlock = blockNumber, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader header = Build.A.XdcBlockHeader() + .TestObject; + header.Hash = hash256; + header.Number = (long)blockNumber; + header.ExtraData = FillExtraDataForTests([TestItem.AddressA, TestItem.AddressB]); + + _snapshotManager.GetSnapshot(hash256).Returns(new Snapshot(header.Number, header.Hash!, signers)); + + _tree.FindHeader((long)blockNumber).Returns(header); + var result = _epochSwitchManager.GetEpochSwitchInfo(header); + result.Should().BeEquivalentTo(expected); + } + + [Test] + public void GetEpochSwitchInfo_ShouldReturnEpochSwitchIfBlockIsAtEpoch() + { + + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = (ulong)0, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 100); + var parentHeader = (XdcBlockHeader)_tree.FindHeader(chainHead.ParentHash!)!; + + EpochSwitchInfo expected = new(SignerAddresses.ToArray(), StandbyAddresses.ToArray(), PenalizedAddresses.ToArray(), new BlockRoundInfo(chainHead.Hash!, chainHead.ExtraConsensusData!.CurrentRound, chainHead.Number)); + expected.EpochSwitchParentBlockInfo = new(parentHeader.Hash!, parentHeader.ExtraConsensusData!.CurrentRound, parentHeader.Number); + + var result = _epochSwitchManager.GetEpochSwitchInfo(chainHead.Hash!); + + Assert.That(result, Is.Not.Null); + result.Should().BeEquivalentTo(expected); + } + + [Test] + public void GetEpochSwitchInfo_ShouldReturnEpochNumbersIfParentBlockIsAtEpoch() + { + ulong blockNumber = 100; + ulong epochLength = 5; + ulong expectedEpochNumber = (ulong)blockNumber / epochLength; + + + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)epochLength, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + // 101 is chosen that parent is at epoch and child is not at epoch + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, (int)blockNumber + 1); + + var parentHeader = (XdcBlockHeader)_tree.FindHeader((long)blockNumber)!; + EpochSwitchInfo expected = new( + parentHeader.ValidatorsAddress!.Value.ToArray(), + StandbyAddresses.ToArray(), + parentHeader.PenaltiesAddress!.Value.ToArray(), + new BlockRoundInfo(parentHeader.Hash!, parentHeader.ExtraConsensusData!.CurrentRound, (long)blockNumber)); + + expected.EpochSwitchParentBlockInfo = new(parentHeader.ParentHash!, parentHeader.ExtraConsensusData.CurrentRound - (ulong)1, parentHeader.Number - 1); + + var result = _epochSwitchManager.GetEpochSwitchInfo(chainHead.Hash!); + Assert.That(result, Is.Not.Null); + result.Should().BeEquivalentTo(expected); + } + + [Test] + public void GetEpochSwitchInfo_ShouldReturnNullIfBlockIsAtEpochAndSnapshotIsNull() + { + ulong blockNumber = 10; + ulong epochLength = 5; + ulong expectedEpochNumber = blockNumber / epochLength; + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)epochLength, + SwitchBlock = blockNumber, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader header = Build.A.XdcBlockHeader() + .TestObject; + header.Number = (long)blockNumber; + header.ExtraData = FillExtraDataForTests([TestItem.AddressA, TestItem.AddressB]); + header.Hash = TestItem.KeccakA; + + _snapshotManager.GetSnapshot(TestItem.KeccakA).Returns((Snapshot)null!); + + _tree.FindHeader((long)blockNumber).Returns(header); + _tree.FindHeader(header.Hash).Returns(header); + + var result = _epochSwitchManager.GetEpochSwitchInfo(header.Hash); + Assert.That(result, Is.Null); + } + + [Test] + public void GetBlockByEpochNumber_ShouldReturnNullIfNoBlockFound() + { + // Arrange + var epochNumber = 10ul; + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)5, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + _tree.FindHeader(Arg.Any()).Returns((XdcBlockHeader?)null); + // Act + var result = _epochSwitchManager.GetBlockByEpochNumber(epochNumber); + // Assert + Assert.That(result, Is.Null); + } + + [Test] + public void GetBlockByEpochNumber_ShouldReturnBlockIfFound() + { + // Arrange + var epochLength = 5ul; + var epochNumber = 7ul; + XdcReleaseSpec releaseSpec = new() + { + EpochLength = (int)epochLength, + SwitchBlock = 0, + V2Configs = [new V2ConfigParams()] + }; + _config.GetSpec(Arg.Any()).Returns(releaseSpec); + + XdcBlockHeader chainHead = GetChainOfBlocks(_tree, _snapshotManager, releaseSpec, 100); + + var headBlock = new Block(chainHead); + _tree.Head.Returns(headBlock); + + int expectedBlockNumber = (int)(epochNumber * epochLength); + + var result = _epochSwitchManager.GetBlockByEpochNumber(epochNumber); + + Assert.That(result?.BlockNumber, Is.EqualTo(expectedBlockNumber)); + } + +} diff --git a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcBlockHeaderBuilder.cs b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcBlockHeaderBuilder.cs index fa9bdc05a386..1b951fe0ab89 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcBlockHeaderBuilder.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcBlockHeaderBuilder.cs @@ -29,7 +29,7 @@ public XdcBlockHeaderBuilder() Address.Zero, UInt256.One, 1, - 30_000_000, + XdcConstants.TargetGasLimit, 1_700_000_000, new byte[] { 1, 2, 3 }) { @@ -84,12 +84,24 @@ public XdcBlockHeaderBuilder WithExtraConsensusData(ExtraFieldsV2 extraFieldsV2) return this; } + public new XdcBlockHeaderBuilder WithParentHash(Hash256 parentHash) + { + XdcTestObjectInternal.ParentHash = parentHash; + return this; + } + public new XdcBlockHeaderBuilder WithBaseFee(UInt256 baseFee) { TestObjectInternal.BaseFeePerGas = baseFee; return this; } + public new XdcBlockHeaderBuilder WithNumber(long blockNumber) + { + TestObjectInternal.Number = blockNumber; + return this; + } + public new XdcBlockHeaderBuilder WithHash(Hash256 hash256) { TestObjectInternal.Hash = hash256; diff --git a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestHelper.cs b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestHelper.cs new file mode 100644 index 000000000000..1455135ebba1 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestHelper.cs @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Crypto; +using Nethermind.Crypto; +using Nethermind.Serialization.Rlp; +using Nethermind.Xdc.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Xdc.Test; +internal class XdcTestHelper +{ + public static PrivateKey[] GeneratePrivateKeys(int count) + { + var keyBuilder = new PrivateKeyGenerator(); + return keyBuilder.Generate(count).ToArray(); + } + + public static QuorumCertificate CreateQc(BlockRoundInfo roundInfo, ulong gapNumber, PrivateKey[] keys) + { + EthereumEcdsa ecdsa = new EthereumEcdsa(0); + var qcEncoder = new VoteDecoder(); + + IEnumerable signatures = CreateVoteSignatures(roundInfo, gapNumber, keys); + + return new QuorumCertificate(roundInfo, signatures.ToArray(), gapNumber); + } + + public static Signature[] CreateVoteSignatures(BlockRoundInfo roundInfo, ulong gapnumber, PrivateKey[] keys) + { + EthereumEcdsa ecdsa = new EthereumEcdsa(0); + var encoder = new VoteDecoder(); + IEnumerable signatures = keys.Select(k => + { + var stream = new KeccakRlpStream(); + encoder.Encode(stream, new Vote(roundInfo, gapnumber), RlpBehaviors.ForSealing); + return ecdsa.Sign(k, stream.GetValueHash()); + }).ToArray(); + return signatures.ToArray(); + } +} diff --git a/src/Nethermind/Nethermind.Xdc.Test/QuorumCertificateManagerTest.cs b/src/Nethermind/Nethermind.Xdc.Test/QuorumCertificateManagerTest.cs index 124cf3fa07d0..f93869f5a97a 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/QuorumCertificateManagerTest.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/QuorumCertificateManagerTest.cs @@ -30,7 +30,8 @@ public void VerifyCertificate_CertificateIsNull_ThrowsArgumentNullException() Substitute.For(), Substitute.For(), Substitute.For(), - Substitute.For()); + Substitute.For(), + new BlockInfoValidator()); Assert.That(() => quorumCertificateManager.VerifyCertificate(null!, Build.A.XdcBlockHeader().TestObject, out _), Throws.ArgumentNullException); } @@ -43,7 +44,8 @@ public void VerifyCertificate_HeaderIsNull_ThrowsArgumentNullException() Substitute.For(), Substitute.For(), Substitute.For(), - Substitute.For()); + Substitute.For(), + new BlockInfoValidator()); Assert.That(() => quorumCertificateManager.VerifyCertificate(Build.A.QuorumCertificate().TestObject, null!, out _), Throws.ArgumentNullException); } @@ -55,25 +57,25 @@ public static IEnumerable QcCases() //Base valid control case PrivateKey[] keys = keyBuilder.Generate(20).ToArray(); IEnumerable
masterNodes = keys.Select(k => k.Address); - yield return new TestCaseData(CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 1), 0, keys), headerBuilder, keys.Select(k => k.Address), true); + yield return new TestCaseData(XdcTestHelper.CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 1), 0, keys), headerBuilder, keys.Select(k => k.Address), true); //Not enough signatures - yield return new TestCaseData(CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 1), 0, keys.Take(13).ToArray()), headerBuilder, keys.Select(k => k.Address), false); + yield return new TestCaseData(XdcTestHelper.CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 1), 0, keys.Take(13).ToArray()), headerBuilder, keys.Select(k => k.Address), false); //1 Vote is not master node - yield return new TestCaseData(CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 1), 0, keys), headerBuilder, keys.Skip(1).Select(k => k.Address), false); + yield return new TestCaseData(XdcTestHelper.CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 1), 0, keys), headerBuilder, keys.Skip(1).Select(k => k.Address), false); //Wrong gap number - yield return new TestCaseData(CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 1), 1, keys), headerBuilder, masterNodes, false); + yield return new TestCaseData(XdcTestHelper.CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 1), 1, keys), headerBuilder, masterNodes, false); //Wrong block number in QC - yield return new TestCaseData(CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 2), 0, keys), headerBuilder, masterNodes, false); + yield return new TestCaseData(XdcTestHelper.CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 1, 2), 0, keys), headerBuilder, masterNodes, false); //Wrong hash in QC - yield return new TestCaseData(CreateQc(new BlockRoundInfo(Hash256.Zero, 1, 1), 0, keys), headerBuilder, masterNodes, false); + yield return new TestCaseData(XdcTestHelper.CreateQc(new BlockRoundInfo(Hash256.Zero, 1, 1), 0, keys), headerBuilder, masterNodes, false); //Wrong round number in QC - yield return new TestCaseData(CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 0, 1), 0, keys), headerBuilder, masterNodes, false); + yield return new TestCaseData(XdcTestHelper.CreateQc(new BlockRoundInfo(headerBuilder.TestObject.Hash!, 0, 1), 0, keys), headerBuilder, masterNodes, false); } [TestCaseSource(nameof(QcCases))] @@ -81,8 +83,11 @@ public void VerifyCertificate_QcWithDifferentParameters_ReturnsExpected(QuorumCe { IEpochSwitchManager epochSwitchManager = Substitute.For(); epochSwitchManager - .GetEpochSwitchInfo(Arg.Any(), Arg.Any()) - .Returns(new EpochSwitchInfo(masternodes.ToArray(), [], new BlockRoundInfo(Hash256.Zero, 1, 10))); + .GetEpochSwitchInfo(Arg.Any()) + .Returns(new EpochSwitchInfo(masternodes.ToArray(), [], [], new BlockRoundInfo(Hash256.Zero, 1, 10))); + epochSwitchManager + .GetEpochSwitchInfo(Arg.Any()) + .Returns(new EpochSwitchInfo(masternodes.ToArray(), [], [], new BlockRoundInfo(Hash256.Zero, 1, 10))); ISpecProvider specProvider = Substitute.For(); IXdcReleaseSpec xdcReleaseSpec = Substitute.For(); xdcReleaseSpec.EpochLength.Returns(900); @@ -94,31 +99,9 @@ public void VerifyCertificate_QcWithDifferentParameters_ReturnsExpected(QuorumCe Substitute.For(), Substitute.For(), specProvider, - epochSwitchManager); + epochSwitchManager, + new BlockInfoValidator()); Assert.That(quorumCertificateManager.VerifyCertificate(quorumCert, xdcBlockHeaderBuilder.TestObject, out _), Is.EqualTo(expected)); } - - private static QuorumCertificate CreateQc(BlockRoundInfo roundInfo, ulong gapNumber, PrivateKey[] keys) - { - EthereumEcdsa ecdsa = new EthereumEcdsa(0); - var qcEncoder = new VoteDecoder(); - - IEnumerable signatures = CreateVoteSignatures(roundInfo, gapNumber, keys); - - return new QuorumCertificate(roundInfo, signatures.ToArray(), gapNumber); - } - - private static Signature[] CreateVoteSignatures(BlockRoundInfo roundInfo, ulong gapnumber, PrivateKey[] keys) - { - EthereumEcdsa ecdsa = new EthereumEcdsa(0); - var encoder = new VoteDecoder(); - IEnumerable signatures = keys.Select(k => - { - var stream = new KeccakRlpStream(); - encoder.Encode(stream, new Vote(roundInfo, gapnumber), RlpBehaviors.ForSealing); - return ecdsa.Sign(k, stream.GetValueHash()); - }).ToArray(); - return signatures.ToArray(); - } } diff --git a/src/Nethermind/Nethermind.Xdc.Test/TimeoutCertificateManagerTests.cs b/src/Nethermind/Nethermind.Xdc.Test/TimeoutCertificateManagerTests.cs index d7323e9f22e6..9f0a62e3ef82 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/TimeoutCertificateManagerTests.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/TimeoutCertificateManagerTests.cs @@ -11,8 +11,8 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; +using Nethermind.Consensus; using Nethermind.Crypto; -using Nethermind.Xdc.RLP; using Nethermind.Xdc.Spec; using Nethermind.Xdc.Types; @@ -47,10 +47,13 @@ public void VerifyTC_SnapshotMissing_ReturnsFalse() XdcBlockHeader header = Build.A.XdcBlockHeader().TestObject; blockTree.FindHeader(Arg.Any()).Returns(header); var tcManager = new TimeoutCertificateManager( + new XdcContext(), snapshotManager, Substitute.For(), Substitute.For(), - blockTree); + blockTree, + Substitute.For(), + Substitute.For()); var ok = tcManager.VerifyTimeoutCertificate(tc, out var err); Assert.That(ok, Is.False); @@ -68,10 +71,13 @@ public void VerifyTC_EmptyCandidates_ReturnsFalse() XdcBlockHeader header = Build.A.XdcBlockHeader().TestObject; blockTree.FindHeader(Arg.Any()).Returns(header); var tcManager = new TimeoutCertificateManager( + new XdcContext(), snapshotManager, Substitute.For(), Substitute.For(), - blockTree); + blockTree, + Substitute.For(), + Substitute.For()); var ok = tcManager.VerifyTimeoutCertificate(tc, out var err); Assert.That(ok, Is.False); @@ -110,9 +116,12 @@ public void VerifyTCWithDifferentParameters_ReturnsExpected(TimeoutCertificate t .Returns(new Snapshot(0, Hash256.Zero, masternodes)); IEpochSwitchManager epochSwitchManager = Substitute.For(); - var epochSwitchInfo = new EpochSwitchInfo(masternodes, [], new BlockRoundInfo(Hash256.Zero, 1, 10)); + var epochSwitchInfo = new EpochSwitchInfo(masternodes, [], [], new BlockRoundInfo(Hash256.Zero, 1, 10)); epochSwitchManager - .GetEpochSwitchInfo(Arg.Any(), Arg.Any()) + .GetEpochSwitchInfo(Arg.Any()) + .Returns(epochSwitchInfo); + epochSwitchManager + .GetEpochSwitchInfo(Arg.Any()) .Returns(epochSwitchInfo); epochSwitchManager.GetTimeoutCertificateEpochInfo(Arg.Any()).Returns(epochSwitchInfo); @@ -128,7 +137,12 @@ public void VerifyTCWithDifferentParameters_ReturnsExpected(TimeoutCertificate t blockTree.Head.Returns(new Block(header, new BlockBody())); blockTree.FindHeader(Arg.Any()).Returns(header); - var tcManager = new TimeoutCertificateManager(snapshotManager, epochSwitchManager, specProvider, blockTree); + var context = new XdcContext(); + ISyncInfoManager syncInfoManager = Substitute.For(); + ISigner signer = Substitute.For(); + + var tcManager = new TimeoutCertificateManager(context, snapshotManager, epochSwitchManager, specProvider, + blockTree, syncInfoManager, signer); Assert.That(tcManager.VerifyTimeoutCertificate(timeoutCertificate, out _), Is.EqualTo(expected)); } @@ -136,10 +150,13 @@ public void VerifyTCWithDifferentParameters_ReturnsExpected(TimeoutCertificate t private TimeoutCertificateManager BuildTimeoutCertificateManager() { return new TimeoutCertificateManager( + new XdcContext(), Substitute.For(), Substitute.For(), Substitute.For(), - Substitute.For()); + Substitute.For(), + Substitute.For(), + Substitute.For()); } private static TimeoutCertificate BuildTimeoutCertificate(PrivateKey[] keys, ulong round = 1, ulong gap = 0) diff --git a/src/Nethermind/Nethermind.Xdc.Test/VotesManagerTests.cs b/src/Nethermind/Nethermind.Xdc.Test/VotesManagerTests.cs new file mode 100644 index 000000000000..4bc770c08fe7 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc.Test/VotesManagerTests.cs @@ -0,0 +1,247 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Nethermind.Blockchain; +using Nethermind.Consensus; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; +using Nethermind.Serialization.Rlp; +using Nethermind.Xdc.Spec; +using Nethermind.Xdc.Types; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Xdc.Test; + +public class VotesManagerTests +{ + public static IEnumerable HandleVoteCases() + { + var (keys, _) = MakeKeys(20); + var masternodes = keys.Select(k => k.Address).ToArray(); + + ulong currentRound = 1; + XdcBlockHeader header = Build.A.XdcBlockHeader() + .WithExtraConsensusData(new ExtraFieldsV2(currentRound, new QuorumCertificate(new BlockRoundInfo(Hash256.Zero, 0, 0), null, 450))) + .TestObject; + var info = new BlockRoundInfo(header.Hash!, currentRound, header.Number); + + // Base case + yield return new TestCaseData(masternodes, header, currentRound, keys.Select(k => BuildSignedVote(info, 450, k)).ToArray(), info, 1); + + // Not enough valid signers + var (extraKeys, _) = MakeKeys(2); + var votes = keys.Take(12).Select(k => BuildSignedVote(info, 450, k)).ToArray(); + var extraVotes = extraKeys.Select(k => BuildSignedVote(info, 450, k)).ToArray(); + yield return new TestCaseData(masternodes, header, currentRound, votes.Concat(extraVotes).ToArray(), info, 0); + + // Wrong gap number generates different keys for the vote pool + var keysForVotes = keys.Take(14).ToArray(); + var votesWithDiffGap = new List(capacity: keysForVotes.Length); + for (var i = 0; i < keysForVotes.Length - 3; i++) votesWithDiffGap.Add(BuildSignedVote(info, 450, keysForVotes[i])); + for (var i = keysForVotes.Length - 3; i < keysForVotes.Length; i++) votesWithDiffGap.Add(BuildSignedVote(info, 451, keysForVotes[i])); + yield return new TestCaseData(masternodes, header, currentRound, votesWithDiffGap.ToArray(), info, 0); + } + + [TestCaseSource(nameof(HandleVoteCases))] + public async Task HandleVote_VariousScenarios_CommitsQcExpectedTimes(Address[] masternodes, XdcBlockHeader header, ulong currentRound, Vote[] votes, BlockRoundInfo info, int expectedCalls) + { + var context = new XdcContext { CurrentRound = currentRound }; + IBlockTree blockTree = Substitute.For(); + blockTree.FindHeader(Arg.Any(), Arg.Any()).Returns(header); + + IEpochSwitchManager epochSwitchManager = Substitute.For(); + var epochSwitchInfo = new EpochSwitchInfo(masternodes, [], [], info); + epochSwitchManager + .GetEpochSwitchInfo(header) + .Returns(epochSwitchInfo); + + ISnapshotManager snapshotManager = Substitute.For(); + IQuorumCertificateManager quorumCertificateManager = Substitute.For(); + ISpecProvider specProvider = Substitute.For(); + IXdcReleaseSpec xdcReleaseSpec = Substitute.For(); + xdcReleaseSpec.CertThreshold.Returns(0.667); + specProvider.GetSpec(Arg.Any()).Returns(xdcReleaseSpec); + + ISigner signer = Substitute.For(); + IForensicsProcessor forensicsProcessor = Substitute.For(); + IBlockInfoValidator blockInfoValidator = new BlockInfoValidator(); + + var voteManager = new VotesManager(context, blockTree, epochSwitchManager, snapshotManager, quorumCertificateManager, + specProvider, signer, forensicsProcessor, blockInfoValidator); + + foreach (var v in votes) + await voteManager.HandleVote(v); + + quorumCertificateManager.Received(expectedCalls).CommitCertificate(Arg.Any()); + } + + [Test] + public async Task HandleVote_HeaderMissing_ReturnsEarly() + { + var (keys, _) = MakeKeys(20); + var masternodes = keys.Select(k => k.Address).ToArray(); + + ulong currentRound = 1; + var context = new XdcContext { CurrentRound = currentRound }; + IBlockTree blockTree = Substitute.For(); + XdcBlockHeader header = Build.A.XdcBlockHeader() + .WithExtraConsensusData(new ExtraFieldsV2(currentRound, new QuorumCertificate(new BlockRoundInfo(Hash256.Zero, 0, 0), null, 450))) + .TestObject; + + var info = new BlockRoundInfo(header.Hash!, currentRound, header.Number); + IEpochSwitchManager epochSwitchManager = Substitute.For(); + var epochSwitchInfo = new EpochSwitchInfo(masternodes, [], [], info); + epochSwitchManager + .GetEpochSwitchInfo(header) + .Returns(epochSwitchInfo); + + ISnapshotManager snapshotManager = Substitute.For(); + IQuorumCertificateManager quorumCertificateManager = Substitute.For(); + ISpecProvider specProvider = Substitute.For(); + IXdcReleaseSpec xdcReleaseSpec = Substitute.For(); + xdcReleaseSpec.CertThreshold.Returns(0.667); + specProvider.GetSpec(Arg.Any()).Returns(xdcReleaseSpec); + + ISigner signer = Substitute.For(); + IForensicsProcessor forensicsProcessor = Substitute.For(); + IBlockInfoValidator blockInfoValidator = new BlockInfoValidator(); + + var voteManager = new VotesManager(context, blockTree, epochSwitchManager, snapshotManager, quorumCertificateManager, + specProvider, signer, forensicsProcessor, blockInfoValidator); + + var keysForVotes = keys.ToArray(); + for (var i = 0; i < keysForVotes.Length - 1; i++) + await voteManager.HandleVote(BuildSignedVote(info, gap: 450, keysForVotes[i])); + + quorumCertificateManager.DidNotReceive().CommitCertificate(Arg.Any()); + + // Now insert header and send one more + blockTree.FindHeader(header.Hash!, Arg.Any()).Returns(header); + await voteManager.HandleVote(BuildSignedVote(info, 450, keysForVotes[keysForVotes.Length - 1])); + + quorumCertificateManager.Received(1).CommitCertificate(Arg.Any()); + } + + [TestCase(5UL, 5UL, 5UL, false)] // Current round already voted + [TestCase(5UL, 4UL, 4UL, false)] // Current round different from blockInfoRound + [TestCase(5UL, 4UL, 5UL, true)] // No LockQc + public void VerifyVotingRules_FirstChecks_ReturnsExpected(ulong currentRound, ulong highestVotedRound, ulong blockInfoRound, bool expected) + { + var ctx = new XdcContext { CurrentRound = currentRound, HighestVotedRound = highestVotedRound }; + VotesManager votesManager = BuildVoteManager(ctx); + + var blockInfo = new BlockRoundInfo(Hash256.Zero, blockInfoRound, 100); + var qc = new QuorumCertificate(blockInfo, null, 0); + + Assert.That(votesManager.VerifyVotingRules(blockInfo, qc), Is.EqualTo(expected)); + } + + [Test] + public void VerifyVotingRules_QcNewerThanLockQc_ReturnsTrue() + { + var lockQc = new QuorumCertificate(new BlockRoundInfo(Hash256.Zero, 4, 99), null, 0); + var ctx = new XdcContext { CurrentRound = 5, HighestVotedRound = 4, LockQC = lockQc }; + VotesManager votesManager = BuildVoteManager(ctx); + + var blockInfo = new BlockRoundInfo(Hash256.Zero, 5, 100); + var qc = new QuorumCertificate(blockInfo, null, 0); + + Assert.That(votesManager.VerifyVotingRules(blockInfo, qc), Is.True); + } + + public static IEnumerable ExtendingFromAncestorCases() + { + XdcBlockHeader[] headers = GenerateBlockHeaders(3, 99); + IBlockTree blockTree = Substitute.For(); + var headerByHash = headers.ToDictionary(h => h.Hash!, h => h); + + XdcBlockHeader nonRelatedHeader = Build.A.XdcBlockHeader().WithNumber(99).TestObject; + nonRelatedHeader.Hash ??= nonRelatedHeader.CalculateHash().ToHash256(); + headerByHash[nonRelatedHeader.Hash] = nonRelatedHeader; + + blockTree.FindHeader(Arg.Any()).Returns(args => + { + var hash = (Hash256)args[0]; + return headerByHash.TryGetValue(hash, out var header) ? header : null; + }); + + var blockInfo = new BlockRoundInfo(headers[2].Hash!, 5, headers[2].Number); + + var ancestorQc = new QuorumCertificate(new BlockRoundInfo(headers[0].Hash!, 3, headers[0].Number), null, 0); + yield return new TestCaseData(blockTree, ancestorQc, blockInfo, true); + + var nonRelatedQc = new QuorumCertificate(new BlockRoundInfo(nonRelatedHeader.Hash, 3, nonRelatedHeader.Number), null, 0); + yield return new TestCaseData(blockTree, nonRelatedQc, blockInfo, false); + } + + [TestCaseSource(nameof(ExtendingFromAncestorCases))] + public void VerifyVotingRules_CheckExtendingFromAncestor_ReturnsExpected(IBlockTree tree, QuorumCertificate lockQc, BlockRoundInfo blockInfo, bool expected) + { + var ctx = new XdcContext { CurrentRound = 5, HighestVotedRound = 4, LockQC = lockQc }; + VotesManager votesManager = BuildVoteManager(ctx, tree); + var qc = new QuorumCertificate(new BlockRoundInfo(Hash256.Zero, 3, 99), null, 0); + + Assert.That(votesManager.VerifyVotingRules(blockInfo, qc), Is.EqualTo(expected)); + } + + private static (PrivateKey[] keys, Address[] addrs) MakeKeys(int n) + { + var keyBuilder = new PrivateKeyGenerator(); + PrivateKey[] keys = keyBuilder.Generate(n).ToArray(); + Address[] addrs = keys.Select(k => k.Address).ToArray(); + return (keys, addrs); + } + + private static Vote BuildSignedVote( + BlockRoundInfo info, ulong gap, PrivateKey key) + { + var decoder = new VoteDecoder(); + var ecdsa = new EthereumEcdsa(0); + var vote = new Vote(info, gap); + var stream = new KeccakRlpStream(); + decoder.Encode(stream, vote, RlpBehaviors.ForSealing); + vote.Signature = ecdsa.Sign(key, stream.GetValueHash()); + vote.Signer = key.Address; + return vote; + } + + private static VotesManager BuildVoteManager(XdcContext ctx, IBlockTree? blockTree = null) + { + blockTree ??= Substitute.For(); + IEpochSwitchManager epochSwitchManager = Substitute.For(); + ISnapshotManager snapshotManager = Substitute.For(); + IQuorumCertificateManager quorumCertificateManager = Substitute.For(); + ISpecProvider specProvider = Substitute.For(); + ISigner signer = Substitute.For(); + IForensicsProcessor forensicsProcessor = Substitute.For(); + IBlockInfoValidator blockInfoValidator = Substitute.For(); + + return new VotesManager(ctx, blockTree, epochSwitchManager, snapshotManager, quorumCertificateManager, + specProvider, signer, forensicsProcessor, blockInfoValidator); + } + + private static XdcBlockHeader[] GenerateBlockHeaders(int n, long blockNumber) + { + var headers = new XdcBlockHeader[n]; + var parentHash = Hash256.Zero; + var number = blockNumber; + for (int i = 0; i < n; i++, number++) + { + headers[i] = Build.A.XdcBlockHeader() + .WithNumber(number) + .WithParentHash(parentHash) + .TestObject; + parentHash = headers[i].CalculateHash().ToHash256(); + } + + return headers; + } +} diff --git a/src/Nethermind/Nethermind.Xdc.Test/XdcBlockProducerTest.cs b/src/Nethermind/Nethermind.Xdc.Test/XdcBlockProducerTest.cs new file mode 100644 index 000000000000..14abbf3656b4 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc.Test/XdcBlockProducerTest.cs @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain; +using Nethermind.Config; +using Nethermind.Consensus; +using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Transactions; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; +using Nethermind.Evm.State; +using Nethermind.Evm.Tracing; +using Nethermind.Logging; +using Nethermind.Xdc.Spec; +using NSubstitute; +using NUnit.Framework; +using System.Linq; +using System.Threading.Tasks; + +namespace Nethermind.Xdc.Test; +internal class XdcBlockProducerTest +{ + [Test] + public async Task BuildBlock_HasCorrectQC_ProducesValidHeader() + { + ISpecProvider specProvider = Substitute.For(); + IXdcReleaseSpec xdcReleaseSpec = Substitute.For(); + xdcReleaseSpec.MinePeriod.Returns(2); + xdcReleaseSpec.EpochLength.Returns(900); + xdcReleaseSpec.GasLimitBoundDivisor.Returns(1); + specProvider.GetSpec(Arg.Any()).Returns(xdcReleaseSpec); + var epochManager = Substitute.For(); + IWorldState stateProvider = Substitute.For(); + stateProvider.HasStateForBlock(Arg.Any()).Returns(true); + + PrivateKey[] masterNodes = XdcTestHelper.GeneratePrivateKeys(108); + epochManager + .GetEpochSwitchInfo(Arg.Any()) + .Returns(new Types.EpochSwitchInfo(masterNodes.Select(m => m.Address).ToArray(), [], [], new Types.BlockRoundInfo(Hash256.Zero, 0, 0))); + + ISealer sealer = new XdcSealer(new Signer(0, new ProtectedPrivateKey(masterNodes[1], ""), NullLogManager.Instance)); + + XdcBlockHeader parent = Build.A.XdcBlockHeader().TestObject; + + var xdcContext = new XdcContext(); + xdcContext.CurrentRound = 1; + xdcContext.HighestQC = XdcTestHelper.CreateQc(new Types.BlockRoundInfo(parent.Hash!, 0, parent.Number), 0, masterNodes); + + IBlockchainProcessor processor = Substitute.For(); + processor.Process(Arg.Any(), Arg.Any(), Arg.Any()).Returns(args => args.ArgAt(0)); + + XdcBlockProducer producer = new XdcBlockProducer( + epochManager, + Substitute.For(), + xdcContext, + Substitute.For(), + processor, + sealer, + Substitute.For(), + stateProvider, + Substitute.For(), + Substitute.For(), + specProvider, + Substitute.For(), + Substitute.For(), + Substitute.For()); + XdcHeaderValidator headerValidator = new XdcHeaderValidator(Substitute.For(), new XdcSealValidator(Substitute.For(), epochManager, specProvider), specProvider, NullLogManager.Instance); + + Block? block = await producer.BuildBlock(parent); + + Assert.That(block, Is.Not.Null); + bool actual = headerValidator.Validate(block.Header, parent, false, out string? error); + Assert.That(actual, Is.True); + } +} diff --git a/src/Nethermind/Nethermind.Xdc.Test/XdcHeaderStoreTests.cs b/src/Nethermind/Nethermind.Xdc.Test/XdcHeaderStoreTests.cs new file mode 100644 index 000000000000..767082192c27 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc.Test/XdcHeaderStoreTests.cs @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using FluentAssertions; +using Nethermind.Blockchain.Blocks; +using Nethermind.Blockchain.Headers; +using Nethermind.Core; +using Nethermind.Core.Test.Builders; +using Nethermind.Db; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Xdc.Test; +internal class XdcBlockAndHeaderStoreTests +{ + IXdcHeaderStore _headerStore; + IBlockStore _blockStore; + + IDb headerDb; + IDb blockNumDb; + IDb blockDb; + + + [SetUp] + public void Setup() + { + headerDb = new MemDb(); + blockNumDb = new MemDb(); + blockDb = new MemDb(); + + _headerStore = new XdcHeaderStore(headerDb, blockNumDb); + _blockStore = new XdcBlockStore(blockDb); + } + + [Test] + public void XdcHeaderStore_ShouldInheritFromHeaderStore() + { + Assert.That(_headerStore, Is.InstanceOf()); + } + + [Test] + public void XdcHeaderStore_InsertAndGetHeader_ShouldWorkCorrectly() + { + // Arrange + XdcBlockHeaderBuilder headerBuilder = Build.A.XdcBlockHeader().WithGeneratedExtraConsensusData(); + var header = headerBuilder.TestObject; + + // Act + _headerStore.Insert(header); + XdcBlockHeader? retrievedHeader = _headerStore.Get(header.Hash!, false); + // Assert + retrievedHeader.Should().BeEquivalentTo(header); + } + + [Test] + public void XdcBlockStore_InsertAndGetBlock_ShouldWorkCorrectly() + { + // Arrange + XdcBlockHeaderBuilder headerBuilder = Build.A.XdcBlockHeader().WithGeneratedExtraConsensusData(); + var header = headerBuilder.TestObject; + BlockBuilder blockBuilder = Build.A.Block.WithHeader(header); + var block = blockBuilder.TestObject; + // Act + _blockStore.Insert(block); + Block? retrievedBlock = _blockStore.Get(block.Number, block.Hash!); + // Assert + retrievedBlock.Should().BeEquivalentTo(block, options => options.Excluding(h => h.EncodedSize)); + } +} diff --git a/src/Nethermind/Nethermind.Xdc.Test/XdcSealValidatorTests.cs b/src/Nethermind/Nethermind.Xdc.Test/XdcSealValidatorTests.cs index 5796bcd4d34c..04e40b5e3904 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/XdcSealValidatorTests.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/XdcSealValidatorTests.cs @@ -17,6 +17,24 @@ namespace Nethermind.Xdc.Test; internal class XdcSealValidatorTests { + private static bool IsEpochSwitch(XdcBlockHeader header, IXdcReleaseSpec spec) + { + if (spec.SwitchBlock == header.Number) + { + return true; + } + ExtraFieldsV2? extraFields = header.ExtraConsensusData; + if (extraFields is null) + { + //Should this throw instead? + return false; + } + ulong parentRound = extraFields.QuorumCert.ProposedBlockInfo.Round; + ulong epochStart = extraFields.CurrentRound - extraFields.CurrentRound % (ulong)spec.EpochLength; + + return parentRound < epochStart; + } + [Test] public void ValidateSeal_NotXdcHeader_ThrowArgumentException() { @@ -183,7 +201,12 @@ public void ValidateParams_HeaderHasDifferentSealParameters_ReturnsExpected(XdcB .CalculateNextEpochMasternodes(Arg.Any(), Arg.Any()) .Returns((epochCandidates.ToArray(), penalties.ToArray())); IEpochSwitchManager epochSwitchManager = Substitute.For(); - epochSwitchManager.GetEpochSwitchInfo(Arg.Any(), Arg.Any()).Returns(new EpochSwitchInfo(epochCandidates.ToArray(), [], new BlockRoundInfo(Hash256.Zero, 0, 0))); + epochSwitchManager.GetEpochSwitchInfo(Arg.Any()).Returns(new EpochSwitchInfo(epochCandidates.ToArray(), [], [], new BlockRoundInfo(Hash256.Zero, 0, 0))); + epochSwitchManager.GetEpochSwitchInfo(Arg.Any()).Returns(new EpochSwitchInfo(epochCandidates.ToArray(), [], [], new BlockRoundInfo(Hash256.Zero, 0, 0))); + + bool isEpochSwitch = IsEpochSwitch(header, releaseSpec); + epochSwitchManager.IsEpochSwitchAtBlock(Arg.Any()).Returns(isEpochSwitch); + XdcSealValidator validator = new XdcSealValidator(snapshotManager, epochSwitchManager, specProvider); Assert.That(validator.ValidateParams(parent, header), Is.EqualTo(expected)); diff --git a/src/Nethermind/Nethermind.Xdc.Test/XdcSealerTests.cs b/src/Nethermind/Nethermind.Xdc.Test/XdcSealerTests.cs new file mode 100644 index 000000000000..1cbb4dbf1c94 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc.Test/XdcSealerTests.cs @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Consensus; +using Nethermind.Core.Crypto; +using Nethermind.Core.Test.Builders; +using Nethermind.Logging; +using NUnit.Framework; +using System.Threading; +using System.Threading.Tasks; + +namespace Nethermind.Xdc.Test; +internal class XdcSealerTests +{ + [Test] + public async Task SealBlock_ShouldSignXdcBlockHeader() + { + // Arrange + var sealer = new XdcSealer(new Signer(0, Build.A.PrivateKey.TestObject, NullLogManager.Instance)); + var block = Build.A.Block.WithHeader(Build.A.XdcBlockHeader().TestObject).TestObject; + + // Act + var sealedBlock = await sealer.SealBlock(block, CancellationToken.None); + XdcBlockHeader sealedHeader = (XdcBlockHeader)sealedBlock.Header; + + // Assert + Assert.That(sealedHeader.Validator, Is.Not.Null); + Assert.That(sealedHeader.Validator!.Length, Is.EqualTo(Signature.Size)); + } +} diff --git a/src/Nethermind/Nethermind.Xdc/BlockInfoValidator.cs b/src/Nethermind/Nethermind.Xdc/BlockInfoValidator.cs new file mode 100644 index 000000000000..262edbe9684a --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/BlockInfoValidator.cs @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Xdc.Types; + +namespace Nethermind.Xdc; + +public interface IBlockInfoValidator +{ + bool ValidateBlockInfo(BlockRoundInfo blockInfo, XdcBlockHeader blockHeader); +} + +public class BlockInfoValidator : IBlockInfoValidator +{ + public bool ValidateBlockInfo(BlockRoundInfo blockInfo, XdcBlockHeader blockHeader) => + (blockInfo.BlockNumber == blockHeader.Number) + && (blockInfo.Hash == blockHeader.Hash) + && (blockInfo.Round == blockHeader.ExtraConsensusData.CurrentRound); +} diff --git a/src/Nethermind/Nethermind.Xdc/EpochSwitchManager.cs b/src/Nethermind/Nethermind.Xdc/EpochSwitchManager.cs new file mode 100644 index 000000000000..b0275d1b8715 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/EpochSwitchManager.cs @@ -0,0 +1,410 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain; +using Nethermind.Core; +using Nethermind.Core.Caching; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Xdc.Spec; +using Nethermind.Xdc.Types; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Nethermind.Xdc; +internal class EpochSwitchManager : IEpochSwitchManager +{ + public EpochSwitchManager(ISpecProvider xdcSpecProvider, IBlockTree tree, ISnapshotManager snapshotManager) + { + _xdcSpecProvider = xdcSpecProvider; + _tree = tree; + _snapshotManager = snapshotManager; + } + + private ISpecProvider _xdcSpecProvider { get; } + private IBlockTree _tree { get; } + private ISnapshotManager _snapshotManager { get; } + private LruCache _round2EpochBlockInfo { get; set; } = new(XdcConstants.InMemoryRound2Epochs, nameof(_round2EpochBlockInfo)); + private LruCache _epochSwitches { get; set; } = new(XdcConstants.InMemoryEpochs, nameof(_epochSwitches)); + + /** + * Determine if the given block is an epoch switch block. + **/ + public bool IsEpochSwitchAtBlock(XdcBlockHeader header) + { + var xdcSpec = _xdcSpecProvider.GetXdcSpec(header); + + if (header.Number == xdcSpec.SwitchBlock) + { + return true; + } + + if (header.ExtraConsensusData is null) + { + return false; + } + + var round = header.ExtraConsensusData.CurrentRound; + var qc = header.ExtraConsensusData.QuorumCert; + + ulong parentRound = qc.ProposedBlockInfo.Round; + ulong epochStartRound = round - (round % (ulong)xdcSpec.EpochLength); + ulong epochNumber = (ulong)xdcSpec.SwitchEpoch + round / (ulong)xdcSpec.EpochLength; + + if (qc.ProposedBlockInfo.BlockNumber == xdcSpec.SwitchBlock) + { + return true; + } + + if (parentRound < epochStartRound) + { + _round2EpochBlockInfo.Set(round, new BlockRoundInfo(header.Hash, round, header.Number)); + return true; + } + + return false; + } + + /** + * Determine if an epoch switch occurs at the given round, based on the parent block. + **/ + public bool IsEpochSwitchAtRound(ulong currentRound, XdcBlockHeader parent) + { + var xdcSpec = _xdcSpecProvider.GetXdcSpec(parent); + + ulong epochNumber = (ulong)xdcSpec.SwitchEpoch + currentRound / (ulong)xdcSpec.EpochLength; + + if (parent.Number == xdcSpec.SwitchBlock) + { + return true; + } + + if (parent.ExtraConsensusData is null) + { + return false; + } + + var parentRound = parent.ExtraConsensusData.CurrentRound; + if (currentRound <= parentRound) + { + return false; + } + + ulong epochStartRound = currentRound - (currentRound % (ulong)xdcSpec.EpochLength); + return parentRound < epochStartRound; + } + + public EpochSwitchInfo? GetEpochSwitchInfo(XdcBlockHeader header) + { + if (_epochSwitches.TryGet(header.Hash, out var epochSwitchInfo)) + { + return epochSwitchInfo; + } + + var xdcSpec = _xdcSpecProvider.GetXdcSpec(header); + + while (!IsEpochSwitchAtBlock(header)) + { + header = (XdcBlockHeader)_tree.FindHeader(header.ParentHash); + } + + Address[] masterNodes; + + if (header.Number == xdcSpec.SwitchBlock) + { + masterNodes = XdcExtensions.ExtractAddresses(header.ExtraData[XdcConstants.ExtraVanity..^XdcConstants.ExtraSeal]).Value.ToArray(); + } + else + { + if (header.ExtraConsensusData is null) + { + return null; + } + + masterNodes = header.ValidatorsAddress.Value.ToArray(); + } + + + var snap = _snapshotManager.GetSnapshot(header.Hash); + if (snap is null) + { + return null; + } + + Address[] penalties = header.PenaltiesAddress.Value.ToArray(); + Address[] candidates = snap.NextEpochCandidates; + + var stanbyNodes = new Address[0]; + + if (masterNodes.Length != candidates.Length) + { + stanbyNodes = candidates + .Except(masterNodes) + .Except(penalties) + .ToArray(); + } + + epochSwitchInfo = new EpochSwitchInfo(masterNodes, stanbyNodes, penalties, new BlockRoundInfo(header.Hash, header.ExtraConsensusData?.CurrentRound ?? 0, header.Number)); + + if (header.ExtraConsensusData?.QuorumCert is not null) + { + epochSwitchInfo.EpochSwitchParentBlockInfo = header.ExtraConsensusData.QuorumCert.ProposedBlockInfo; + } + + + _epochSwitches.Set(header.Hash, epochSwitchInfo); + return epochSwitchInfo; + } + + public EpochSwitchInfo? GetEpochSwitchInfo(Hash256 hash) + { + if (_epochSwitches.TryGet(hash, out var epochSwitchInfo)) + { + return epochSwitchInfo; + } + + XdcBlockHeader h = (XdcBlockHeader)_tree.FindHeader(hash); + if (h is null) + { + return null; + } + + return GetEpochSwitchInfo(h); + } + + private EpochSwitchInfo[] GetEpochSwitchBetween(XdcBlockHeader start, XdcBlockHeader end) + { + var epochSwitchInfos = new List(); + + Hash256 iteratorHash = end.Hash; + long iteratorBlockNumber = end.Number; + + while (iteratorBlockNumber > start.Number) + { + EpochSwitchInfo epochSwitchInfo; + + if ((epochSwitchInfo = GetEpochSwitchInfo(iteratorHash)) is null) + { + return null; + } + + if (epochSwitchInfo.EpochSwitchParentBlockInfo is null) + { + break; + } + + iteratorHash = epochSwitchInfo.EpochSwitchParentBlockInfo.Hash; + iteratorBlockNumber = epochSwitchInfo.EpochSwitchBlockInfo.BlockNumber; + + if (iteratorBlockNumber >= start.Number) + { + epochSwitchInfos.Add(epochSwitchInfo); + } + } + + epochSwitchInfos.Reverse(); + return epochSwitchInfos.ToArray(); + } + + private BlockRoundInfo? GetBlockInfoInCache(ulong estRound, ulong epoch) + { + var epochSwitchInCache = new List(); + + for (ulong r = estRound; r < estRound + (ulong)epoch; r++) + { + if (_round2EpochBlockInfo.TryGet(r, out BlockRoundInfo blockInfo)) + { + epochSwitchInCache.Add(blockInfo); + } + } + + if (epochSwitchInCache.Count == 1) + { + return epochSwitchInCache[0]; + } + else if (epochSwitchInCache.Count == 0) + { + return null; + } + + foreach (var blockInfo in epochSwitchInCache) + { + var header = _tree.FindHeader(blockInfo.BlockNumber); + if (header is null) + { + continue; + } + if (header.Hash == blockInfo.Hash) + { + return blockInfo; + } + } + + return null; + } + + private bool TryBinarySearchBlockByEpochNumber(ulong targetEpochNumber, long start, long end, ulong switchBlock, ulong epoch, IXdcReleaseSpec xdcSpec, out BlockRoundInfo epochBlockInfo) + { + while (start < end) + { + var header = (XdcBlockHeader)_tree.FindHeader((start + end) / 2); + if (header is null) + { + epochBlockInfo = null; + return false; + } + + if (header.ExtraConsensusData is null) + { + epochBlockInfo = null; + return false; + } + + bool isEpochSwitch = IsEpochSwitchAtBlock(header); + ulong epochNum = (ulong)xdcSpec.SwitchEpoch + (header.ExtraConsensusData?.CurrentRound ?? 0) / (ulong)xdcSpec.EpochLength; + + if (epochNum == targetEpochNumber) + { + if (header.ExtraConsensusData is null) + { + epochBlockInfo = null; + return false; + } + + ulong round = header.ExtraConsensusData.CurrentRound; + + if (isEpochSwitch) + { + epochBlockInfo = new BlockRoundInfo(header.Hash, round, header.Number); + return true; + } + else + { + end = header.Number; + // trick to shorten the search + start = Math.Max(start, end - (int)(round % epoch)); + } + } + else if (epochNum > targetEpochNumber) + { + end = header.Number; + } + else + { + long nextStart = header.Number; + if (nextStart == start) + { + break; + } + start = nextStart; + } + } + + epochBlockInfo = null; + return false; + } + + public EpochSwitchInfo? GetTimeoutCertificateEpochInfo(TimeoutCertificate timeoutCert) + { + var headOfChainHeader = (XdcBlockHeader)_tree.Head.Header; + + EpochSwitchInfo epochSwitchInfo = GetEpochSwitchInfo(headOfChainHeader); + if (epochSwitchInfo is null) + { + return null; + } + + var xdcSpec = _xdcSpecProvider.GetXdcSpec(headOfChainHeader); + + ulong epochRound = epochSwitchInfo.EpochSwitchBlockInfo.Round; + ulong tempTCEpoch = (ulong)xdcSpec.SwitchEpoch + epochRound / (ulong)xdcSpec.EpochLength; + + var epochBlockInfo = new BlockRoundInfo(epochSwitchInfo.EpochSwitchBlockInfo.Hash, epochRound, epochSwitchInfo.EpochSwitchBlockInfo.BlockNumber); + + while (epochBlockInfo.Round > timeoutCert.Round) + { + tempTCEpoch--; + + if (GetBlockByEpochNumber(tempTCEpoch) is null) + { + return null; + } + } + + return GetEpochSwitchInfo(epochBlockInfo.Hash); + } + + public BlockRoundInfo? GetBlockByEpochNumber(ulong targetEpoch) + { + var headHeader = _tree.Head.Header as XdcBlockHeader; + + var xdcSpec = _xdcSpecProvider.GetXdcSpec(headHeader); + + EpochSwitchInfo epochSwitchInfo = GetEpochSwitchInfo(headHeader); + if (epochSwitchInfo is null) + { + return null; + } + + ulong epochNumber = (ulong)xdcSpec.SwitchEpoch + epochSwitchInfo.EpochSwitchBlockInfo.Round / (ulong)xdcSpec.EpochLength; + + if (targetEpoch == epochNumber) + { + return epochSwitchInfo.EpochSwitchBlockInfo; + } + + if (targetEpoch > epochNumber) + { + return null; + } + + if (targetEpoch < (ulong)xdcSpec.SwitchEpoch) + { + return null; + } + + ulong estRound = (targetEpoch - (ulong)xdcSpec.SwitchEpoch) * (ulong)xdcSpec.EpochLength; + + var epochBlockInfo = GetBlockInfoInCache(estRound, (ulong)xdcSpec.EpochLength); + if (epochBlockInfo is not null) + { + return epochBlockInfo; + } + + var epoch = (ulong)xdcSpec.EpochLength; + ulong estBlockNumDiff = epoch * (epochNumber - targetEpoch); + long estBlockNum = Math.Max((long)xdcSpec.SwitchBlock, epochSwitchInfo.EpochSwitchBlockInfo.BlockNumber - (long)estBlockNumDiff); + + ulong closeEpochNum = 2ul; + + if (closeEpochNum >= epochNumber - targetEpoch) + { + var estBlockHeader = (XdcBlockHeader)_tree.FindHeader(estBlockNum); + if (estBlockHeader is null) + { + return null; + } + var epochSwitchInfos = GetEpochSwitchBetween(estBlockHeader, headHeader); + if (epochSwitchInfos is null) + { + return null; + } + foreach (var info in epochSwitchInfos) + { + ulong epochNum = (ulong)xdcSpec.SwitchEpoch + info.EpochSwitchBlockInfo.Round / (ulong)xdcSpec.EpochLength; + if (epochNum == targetEpoch) + { + return info.EpochSwitchBlockInfo; + } + } + } + + if (!TryBinarySearchBlockByEpochNumber(targetEpoch, estBlockNum, epochSwitchInfo.EpochSwitchBlockInfo.BlockNumber, (ulong)xdcSpec.SwitchBlock, (ulong)xdcSpec.EpochLength, xdcSpec, out epochBlockInfo)) + { + return null; + } + + return epochBlockInfo; + } +} diff --git a/src/Nethermind/Nethermind.Xdc/Errors/QuorumCertificateException.cs b/src/Nethermind/Nethermind.Xdc/Errors/QuorumCertificateException.cs index 81c208dfe276..9cb282969361 100644 --- a/src/Nethermind/Nethermind.Xdc/Errors/QuorumCertificateException.cs +++ b/src/Nethermind/Nethermind.Xdc/Errors/QuorumCertificateException.cs @@ -3,19 +3,9 @@ using Nethermind.Blockchain; using Nethermind.Xdc.Types; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Nethermind.Xdc.Errors; -internal class QuorumCertificateException : BlockchainException +internal class QuorumCertificateException(QuorumCertificate certificate, string message) : BlockchainException(message) { - public QuorumCertificateException(QuorumCertificate certificate, string message) : base(message) - { - Certificate = certificate; - } - - public QuorumCertificate Certificate { get; } + public QuorumCertificate Certificate { get; } = certificate; } diff --git a/src/Nethermind/Nethermind.Xdc/IBlockInfoValidator.cs b/src/Nethermind/Nethermind.Xdc/IBlockInfoValidator.cs deleted file mode 100644 index 60164059a164..000000000000 --- a/src/Nethermind/Nethermind.Xdc/IBlockInfoValidator.cs +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Xdc.Types; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Nethermind.Xdc; -internal interface IBlockInfoValidator -{ - void VerifyBlockInfo(BlockRoundInfo blockInfo, XdcBlockHeader blockHeader); -} diff --git a/src/Nethermind/Nethermind.Xdc/IEpochSwitchManager.cs b/src/Nethermind/Nethermind.Xdc/IEpochSwitchManager.cs index f6d21fbc14db..f5d0b3d7fa7e 100644 --- a/src/Nethermind/Nethermind.Xdc/IEpochSwitchManager.cs +++ b/src/Nethermind/Nethermind.Xdc/IEpochSwitchManager.cs @@ -1,25 +1,16 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using Nethermind.Blockchain; using Nethermind.Xdc.Types; using Nethermind.Core.Crypto; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Nethermind.Xdc; public interface IEpochSwitchManager { - EpochSwitchInfo? GetPreviousEpochSwitchInfoByHash(Hash256 parentHash, int limit); - bool IsEpochSwitchAtRound(ulong currentRound, XdcBlockHeader parent, out ulong epochNumber); - bool IsEpochSwitchAtBlock(XdcBlockHeader header, out ulong epochNumber); - EpochSwitchInfo? GetEpochSwitchInfo(XdcBlockHeader header, Hash256 parentHash); - bool IsEpochSwitch(XdcBlockHeader header, out ulong epochNumber); - EpochSwitchInfo[] GetEpochSwitchBetween(XdcBlockHeader start, XdcBlockHeader end); - (ulong currentCheckpointNumber, ulong epochNumber) GetCurrentEpochNumbers(ulong blockNumber); + bool IsEpochSwitchAtRound(ulong currentRound, XdcBlockHeader parent); + bool IsEpochSwitchAtBlock(XdcBlockHeader header); + EpochSwitchInfo? GetEpochSwitchInfo(XdcBlockHeader? header); + EpochSwitchInfo? GetEpochSwitchInfo(Hash256 blockHash); EpochSwitchInfo? GetTimeoutCertificateEpochInfo(TimeoutCertificate timeoutCertificate); BlockRoundInfo? GetBlockByEpochNumber(ulong epochNumber); } diff --git a/src/Nethermind/Nethermind.Xdc/IForensicsProcessor.cs b/src/Nethermind/Nethermind.Xdc/IForensicsProcessor.cs index 7ee5fe2a8de2..a9aac8a3e094 100644 --- a/src/Nethermind/Nethermind.Xdc/IForensicsProcessor.cs +++ b/src/Nethermind/Nethermind.Xdc/IForensicsProcessor.cs @@ -27,7 +27,7 @@ public interface IForensicsProcessor Task ProcessVoteEquivocation(Vote incomingVote); - Task DetectEquivocationInVotePool(Vote vote, List votePool); + Task DetectEquivocationInVotePool(Vote vote, IEnumerable votePool); Task SendVoteEquivocationProof(Vote vote1, Vote vote2, Address signer); } diff --git a/src/Nethermind/Nethermind.Xdc/IPenaltyHandler.cs b/src/Nethermind/Nethermind.Xdc/IPenaltyHandler.cs index 242441f9791d..a4eec6486712 100644 --- a/src/Nethermind/Nethermind.Xdc/IPenaltyHandler.cs +++ b/src/Nethermind/Nethermind.Xdc/IPenaltyHandler.cs @@ -3,13 +3,6 @@ using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Evm.State; -using Nethermind.Xdc.Types; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Nethermind.Xdc; public interface IPenaltyHandler diff --git a/src/Nethermind/Nethermind.Xdc/ISyncInfoManager.cs b/src/Nethermind/Nethermind.Xdc/ISyncInfoManager.cs index 4ca0423ac7eb..d1c578819cc2 100644 --- a/src/Nethermind/Nethermind.Xdc/ISyncInfoManager.cs +++ b/src/Nethermind/Nethermind.Xdc/ISyncInfoManager.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace Nethermind.Xdc; -internal interface ISyncInfoManager +public interface ISyncInfoManager { void ProcessSyncInfo(SyncInfo syncInfo); bool VerifySyncInfo(SyncInfo syncInfo); diff --git a/src/Nethermind/Nethermind.Xdc/ITimeoutCertificateManager.cs b/src/Nethermind/Nethermind.Xdc/ITimeoutCertificateManager.cs index f645556f8c24..1be500848244 100644 --- a/src/Nethermind/Nethermind.Xdc/ITimeoutCertificateManager.cs +++ b/src/Nethermind/Nethermind.Xdc/ITimeoutCertificateManager.cs @@ -3,13 +3,14 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Xdc.Types; -using System; +using System.Threading.Tasks; namespace Nethermind.Xdc; public interface ITimeoutCertificateManager { - void HandleTimeout(Timeout timeout); - void OnCountdownTimer(DateTime time); + Task OnReceiveTimeout(Timeout timeout); + Task HandleTimeout(Timeout timeout); + void OnCountdownTimer(); void ProcessTimeoutCertificate(TimeoutCertificate timeoutCertificate); bool VerifyTimeoutCertificate(TimeoutCertificate timeoutCertificate, out string errorMessage); } diff --git a/src/Nethermind/Nethermind.Xdc/IVotesManager.cs b/src/Nethermind/Nethermind.Xdc/IVotesManager.cs index 49da4cb39dd9..1b55e88cf94f 100644 --- a/src/Nethermind/Nethermind.Xdc/IVotesManager.cs +++ b/src/Nethermind/Nethermind.Xdc/IVotesManager.cs @@ -2,10 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Xdc.Types; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace Nethermind.Xdc; @@ -13,7 +9,6 @@ internal interface IVotesManager { Task CastVote(BlockRoundInfo blockInfo); Task HandleVote(Vote vote); - Task VerifyVotes(List votes, XdcBlockHeader header); + Task OnReceiveVote(Vote vote); bool VerifyVotingRules(BlockRoundInfo blockInfo, QuorumCertificate qc); - List GetVotes(); } diff --git a/src/Nethermind/Nethermind.Xdc/IXdcHeaderStore.cs b/src/Nethermind/Nethermind.Xdc/IXdcHeaderStore.cs new file mode 100644 index 000000000000..af14ce999857 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/IXdcHeaderStore.cs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain.Headers; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Xdc; +internal interface IXdcHeaderStore : IHeaderStore +{ + void Insert(XdcBlockHeader header) => ((IHeaderStore)this).Insert(header); + void BulkInsert(IReadOnlyList headers) => BulkInsert(headers.Cast().ToList()); + new XdcBlockHeader? Get(Hash256 blockHash, bool shouldCache, long? blockNumber = null) => ((IHeaderStore)this).Get(blockHash, shouldCache, blockNumber) as XdcBlockHeader; + + void Cache(XdcBlockHeader header) => ((IHeaderStore)this).Cache(header); +} diff --git a/src/Nethermind/Nethermind.Xdc/QuorumCertificateManager.cs b/src/Nethermind/Nethermind.Xdc/QuorumCertificateManager.cs index 48aec9287172..fb09828f18c2 100644 --- a/src/Nethermind/Nethermind.Xdc/QuorumCertificateManager.cs +++ b/src/Nethermind/Nethermind.Xdc/QuorumCertificateManager.cs @@ -2,23 +2,16 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; using Nethermind.Blockchain; -using Nethermind.Config; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Db; -using Nethermind.Logging; using Nethermind.Serialization.Rlp; -using Nethermind.Xdc; using Nethermind.Xdc.Errors; using Nethermind.Xdc.Spec; using Nethermind.Xdc.Types; @@ -32,18 +25,21 @@ public QuorumCertificateManager( IBlockTree chain, IDb qcDb, ISpecProvider xdcConfig, - IEpochSwitchManager epochSwitchManager) + IEpochSwitchManager epochSwitchManager, + IBlockInfoValidator blockInfoValidator) { _context = context; _blockTree = chain; _qcDb = qcDb; _specProvider = xdcConfig; _epochSwitchManager = epochSwitchManager; + _blockInfoValidator = blockInfoValidator; } private XdcContext _context { get; } private IBlockTree _blockTree; private readonly IDb _qcDb; + private IBlockInfoValidator _blockInfoValidator; private IEpochSwitchManager _epochSwitchManager { get; } private ISpecProvider _specProvider { get; } private EthereumEcdsa _ethereumEcdsa = new EthereumEcdsa(0); @@ -61,8 +57,7 @@ public void CommitCertificate(QuorumCertificate qc) if (proposedBlockHeader is null) throw new InvalidBlockException(proposedBlockHeader, "Proposed block header not found in chain"); - //TODO this could be wrong way of fetching spec if a release spec is defined on a round basis - IXdcReleaseSpec spec = _specProvider.GetXdcSpec(proposedBlockHeader); + IXdcReleaseSpec spec = _specProvider.GetXdcSpec(proposedBlockHeader, _context.CurrentRound); //Can only look for a QC in proposed block after the switch block if (proposedBlockHeader.Number > spec.SwitchBlock) @@ -83,7 +78,7 @@ public void CommitCertificate(QuorumCertificate qc) if (qc.ProposedBlockInfo.Round >= _context.CurrentRound) { - _context.SetNewRound(_blockTree, qc.ProposedBlockInfo.Round); + _context.SetNewRound(_blockTree, qc.ProposedBlockInfo.Round + 1); } } @@ -107,7 +102,7 @@ private void SaveQc(QuorumCertificate qc, long key) private bool CommitBlock(IBlockTree chain, XdcBlockHeader proposedBlockHeader, ulong proposedRound, QuorumCertificate proposedQuorumCert) { IXdcReleaseSpec spec = _specProvider.GetXdcSpec(proposedBlockHeader); - //Can only commit a QC if the proposed block is at least 2 blocks after the switch block, since we want to check grand parent of proposed QC + //Can only commit a QC if the proposed block is at least 2 blocks after the switch block, since we want to check grandparent of proposed QC if ((proposedBlockHeader.Number - 2) <= spec.SwitchBlock) return false; @@ -147,7 +142,7 @@ public bool VerifyCertificate(QuorumCertificate qc, XdcBlockHeader parentHeader, if (qc.Signatures is null) throw new ArgumentException("QC must contain vote signatures.", nameof(qc)); - EpochSwitchInfo epochSwitchInfo = _epochSwitchManager.GetEpochSwitchInfo(parentHeader, qc.ProposedBlockInfo.Hash); + EpochSwitchInfo epochSwitchInfo = _epochSwitchManager.GetEpochSwitchInfo(parentHeader) ?? _epochSwitchManager.GetEpochSwitchInfo(qc.ProposedBlockInfo.Hash); if (epochSwitchInfo is null) { error = $"Epoch switch info not found for header {parentHeader?.ToString(Format.FullHashAndNumber)}"; @@ -196,7 +191,7 @@ public bool VerifyCertificate(QuorumCertificate qc, XdcBlockHeader parentHeader, return false; } - if (!ValidateBlockInfo(qc, parentHeader)) + if (!_blockInfoValidator.ValidateBlockInfo(qc.ProposedBlockInfo, parentHeader)) { error = "QC block data does not match header data."; return false; @@ -205,15 +200,4 @@ public bool VerifyCertificate(QuorumCertificate qc, XdcBlockHeader parentHeader, error = null; return true; } - - private bool ValidateBlockInfo(QuorumCertificate qc, XdcBlockHeader parentHeader) - { - if (qc.ProposedBlockInfo.BlockNumber != parentHeader.Number) - return false; - if (qc.ProposedBlockInfo.Hash != parentHeader.Hash) - return false; - if (qc.ProposedBlockInfo.Round != parentHeader.ExtraConsensusData.CurrentRound) - return false; - return true; - } } diff --git a/src/Nethermind/Nethermind.Xdc/RLP/SnapshotDecoder.cs b/src/Nethermind/Nethermind.Xdc/RLP/SnapshotDecoder.cs index eafa36f6a290..e46d339dea09 100644 --- a/src/Nethermind/Nethermind.Xdc/RLP/SnapshotDecoder.cs +++ b/src/Nethermind/Nethermind.Xdc/RLP/SnapshotDecoder.cs @@ -27,18 +27,7 @@ public Snapshot Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors return new Snapshot(number, hash256, candidates); } - - public Rlp Encode(Snapshot item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - { - if (item is null) - return Rlp.OfEmptySequence; - - RlpStream rlpStream = new(GetLength(item, rlpBehaviors)); - Encode(rlpStream, item, rlpBehaviors); - return new Rlp(rlpStream.Data.ToArray()); - } - - private Address[] DecodeAddressArray(ref Rlp.ValueDecoderContext decoderContext) + public static Address[] DecodeAddressArray(ref Rlp.ValueDecoderContext decoderContext) { if (decoderContext.IsNextItemNull()) { @@ -60,6 +49,16 @@ private Address[] DecodeAddressArray(ref Rlp.ValueDecoderContext decoderContext) return addresses; } + public Rlp Encode(Snapshot item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + if (item is null) + return Rlp.OfEmptySequence; + + RlpStream rlpStream = new(GetLength(item, rlpBehaviors)); + Encode(rlpStream, item, rlpBehaviors); + return new Rlp(rlpStream.Data.ToArray()); + } + public Snapshot Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { if (rlpStream.IsNextItemNull()) diff --git a/src/Nethermind/Nethermind.Xdc/RLP/VoteDecoder.cs b/src/Nethermind/Nethermind.Xdc/RLP/VoteDecoder.cs index cad93402dff0..e424126dc8df 100644 --- a/src/Nethermind/Nethermind.Xdc/RLP/VoteDecoder.cs +++ b/src/Nethermind/Nethermind.Xdc/RLP/VoteDecoder.cs @@ -78,6 +78,17 @@ public void Encode(RlpStream stream, Vote item, RlpBehaviors rlpBehaviors = RlpB stream.Encode(item.GapNumber); } + public Rlp Encode(Vote item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + if (item is null) + return Rlp.OfEmptySequence; + + RlpStream rlpStream = new(GetLength(item, rlpBehaviors)); + Encode(rlpStream, item, rlpBehaviors); + + return new Rlp(rlpStream.Data.ToArray()); + } + public int GetLength(Vote item, RlpBehaviors rlpBehaviors) { return Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); diff --git a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs index f3d2fe89828e..48e5629df82b 100644 --- a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs @@ -10,7 +10,7 @@ namespace Nethermind.Xdc.Spec; public class XdcReleaseSpec : ReleaseSpec, IXdcReleaseSpec { public int EpochLength { get; set; } - public long Gap { get; set; } + public int Gap { get; set; } public int SwitchEpoch { get; set; } public UInt256 SwitchBlock { get; set; } public int MaxMasternodes { get; set; } // v2 max masternodes @@ -29,6 +29,7 @@ public class XdcReleaseSpec : ReleaseSpec, IXdcReleaseSpec public int MinimumSigningTx { get; set; } // Signing txs that a node needs to produce to get out of penalty, after `LimitPenaltyEpoch` public List V2Configs { get; set; } + public void ApplyV2Config(ulong round) { V2ConfigParams configParams = GetConfigAtRound(V2Configs, round); @@ -57,7 +58,7 @@ internal static V2ConfigParams GetConfigAtRound(List list, ulong public interface IXdcReleaseSpec : IReleaseSpec { public int EpochLength { get; } - public long Gap { get; } + public int Gap { get; } public int SwitchEpoch { get; set; } public UInt256 SwitchBlock { get; set; } public int MaxMasternodes { get; set; } // v2 max masternodes diff --git a/src/Nethermind/Nethermind.Xdc/TimeoutCertificateManager.cs b/src/Nethermind/Nethermind.Xdc/TimeoutCertificateManager.cs index 7d0de40dcd28..e14413df64a0 100644 --- a/src/Nethermind/Nethermind.Xdc/TimeoutCertificateManager.cs +++ b/src/Nethermind/Nethermind.Xdc/TimeoutCertificateManager.cs @@ -3,41 +3,88 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Nethermind.Blockchain; +using Nethermind.Consensus; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Serialization.Rlp; using Nethermind.Xdc.RLP; +using Nethermind.Xdc.Errors; using Nethermind.Xdc.Types; using Nethermind.Xdc.Spec; namespace Nethermind.Xdc; -public class TimeoutCertificateManager(ISnapshotManager snapshotManager, IEpochSwitchManager epochSwitchManager, ISpecProvider specProvider, IBlockTree blockTree) : ITimeoutCertificateManager +public class TimeoutCertificateManager(XdcContext context, ISnapshotManager snapshotManager, IEpochSwitchManager epochSwitchManager, ISpecProvider specProvider, IBlockTree blockTree, ISyncInfoManager syncInfoManager, ISigner signer) : ITimeoutCertificateManager { + private XdcContext _ctx = context; private ISnapshotManager _snapshotManager = snapshotManager; private IEpochSwitchManager _epochSwitchManager = epochSwitchManager; private ISpecProvider _specProvider = specProvider; private IBlockTree _blockTree = blockTree; + private ISyncInfoManager _syncInfoManager = syncInfoManager; + private ISigner _signer = signer; + private EthereumEcdsa _ethereumEcdsa = new EthereumEcdsa(0); private static readonly TimeoutDecoder _timeoutDecoder = new(); + private XdcPool _timeouts = new(); - public void HandleTimeout(Timeout timeout) + public Task HandleTimeout(Timeout timeout) { - throw new NotImplementedException(); + if (timeout.Round != _ctx.CurrentRound) + { + // Not interested in processing timeout for round different from the current one + return Task.CompletedTask; + } + + _timeouts.Add(timeout); + var collectedTimeouts = _timeouts.GetItems(timeout); + + var xdcHeader = _blockTree.Head?.Header as XdcBlockHeader; + EpochSwitchInfo epochSwitchInfo = _epochSwitchManager.GetEpochSwitchInfo(xdcHeader); + if (epochSwitchInfo is null) + { + // Failed to get epoch switch info, cannot process timeout + return Task.CompletedTask; + } + + IXdcReleaseSpec spec = _specProvider.GetXdcSpec(xdcHeader, timeout.Round); + var certThreshold = spec.CertThreshold; + if (collectedTimeouts.Count >= epochSwitchInfo.Masternodes.Length * certThreshold) + { + OnTimeoutPoolThresholdReached(collectedTimeouts, timeout); + } + return Task.CompletedTask; } - public void OnCountdownTimer(DateTime time) + private void OnTimeoutPoolThresholdReached(IEnumerable timeouts, Timeout timeout) { - throw new NotImplementedException(); + Signature[] signatures = timeouts.Select(t => t.Signature).ToArray(); + + var timeoutCertificate = new TimeoutCertificate(timeout.Round, signatures, timeout.GapNumber); + + ProcessTimeoutCertificate(timeoutCertificate); + + SyncInfo syncInfo = _syncInfoManager.GetSyncInfo(); + //TODO: Broadcast syncInfo } public void ProcessTimeoutCertificate(TimeoutCertificate timeoutCertificate) { - throw new NotImplementedException(); + if (timeoutCertificate.Round > _ctx.HighestTC.Round) + { + _ctx.HighestTC = timeoutCertificate; + } + + if (timeoutCertificate.Round >= _ctx.CurrentRound) + { + //TODO Check how this new round is set + _ctx.SetNewRound(_blockTree, timeoutCertificate.Round + 1); + } } public bool VerifyTimeoutCertificate(TimeoutCertificate timeoutCertificate, out string errorMessage) @@ -60,9 +107,7 @@ public bool VerifyTimeoutCertificate(TimeoutCertificate timeoutCertificate, out var nextEpochCandidates = new HashSet
(snapshot.NextEpochCandidates); var signatures = new HashSet(timeoutCertificate.Signatures); - BlockHeader header = _blockTree.Head?.Header; - if (header is not XdcBlockHeader xdcHeader) - throw new InvalidOperationException($"Only type of {nameof(XdcBlockHeader)} is allowed"); + var xdcHeader = _blockTree.Head?.Header as XdcBlockHeader; IXdcReleaseSpec spec = _specProvider.GetXdcSpec(xdcHeader, timeoutCertificate.Round); EpochSwitchInfo epochInfo = _epochSwitchManager.GetTimeoutCertificateEpochInfo(timeoutCertificate); if (epochInfo is null) @@ -98,11 +143,105 @@ public bool VerifyTimeoutCertificate(TimeoutCertificate timeoutCertificate, out return true; } + public void OnCountdownTimer() + { + if (!AllowedToSend()) + return; + + SendTimeout(); + _ctx.TimeoutCounter++; + + var xdcHeader = _blockTree.Head?.Header as XdcBlockHeader; + IXdcReleaseSpec spec = _specProvider.GetXdcSpec(xdcHeader!, _ctx.CurrentRound); + + if (_ctx.TimeoutCounter % spec.TimeoutSyncThreshold == 0) + { + SyncInfo syncInfo = _syncInfoManager.GetSyncInfo(); + //TODO: Broadcast syncInfo + } + } + + public Task OnReceiveTimeout(Timeout timeout) + { + var currentBlock = _blockTree.Head ?? throw new InvalidOperationException("Failed to get current block"); + var currentHeader = currentBlock.Header as XdcBlockHeader; + var currentBlockNumber = currentBlock.Number; + var epochLenth = _specProvider.GetXdcSpec(currentHeader, timeout.Round).EpochLength; + if (Math.Abs((long)timeout.GapNumber - currentBlockNumber) > 3 * epochLenth) + { + // Discarded propagated timeout, too far away + return Task.CompletedTask; + } + + if (FilterTimeout(timeout)) + { + //TODO: Broadcast Timeout + return HandleTimeout(timeout); + } + return Task.CompletedTask; + } + + private bool FilterTimeout(Timeout timeout) + { + if (timeout.Round < _ctx.CurrentRound) return false; + Snapshot snapshot = _snapshotManager.GetSnapshotByGapNumber(_blockTree, timeout.GapNumber); + if (snapshot is null || snapshot.NextEpochCandidates.Length == 0) return false; + + // Verify msg signature + ValueHash256 timeoutMsgHash = ComputeTimeoutMsgHash(timeout.Round, timeout.GapNumber); + Address signer = _ethereumEcdsa.RecoverAddress(timeout.Signature, in timeoutMsgHash); + timeout.Signer = signer; + + return snapshot.NextEpochCandidates.Contains(signer); + } + + private void SendTimeout() + { + ulong gapNumber = 0; + var currentHeader = (XdcBlockHeader)_blockTree.Head?.Header; + if (currentHeader is null) throw new InvalidOperationException("Failed to retrieve current header"); + IXdcReleaseSpec spec = _specProvider.GetXdcSpec(currentHeader, _ctx.CurrentRound); + if (_epochSwitchManager.IsEpochSwitchAtRound(_ctx.CurrentRound, currentHeader)) + { + ulong currentNumber = (ulong)currentHeader.Number + 1; + gapNumber = Math.Max(0, currentNumber - currentNumber % (ulong)spec.EpochLength - (ulong)spec.Gap); + } + else + { + EpochSwitchInfo epochSwitchInfo = _epochSwitchManager.GetEpochSwitchInfo(currentHeader); + if (epochSwitchInfo is null) + throw new ConsensusHeaderDataExtractionException(nameof(EpochSwitchInfo)); + + ulong currentNumber = (ulong)epochSwitchInfo.EpochSwitchBlockInfo.BlockNumber; + gapNumber = Math.Max(0, currentNumber - currentNumber % (ulong)spec.EpochLength - (ulong)spec.Gap); + } + + ValueHash256 msgHash = ComputeTimeoutMsgHash(_ctx.CurrentRound, gapNumber); + Signature signedHash = _signer.Sign(msgHash); + var timeoutMsg = new Timeout(_ctx.CurrentRound, signedHash, gapNumber); + timeoutMsg.Signer = _signer.Address; + + HandleTimeout(timeoutMsg); + + //TODO: Broadcast _ctx.HighestTC + } + + // Returns true if the signer is within the master node list + private bool AllowedToSend() + { + var currentHeader = (XdcBlockHeader)_blockTree.Head?.Header; + EpochSwitchInfo epochSwitchInfo = _epochSwitchManager.GetEpochSwitchInfo(currentHeader); + if (epochSwitchInfo is null) + return false; + return epochSwitchInfo.Masternodes.Contains(_signer.Address); + } + internal static ValueHash256 ComputeTimeoutMsgHash(ulong round, ulong gap) { - var timeout = new Timeout(round, null, gap); - Rlp encoded = _timeoutDecoder.Encode(timeout, RlpBehaviors.ForSealing); - return Keccak.Compute(encoded.Bytes).ValueHash256; + Timeout timeout = new(round, null, gap); + KeccakRlpStream stream = new KeccakRlpStream(); + _timeoutDecoder.Encode(stream, timeout, RlpBehaviors.ForSealing); + return stream.GetValueHash(); } -} +} diff --git a/src/Nethermind/Nethermind.Xdc/Types/EpochSwitchInfo.cs b/src/Nethermind/Nethermind.Xdc/Types/EpochSwitchInfo.cs index b4455914710e..3f14241b928a 100644 --- a/src/Nethermind/Nethermind.Xdc/Types/EpochSwitchInfo.cs +++ b/src/Nethermind/Nethermind.Xdc/Types/EpochSwitchInfo.cs @@ -6,9 +6,11 @@ namespace Nethermind.Xdc.Types; -public class EpochSwitchInfo(Address[] masternodes, Address[] penalties, BlockRoundInfo epochSwitchBlockInfo) +public class EpochSwitchInfo(Address[] masternodes, Address[] StandbyNodes, Address[] penalties, BlockRoundInfo epochSwitchCurrentBlockInfo) { public Address[] Masternodes { get; set; } = masternodes; + public Address[] StandbyNodes { get; } = StandbyNodes; public Address[] Penalties { get; set; } = penalties; - public BlockRoundInfo EpochSwitchBlockInfo { get; set; } = epochSwitchBlockInfo; + public BlockRoundInfo EpochSwitchBlockInfo { get; set; } = epochSwitchCurrentBlockInfo; + public BlockRoundInfo? EpochSwitchParentBlockInfo { get; set; } } diff --git a/src/Nethermind/Nethermind.Xdc/Types/Timeout.cs b/src/Nethermind/Nethermind.Xdc/Types/Timeout.cs index 218b7735ba95..80f62476be9f 100644 --- a/src/Nethermind/Nethermind.Xdc/Types/Timeout.cs +++ b/src/Nethermind/Nethermind.Xdc/Types/Timeout.cs @@ -9,15 +9,13 @@ namespace Nethermind.Xdc.Types; -public class Timeout(ulong round, Signature? signature, ulong gapNumber) +public class Timeout(ulong round, Signature? signature, ulong gapNumber) : IXdcPoolItem { - private Address signer; - + private readonly TimeoutDecoder _decoder = new(); public ulong Round { get; set; } = round; public Signature? Signature { get; set; } = signature; public ulong GapNumber { get; set; } = gapNumber; - + public Address? Signer { get; set; } public override string ToString() => $"{Round}:{GapNumber}"; - - public void SetSigner(Address signer) => this.signer = signer; + public (ulong Round, Hash256 hash) PoolKey() => (Round, Keccak.Compute(_decoder.Encode(this, RlpBehaviors.ForSealing).Bytes)); } diff --git a/src/Nethermind/Nethermind.Xdc/Types/TimeoutCertificate.cs b/src/Nethermind/Nethermind.Xdc/Types/TimeoutCertificate.cs index f60eb302c7e1..9c9cebf2fe49 100644 --- a/src/Nethermind/Nethermind.Xdc/Types/TimeoutCertificate.cs +++ b/src/Nethermind/Nethermind.Xdc/Types/TimeoutCertificate.cs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core.Crypto; -using Nethermind.Serialization.Rlp; -using Nethermind.Xdc.RLP; namespace Nethermind.Xdc.Types; diff --git a/src/Nethermind/Nethermind.Xdc/Types/Vote.cs b/src/Nethermind/Nethermind.Xdc/Types/Vote.cs index f43bec3d0eca..cd92e37e982d 100644 --- a/src/Nethermind/Nethermind.Xdc/Types/Vote.cs +++ b/src/Nethermind/Nethermind.Xdc/Types/Vote.cs @@ -3,16 +3,20 @@ using Nethermind.Core; using Nethermind.Core.Crypto; -using System.Text.Json.Serialization; +using RlpBehaviors = Nethermind.Serialization.Rlp.RlpBehaviors; namespace Nethermind.Xdc.Types; -public class Vote(BlockRoundInfo proposedBlockInfo, ulong gapNumber, Signature signature = null) +public class Vote(BlockRoundInfo proposedBlockInfo, ulong gapNumber, Signature signature = null) : IXdcPoolItem { + private readonly VoteDecoder _decoder = new(); public BlockRoundInfo ProposedBlockInfo { get; set; } = proposedBlockInfo; public ulong GapNumber { get; set; } = gapNumber; public Signature? Signature { get; set; } = signature; + public Address? Signer { get; set; } public override string ToString() => $"{ProposedBlockInfo.Round}:{GapNumber}:{ProposedBlockInfo.BlockNumber}"; + + public (ulong Round, Hash256 hash) PoolKey() => (ProposedBlockInfo.Round, Keccak.Compute(_decoder.Encode(this, RlpBehaviors.ForSealing).Bytes)); } diff --git a/src/Nethermind/Nethermind.Xdc/Types/VoteForSign.cs b/src/Nethermind/Nethermind.Xdc/Types/VoteForSign.cs deleted file mode 100644 index a8b7e1a075f9..000000000000 --- a/src/Nethermind/Nethermind.Xdc/Types/VoteForSign.cs +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core.Crypto; -using Nethermind.Serialization.Rlp; - -namespace Nethermind.Xdc.Types; - -public class VoteForSign(BlockRoundInfo proposedBlockInfo, long gapNumber) -{ - public BlockRoundInfo ProposedBlockInfo { get; set; } = proposedBlockInfo; - public long GapNumber { get; set; } = gapNumber; - public Hash256 SigHash() => Keccak.Compute(Rlp.Encode(this).Bytes); -} diff --git a/src/Nethermind/Nethermind.Xdc/VotesManager.cs b/src/Nethermind/Nethermind.Xdc/VotesManager.cs new file mode 100644 index 000000000000..4988b2753117 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/VotesManager.cs @@ -0,0 +1,241 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain; +using Nethermind.Consensus; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Crypto; +using Nethermind.Serialization.Rlp; +using Nethermind.Xdc.Spec; +using Nethermind.Xdc.Types; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Nethermind.Xdc; + +internal class VotesManager( + XdcContext context, + IBlockTree tree, + IEpochSwitchManager epochSwitchManager, + ISnapshotManager snapshotManager, + IQuorumCertificateManager quorumCertificateManager, + ISpecProvider specProvider, + ISigner signer, + IForensicsProcessor forensicsProcessor, + IBlockInfoValidator blockInfoValidator) : IVotesManager +{ + private IBlockTree _tree = tree; + private IEpochSwitchManager _epochSwitchManager = epochSwitchManager; + private ISnapshotManager _snapshotManager = snapshotManager; + private IQuorumCertificateManager _quorumCertificateManager = quorumCertificateManager; + private XdcContext _ctx = context; + private IForensicsProcessor _forensicsProcessor = forensicsProcessor; + private ISpecProvider _specProvider = specProvider; + private ISigner _signer = signer; + private IBlockInfoValidator _blockInfoValidator = blockInfoValidator; + + private XdcPool _votePool = new(); + private static VoteDecoder _voteDecoder = new(); + private static EthereumEcdsa _ethereumEcdsa = new(0); + private readonly ConcurrentDictionary _qcBuildStartedByRound = new(); + private const int _maxBlockDistance = 7; // Maximum allowed backward distance from the chain head + + public Task CastVote(BlockRoundInfo blockInfo) + { + EpochSwitchInfo epochSwitchInfo = _epochSwitchManager.GetEpochSwitchInfo(blockInfo.Hash); + if (epochSwitchInfo is null) + throw new ArgumentException($"Cannot find epoch info for block {blockInfo.Hash}", nameof(EpochSwitchInfo)); + //Optimize this by fetching with block number and round only + + var header = _tree.FindHeader(blockInfo.Hash) as XdcBlockHeader; + IXdcReleaseSpec spec = _specProvider.GetXdcSpec(header, blockInfo.Round); + long epochSwitchNumber = epochSwitchInfo.EpochSwitchBlockInfo.BlockNumber; + long gapNumber = Math.Max(0, epochSwitchNumber - epochSwitchNumber % spec.EpochLength - spec.Gap); + + var vote = new Vote(blockInfo, (ulong)gapNumber); + // Sets signature and signer for the vote + Sign(vote); + + _ctx.HighestVotedRound = blockInfo.Round; + + HandleVote(vote); + //TODO Broadcast vote to peers + return Task.CompletedTask; + } + + public Task HandleVote(Vote vote) + { + if ((vote.ProposedBlockInfo.Round != _ctx.CurrentRound) && (vote.ProposedBlockInfo.Round != _ctx.CurrentRound + 1)) + { + //We only care about votes for the current round or the next round + return Task.CompletedTask; + } + + // Collect votes + _votePool.Add(vote); + IReadOnlyCollection roundVotes = _votePool.GetItems(vote); + _ = _forensicsProcessor.DetectEquivocationInVotePool(vote, roundVotes); + _ = _forensicsProcessor.ProcessVoteEquivocation(vote); + + //TODO Optimize this by fetching with block number and round only + XdcBlockHeader proposedHeader = _tree.FindHeader(vote.ProposedBlockInfo.Hash, vote.ProposedBlockInfo.BlockNumber) as XdcBlockHeader; + if (proposedHeader is null) + { + //This is a vote for a block we have not seen yet, just return for now + return Task.CompletedTask; + } + + EpochSwitchInfo epochInfo = _epochSwitchManager.GetEpochSwitchInfo(proposedHeader); + if (epochInfo is null) + { + //Unknown epoch switch info, cannot process vote + return Task.CompletedTask; + } + if (epochInfo.Masternodes.Length == 0) + { + throw new InvalidOperationException($"Epoch has empty master node list for {vote.ProposedBlockInfo.Hash}"); + } + + double certThreshold = _specProvider.GetXdcSpec(proposedHeader, vote.ProposedBlockInfo.Round).CertThreshold; + bool thresholdReached = roundVotes.Count >= epochInfo.Masternodes.Length * certThreshold; + if (thresholdReached) + { + if (!_blockInfoValidator.ValidateBlockInfo(vote.ProposedBlockInfo, proposedHeader)) + return Task.CompletedTask; + + Signature[] validSignatures = GetValidSignatures(roundVotes, epochInfo.Masternodes); + if (validSignatures.Length < epochInfo.Masternodes.Length * certThreshold) + return Task.CompletedTask; + + // At this point, the QC should be processed for this *round*. + // Ensure this runs only once per round: + var round = vote.ProposedBlockInfo.Round; + if (!_qcBuildStartedByRound.TryAdd(round, 0)) + return Task.CompletedTask; + OnVotePoolThresholdReached(validSignatures, vote); + } + return Task.CompletedTask; + } + + public void EndRound(ulong round) + { + _votePool.EndRound(round); + + foreach (var key in _qcBuildStartedByRound.Keys) + if (key <= round) _qcBuildStartedByRound.TryRemove(key, out _); + } + + public bool VerifyVotingRules(BlockRoundInfo blockInfo, QuorumCertificate qc) + { + if (_ctx.CurrentRound <= _ctx.HighestVotedRound) + { + return false; + } + + if (blockInfo.Round != _ctx.CurrentRound) + { + return false; + } + + if (_ctx.LockQC is null) + { + return true; + } + + if (qc.ProposedBlockInfo.Round > _ctx.LockQC.ProposedBlockInfo.Round) + { + return true; + } + + if (!IsExtendingFromAncestor(blockInfo, _ctx.LockQC.ProposedBlockInfo)) + { + return false; + } + + return true; + } + + public Task OnReceiveVote(Vote vote) + { + var voteBlockNumber = vote.ProposedBlockInfo.BlockNumber; + var currentBlockNumber = _tree.Head?.Number ?? throw new InvalidOperationException("Failed to get current block number"); + if (Math.Abs(voteBlockNumber - currentBlockNumber) > _maxBlockDistance) + { + // Discarded propagated vote, too far away + return Task.CompletedTask; + } + + if (FilterVote(vote)) + { + //TODO: Broadcast Vote + return HandleVote(vote); + } + return Task.CompletedTask; + } + + private bool FilterVote(Vote vote) + { + if (vote.ProposedBlockInfo.Round < _ctx.CurrentRound) return false; + + Snapshot snapshot = _snapshotManager.GetSnapshotByGapNumber(_tree, vote.GapNumber); + if (snapshot is null) throw new InvalidOperationException($"Failed to get snapshot by gapNumber={vote.GapNumber}"); + // Verify message signature + vote.Signer ??= _ethereumEcdsa.RecoverVoteSigner(vote); + return snapshot.NextEpochCandidates.Any(x => x == vote.Signer); + } + + private void OnVotePoolThresholdReached(Signature[] validSignatures, Vote currVote) + { + QuorumCertificate qc = new(currVote.ProposedBlockInfo, validSignatures, currVote.GapNumber); + _quorumCertificateManager.CommitCertificate(qc); + } + + private bool IsExtendingFromAncestor(BlockRoundInfo currentBlockInfo, BlockRoundInfo ancestorBlockInfo) + { + long blockNumDiff = currentBlockInfo.BlockNumber - ancestorBlockInfo.BlockNumber; + var nextBlockHash = currentBlockInfo.Hash; + + for (int i = 0; i < blockNumDiff; i++) + { + XdcBlockHeader parentHeader = _tree.FindHeader(nextBlockHash) as XdcBlockHeader; + if (parentHeader is null) + return false; + + nextBlockHash = parentHeader.ParentHash; + } + + return nextBlockHash == ancestorBlockInfo.Hash; + } + + private Signature[] GetValidSignatures(IEnumerable votes, Address[] masternodes) + { + var masternodeSet = new HashSet
(masternodes); + var signatures = new List(); + foreach (var vote in votes) + { + if (vote.Signer is null) + { + vote.Signer = _ethereumEcdsa.RecoverVoteSigner(vote); + } + + if (masternodeSet.Contains(vote.Signer)) + { + signatures.Add(vote.Signature); + } + } + return signatures.ToArray(); + } + + private void Sign(Vote vote) + { + KeccakRlpStream stream = new(); + _voteDecoder.Encode(stream, vote, RlpBehaviors.ForSealing); + vote.Signature = _signer.Sign(stream.GetValueHash()); + vote.Signer = _signer.Address; + } +} diff --git a/src/Nethermind/Nethermind.Xdc/XdcBlockHeader.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockHeader.cs index 568d71344fd9..e3c91cc92654 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcBlockHeader.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockHeader.cs @@ -44,7 +44,7 @@ public ImmutableArray
? ValidatorsAddress { if (_validatorsAddress is not null) return _validatorsAddress; - _validatorsAddress = ExtractAddresses(Validators); + _validatorsAddress = XdcExtensions.ExtractAddresses(Validators); return _validatorsAddress; } set { _validatorsAddress = value; } @@ -59,27 +59,15 @@ public ImmutableArray
? PenaltiesAddress { if (_penaltiesAddress is not null) return _penaltiesAddress; - _penaltiesAddress = ExtractAddresses(Penalties); + _penaltiesAddress = XdcExtensions.ExtractAddresses(Penalties); return _penaltiesAddress; } set { _penaltiesAddress = value; } } - internal Address[] GetMasterNodesFromEpochSwitchHeader() - { - if (Validators == null) - throw new InvalidOperationException("Header has no validators."); - Address[] masterNodes = new Address[Validators.Length / 20]; - for (int i = 0; i < masterNodes.Length; i++) - { - masterNodes[i] = new Address(Validators.AsSpan(i * 20, 20)); - } - return masterNodes; - } - private ExtraFieldsV2 _extraFieldsV2; /// - /// Consensus data that must be included in a V2 block, which contains the quorum certificate and round information. + /// Consensus data that must be included in a V2 block, which contains the quorum certificate and round information. /// public ExtraFieldsV2? ExtraConsensusData { @@ -91,7 +79,7 @@ public ExtraFieldsV2? ExtraConsensusData if (_extraFieldsV2 == null) { //Check V2 consensus version in ExtraData field. - if (ExtraData.Length < 3 || ExtraData[0] != 2) + if (ExtraData.Length < 3 || ExtraData[0] != XdcConstants.ConsensusVersion) return null; Rlp.ValueDecoderContext valueDecoderContext = new Rlp.ValueDecoderContext(ExtraData.AsSpan(1)); _extraFieldsV2 = _extraConsensusDataDecoder.Decode(ref valueDecoderContext); @@ -101,37 +89,6 @@ public ExtraFieldsV2? ExtraConsensusData set { _extraFieldsV2 = value; } } - public bool IsEpochSwitch(IXdcReleaseSpec spec) - { - if (spec.SwitchBlock == this.Number) - { - return true; - } - ExtraFieldsV2? extraFields = ExtraConsensusData; - if (extraFields is null) - { - //Should this throw instead? - return false; - } - ulong parentRound = extraFields.QuorumCert.ProposedBlockInfo.Round; - ulong epochStart = extraFields.CurrentRound - extraFields.CurrentRound % (ulong)spec.EpochLength; - - return parentRound < epochStart; - } - - private static ImmutableArray
? ExtractAddresses(byte[]? data) - { - if (data is null || data.Length % Address.Size != 0) - return null; - - Address[] addresses = new Address[data.Length / Address.Size]; - for (int i = 0; i < addresses.Length; i++) - { - addresses[i] = new Address(data.AsSpan(i * Address.Size, Address.Size)); - } - return addresses.ToImmutableArray(); - } - public ValueHash256 CalculateHash() { KeccakRlpStream rlpStream = new KeccakRlpStream(); diff --git a/src/Nethermind/Nethermind.Xdc/XdcBlockProducer.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockProducer.cs new file mode 100644 index 000000000000..eefdcb9f9be1 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockProducer.cs @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain; +using Nethermind.Config; +using Nethermind.Consensus; +using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Producers; +using Nethermind.Consensus.Transactions; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Evm.State; +using Nethermind.Evm.Tracing; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Xdc.RLP; +using Nethermind.Xdc.Spec; +using Nethermind.Xdc.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Nethermind.Xdc; +internal class XdcBlockProducer : BlockProducerBase +{ + private readonly IEpochSwitchManager epochSwitchManager; + private readonly ISnapshotManager snapshotManager; + private readonly XdcContext xdcContext; + private readonly ISealer sealer; + private readonly ISpecProvider specProvider; + private readonly ILogManager logManager; + private static readonly ExtraConsensusDataDecoder _extraConsensusDataDecoder = new(); + + public XdcBlockProducer(IEpochSwitchManager epochSwitchManager, ISnapshotManager snapshotManager, XdcContext xdcContext, ITxSource txSource, IBlockchainProcessor processor, ISealer sealer, IBlockTree blockTree, IWorldState stateProvider, IGasLimitCalculator? gasLimitCalculator, ITimestamper? timestamper, ISpecProvider specProvider, ILogManager logManager, IDifficultyCalculator? difficultyCalculator, IBlocksConfig? blocksConfig) : base(txSource, processor, sealer, blockTree, stateProvider, gasLimitCalculator, timestamper, specProvider, logManager, difficultyCalculator, blocksConfig) + { + this.epochSwitchManager = epochSwitchManager; + this.snapshotManager = snapshotManager; + this.xdcContext = xdcContext; + this.sealer = sealer; + this.specProvider = specProvider; + this.logManager = logManager; + } + + protected override BlockHeader PrepareBlockHeader(BlockHeader parent, PayloadAttributes payloadAttributes) + { + if (parent is not XdcBlockHeader xdcParent) + throw new ArgumentException("Only XDC header are supported."); + + QuorumCertificate highestCert = xdcContext.HighestQC; + var currentRound = xdcContext.CurrentRound; + + //TODO maybe some sanity checks here for round and hash + + byte[] extra = [XdcConstants.ConsensusVersion, .. _extraConsensusDataDecoder.Encode(new ExtraFieldsV2(currentRound, highestCert)).Bytes]; + + Address blockAuthor = sealer.Address; + XdcBlockHeader xdcBlockHeader = new( + parent.Hash!, + Keccak.OfAnEmptySequenceRlp, + blockAuthor, + UInt256.Zero, + parent.Number + 1, + //This should probably use TargetAdjustedGasLimitCalculator + XdcConstants.TargetGasLimit, + 0, + extra) + { + Author = blockAuthor, + }; + + IXdcReleaseSpec spec = specProvider.GetXdcSpec(xdcBlockHeader, currentRound); + + xdcBlockHeader.Timestamp = payloadAttributes?.Timestamp ?? parent.Timestamp + (ulong)spec.MinePeriod; + + xdcBlockHeader.Difficulty = 1; + xdcBlockHeader.TotalDifficulty = 1; + + xdcBlockHeader.BaseFeePerGas = BaseFeeCalculator.Calculate(parent, spec); + + xdcBlockHeader.MixHash = Hash256.Zero; + + if (epochSwitchManager.IsEpochSwitchAtBlock(xdcBlockHeader)) + { + (Address[] masternodes, Address[] penalties) = snapshotManager.CalculateNextEpochMasternodes(xdcBlockHeader, spec); + + xdcBlockHeader.Validators = new byte[masternodes.Length * Address.Size]; + + for (int i = 0; i < masternodes.Length; i++) + { + Array.Copy(masternodes[i].Bytes, 0, xdcBlockHeader.Validators, i * Address.Size, Address.Size); + } + + xdcBlockHeader.Penalties = new byte[penalties.Length * Address.Size]; + + for (int i = 0; i < penalties.Length; i++) + { + Array.Copy(penalties[i].Bytes, 0, xdcBlockHeader.Penalties, i * Address.Size, Address.Size); + } + } + return xdcBlockHeader; + } +} diff --git a/src/Nethermind/Nethermind.Xdc/XdcBlockStore.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockStore.cs new file mode 100644 index 000000000000..efaedcb083d9 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockStore.cs @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Autofac.Features.AttributeFilters; +using Nethermind.Blockchain.Blocks; +using Nethermind.Blockchain.Headers; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Db; +using Nethermind.Serialization.Rlp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Xdc; +internal class XdcBlockStore([KeyFilter(DbNames.Headers)] IDb blockDb) : BlockStore(blockDb, new XdcHeaderDecoder()) +{ +} diff --git a/src/Nethermind/Nethermind.Xdc/XdcConstants.cs b/src/Nethermind/Nethermind.Xdc/XdcConstants.cs index b12d5aa348f2..a22a5ce1b8a1 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcConstants.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcConstants.cs @@ -3,28 +3,28 @@ using Nethermind.Core.Crypto; using Nethermind.Int256; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Nethermind.Xdc; internal static class XdcConstants { - public static readonly ulong EpochLength = 900UL; // Default number of blocks after which to checkpoint and reset the pending votes + public const ulong EpochLength = 900UL; // Default number of blocks after which to checkpoint and reset the pending votes - public static readonly int ExtraVanity = 32; // Fixed number of extra-data prefix bytes reserved for signer vanity - public static readonly int ExtraSeal = 65; // Fixed number of extra-data suffix bytes reserved for signer seal + public const int ExtraVanity = 32; // Fixed number of extra-data prefix bytes reserved for signer vanity + public const int ExtraSeal = 65; // Fixed number of extra-data suffix bytes reserved for signer seal public const ulong NonceAuthVoteValue = 0xffffffffffffffff; // Magic nonce number to vote on adding a new signer public const ulong NonceDropVoteValue = 0x0000000000000000; // Magic nonce number to vote on removing a signer - public static readonly Hash256 UncleHash = Keccak.OfAnEmptySequenceRlp; // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW - public static readonly ulong InMemoryEpochs = 5 * 900UL; // Number of mapping from block to epoch switch infos to keep in memory + public const int InMemoryEpochs = 5 * 900; // Number of mapping from block to epoch switch infos to keep in memory + + public const int InMemoryRound2Epochs = 65536; // One epoch ~ 0.5h, 65536 epochs ~ 3.7y, ~10MB memory + + public const long TargetGasLimit = 84000000; // XDC default gas limit per block + + public const byte ConsensusVersion = 0x02; - public static readonly int InMemoryRound2Epochs = 65536; // One epoch ~ 0.5h, 65536 epochs ~ 3.7y, ~10MB memory + public const int GasLimitBoundDivisor = 1024; // The bound divisor of gas limit adjustment per block // --- Compile-time constants --- public const int InMemorySnapshots = 128; // Number of recent vote snapshots to keep in memory @@ -33,7 +33,8 @@ internal static class XdcConstants public const int PeriodicJobPeriod = 60; public const int PoolHygieneRound = 10; - - public static UInt256 DifficultyDefault = UInt256.One; public const int InMemorySignatures = 4096; + + public static readonly Hash256 UncleHash = Keccak.OfAnEmptySequenceRlp; // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW + public static readonly UInt256 DifficultyDefault = UInt256.One; } diff --git a/src/Nethermind/Nethermind.Xdc/XdcContext.cs b/src/Nethermind/Nethermind.Xdc/XdcContext.cs index 00ee0cf32a74..9240c35decb3 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcContext.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcContext.cs @@ -6,24 +6,26 @@ using Nethermind.Core.Crypto; using Nethermind.Xdc.Types; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using static Org.BouncyCastle.Asn1.Cmp.Challenge; +using System.Collections.Concurrent; +using BlockInfo = Nethermind.Xdc.Types.BlockRoundInfo; +using Round = ulong; namespace Nethermind.Xdc; + public class XdcContext { + public ConcurrentDictionary Signatures { get; set; } public Address Leader { get; set; } - public int TimeoutCounter { get; set; } - public ulong CurrentRound { get; set; } - public ulong HighestSelfMindeRound { get; set; } - public ulong HighestVotedRound { get; set; } + public int TimeoutCounter { get; set; } = 0; + public Round CurrentRound { get; set; } + public Round HighestSelfMindeRound { get; set; } + public Round HighestVotedRound { get; set; } public QuorumCertificate HighestQC { get; set; } public QuorumCertificate LockQC { get; set; } public TimeoutCertificate HighestTC { get; set; } - public BlockRoundInfo HighestCommitBlock { get; set; } + public BlockInfo HighestCommitBlock { get; set; } + public SignFn SignFun { get; set; } + public bool IsInitialized { get; set; } = false; public event Action NewRoundSetEvent; diff --git a/src/Nethermind/Nethermind.Xdc/XdcExtensions.cs b/src/Nethermind/Nethermind.Xdc/XdcExtensions.cs index 8961e3f66775..d703804d01a0 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcExtensions.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcExtensions.cs @@ -7,12 +7,10 @@ using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Serialization.Rlp; -using Nethermind.Xdc; using Nethermind.Xdc.Spec; using Nethermind.Xdc.Types; using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Immutable; namespace Nethermind.Xdc; public static class XdcExtensions @@ -68,7 +66,16 @@ public static IXdcReleaseSpec GetXdcSpec(this ISpecProvider specProvider, XdcBlo return snapshotManager.GetSnapshot(gapBlockHash); } + public static ImmutableArray
? ExtractAddresses(this Span data) + { + if (data.Length % Address.Size != 0) + return null; - - + Address[] addresses = new Address[data.Length / Address.Size]; + for (int i = 0; i < addresses.Length; i++) + { + addresses[i] = new Address(data.Slice(i * Address.Size, Address.Size)); + } + return addresses.ToImmutableArray(); + } } diff --git a/src/Nethermind/Nethermind.Xdc/XdcHeaderStore.cs b/src/Nethermind/Nethermind.Xdc/XdcHeaderStore.cs new file mode 100644 index 000000000000..b6e4e087d24b --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcHeaderStore.cs @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Autofac.Features.AttributeFilters; +using Nethermind.Blockchain.Headers; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Db; +using Nethermind.Serialization.Rlp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Xdc; +internal class XdcHeaderStore([KeyFilter(DbNames.Headers)] IDb headerDb, [KeyFilter(DbNames.BlockNumbers)] IDb blockNumberDb) : HeaderStore(headerDb, blockNumberDb, new XdcHeaderDecoder()), IXdcHeaderStore +{ +} diff --git a/src/Nethermind/Nethermind.Xdc/XdcHeaderValidator.cs b/src/Nethermind/Nethermind.Xdc/XdcHeaderValidator.cs index a27f13914255..d5ed9f1c5cc8 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcHeaderValidator.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcHeaderValidator.cs @@ -66,7 +66,7 @@ protected override bool Validate(BlockHeader header, BlockHeader? par protected override bool ValidateSeal(BlockHeader header, BlockHeader parent, bool isUncle, ref string? error) { if (_sealValidator is XdcSealValidator xdcSealValidator) - return xdcSealValidator.ValidateParams(header, parent, out error); + return xdcSealValidator.ValidateParams(parent, header, out error); if (!_sealValidator.ValidateParams(parent, header, isUncle)) { diff --git a/src/Nethermind/Nethermind.Xdc/XdcPool.cs b/src/Nethermind/Nethermind.Xdc/XdcPool.cs new file mode 100644 index 000000000000..a30a0878e74a --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcPool.cs @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Threading; + +namespace Nethermind.Xdc; + +public class XdcPool where T : IXdcPoolItem +{ + private readonly Dictionary<(ulong Round, Hash256 Hash), ArrayPoolList> _items = new(); + private readonly McsLock _lock = new(); + + public long Add(T item) + { + using var lockRelease = _lock.Acquire(); + { + var key = item.PoolKey(); + if (!_items.TryGetValue(key, out var list)) + { + //128 should be enough to cover all master nodes and some extras + list = new ArrayPoolList(128); + _items[key] = list; + } + if (!list.Contains(item)) + list.Add(item); + return list.Count; + } + } + + public void EndRound(ulong round) + { + using var lockRelease = _lock.Acquire(); + { + foreach (var key in _items.Keys) + { + if (key.Round <= round && _items.Remove(key, out ArrayPoolList list)) + { + list?.Dispose(); + } + } + } + } + + public IReadOnlyCollection GetItems(T item) + { + using var lockRelease = _lock.Acquire(); + { + var key = item.PoolKey(); + if (_items.TryGetValue(key, out ArrayPoolList list)) + { + //Allocating a new array since it goes outside the lock + return list.ToArray(); + } + return []; + } + } + + public long GetCount(T item) + { + using var lockRelease = _lock.Acquire(); + { + var key = item.PoolKey(); + if (_items.TryGetValue(key, out ArrayPoolList list)) + { + return list.Count; + } + return 0; + } + } +} + +public interface IXdcPoolItem +{ + (ulong Round, Hash256 hash) PoolKey(); +} diff --git a/src/Nethermind/Nethermind.Xdc/XdcSealValidator.cs b/src/Nethermind/Nethermind.Xdc/XdcSealValidator.cs index f39a2f880460..629b60addfab 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcSealValidator.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcSealValidator.cs @@ -29,6 +29,8 @@ public bool ValidateParams(BlockHeader parent, BlockHeader header, bool isUncle return ValidateParams(parent, header, out _); } + + public bool ValidateParams(BlockHeader parent, BlockHeader header, out string error) { if (header is not XdcBlockHeader xdcHeader) @@ -53,7 +55,7 @@ public bool ValidateParams(BlockHeader parent, BlockHeader header, out string er Address[] masternodes; - if (xdcHeader.IsEpochSwitch(xdcSpec)) + if (epochSwitchManager.IsEpochSwitchAtBlock(xdcHeader)) { if (xdcHeader.Nonce != XdcConstants.NonceDropVoteValue) { @@ -87,18 +89,20 @@ public bool ValidateParams(BlockHeader parent, BlockHeader header, out string er } else { - if (xdcHeader.Validators?.Length != 0) + if (xdcHeader.Validators is not null && + xdcHeader.Validators.Length != 0) { error = "Validators are not empty in non-epoch switch header."; return false; } - if (xdcHeader.Penalties?.Length != 0) + if (xdcHeader.Penalties is not null && + xdcHeader.Penalties?.Length != 0) { error = "Penalties are not empty in non-epoch switch header."; return false; } //TODO get masternodes from snapshot - EpochSwitchInfo epochSwitchInfo = epochSwitchManager.GetEpochSwitchInfo(xdcHeader, xdcHeader.ParentHash); + EpochSwitchInfo epochSwitchInfo = epochSwitchManager.GetEpochSwitchInfo(xdcHeader); masternodes = epochSwitchInfo.Masternodes; if (masternodes is null || masternodes.Length == 0) throw new InvalidOperationException($"Snap shot returned no master nodes for header \n{xdcHeader.ToString()}"); diff --git a/src/Nethermind/Nethermind.Xdc/XdcSealer.cs b/src/Nethermind/Nethermind.Xdc/XdcSealer.cs new file mode 100644 index 000000000000..49ce730e2459 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcSealer.cs @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Consensus; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Crypto; +using Nethermind.Serialization.Rlp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Nethermind.Xdc; +internal class XdcSealer(ISigner signer) : ISealer +{ + private static XdcHeaderDecoder _xdcHeaderDecoder = new XdcHeaderDecoder(); + public Address Address => signer.Address; + + public bool CanSeal(long blockNumber, Hash256 parentHash) + { + //We might want to add more logic here in the future + return true; + } + + public Task SealBlock(Block block, CancellationToken cancellationToken) + { + if (block.Header is not XdcBlockHeader xdcBlockHeader) + throw new ArgumentException("Only XDC headers are supported."); + if (block.IsGenesis) throw new InvalidOperationException("Can't sign genesis block"); + + KeccakRlpStream hashStream = new KeccakRlpStream(); + _xdcHeaderDecoder.Encode(hashStream, xdcBlockHeader, RlpBehaviors.ForSealing); + xdcBlockHeader.Validator = signer.Sign(hashStream.GetValueHash()).BytesWithRecovery; + + xdcBlockHeader.Hash = xdcBlockHeader.CalculateHash().ToHash256(); + return Task.FromResult(block); + } +} diff --git a/src/Nethermind/Nethermind.sln.DotSettings b/src/Nethermind/Nethermind.sln.DotSettings index ac13d17e4579..291c030deaa0 100644 --- a/src/Nethermind/Nethermind.sln.DotSettings +++ b/src/Nethermind/Nethermind.sln.DotSettings @@ -1,2 +1,3 @@  + True True \ No newline at end of file diff --git a/src/Nethermind/Nethermind.slnx b/src/Nethermind/Nethermind.slnx index 75fe0943fc99..793a8a8b2639 100644 --- a/src/Nethermind/Nethermind.slnx +++ b/src/Nethermind/Nethermind.slnx @@ -66,7 +66,6 @@ - diff --git a/tools/DocGen/DBSizeGenerator.cs b/tools/DocGen/DBSizeGenerator.cs index f78ce87ae201..db8c77ecc544 100644 --- a/tools/DocGen/DBSizeGenerator.cs +++ b/tools/DocGen/DBSizeGenerator.cs @@ -28,7 +28,6 @@ internal static void Generate(string docsPath, string? dbSizeSourcePath) [ "mainnet", "sepolia", - "holesky", "gnosis", "chiado", "energyweb", diff --git a/tools/Evm/T8n/T8nExecutor.cs b/tools/Evm/T8n/T8nExecutor.cs index fb27cbe1ca21..72935f0e7674 100644 --- a/tools/Evm/T8n/T8nExecutor.cs +++ b/tools/Evm/T8n/T8nExecutor.cs @@ -116,9 +116,9 @@ public static T8nExecutionResult Execute(T8nCommandArguments arguments) blockReceiptsTracer.LastReceipt.BlockNumber = 0; transactionExecutionReport.SuccessfulTransactionReceipts.Add(blockReceiptsTracer.LastReceipt); } - else if (transactionResult.Error is not null && transaction.SenderAddress is not null) + else if (!transactionResult.TransactionExecuted && transaction.SenderAddress is not null) { - var error = GethErrorMappings.GetErrorMapping(transactionResult.Error, + var error = GethErrorMappings.GetErrorMapping(transactionResult.ErrorDescription, transaction.SenderAddress.ToString(true), transaction.Nonce, stateProvider.GetNonce(transaction.SenderAddress)); diff --git a/tools/Kute/Nethermind.Tools.Kute/Metrics/PrometheusPushGatewayMetricsReporter.cs b/tools/Kute/Nethermind.Tools.Kute/Metrics/PrometheusPushGatewayMetricsReporter.cs index 4fe42ad367a2..fedfcc96ea3d 100644 --- a/tools/Kute/Nethermind.Tools.Kute/Metrics/PrometheusPushGatewayMetricsReporter.cs +++ b/tools/Kute/Nethermind.Tools.Kute/Metrics/PrometheusPushGatewayMetricsReporter.cs @@ -119,7 +119,7 @@ public async Task Total(TimeSpan elapsed, CancellationToken token = default) private static string GetMetricName(string name) { - var lowerName = name.ToLower(); + var lowerName = name.ToLowerInvariant(); var sanitizedName = lowerName.Replace(" ", "_").Replace("-", "_"); return $"{JobName}_{sanitizedName}"; } diff --git a/tools/SendBlobs/Dockerfile b/tools/SendBlobs/Dockerfile index 9ef638c47053..45aaef59a838 100644 --- a/tools/SendBlobs/Dockerfile +++ b/tools/SendBlobs/Dockerfile @@ -9,17 +9,22 @@ ARG COMMIT_HASH ARG TARGETARCH ARG TARGETOS -COPY . . +COPY ./src/Nethermind ./src/Nethermind +COPY ./tools/SendBlobs ./tools/SendBlobs +COPY ./Directory.*.props . +COPY ./global.json ./global.json +COPY ./nuget.config ./nuget.config +COPY ./tools/Directory.Build.props ./tools/Directory.Build.props RUN arch=$([ "$TARGETARCH" = "amd64" ] && echo "x64" || echo "$TARGETARCH") && \ - dotnet publish tools/SendBlobs -c $BUILD_CONFIG -r $TARGETOS-$arch -o out --sc true \ - -p:BuildTimestamp=$BUILD_TIMESTAMP -p:Commit=$COMMIT_HASH + dotnet publish tools/SendBlobs/SendBlobs.csproj -c $BUILD_CONFIG -r $TARGETOS-$arch -o /out --sc true \ + -p:DebugType=None \ + -p:DebugSymbols=false -FROM --platform=$TARGETPLATFORM ubuntu +FROM mcr.microsoft.com/dotnet/runtime-deps:9.0-noble WORKDIR /nethermind -COPY --from=build /out/SendBlobs . +COPY --from=build /out ./ -RUN apt update && apt-get install -y ca-certificates ENTRYPOINT ["./SendBlobs"] diff --git a/tools/SendBlobs/README.md b/tools/SendBlobs/README.md index ebd6d41899fd..b77291e350da 100644 --- a/tools/SendBlobs/README.md +++ b/tools/SendBlobs/README.md @@ -9,51 +9,37 @@ docker run nethermindeth/send-blobs:latest --rpcurl http://localhost:8545 --blob ## Usage + +The tool can help with: + +- blob spamming with random data, from multiple accounts +- sending files as blobs, +- batch funds distribution + +Use "SendBlobs [command] --help" for more information about supported commands. + +The default fork for now is Prague, which means blob will be sent with V0 proofs. Use `--fork Osaka` option to change it to V1. The default behavior may change post Osaka. + +## Build + +```sh +apt install libsnappy-dev dotnet-sdk-9.0 -y +cd ./nethermind/tools/SendBlobs +dotnet publish --sc -o . +./SendBlobs +``` + +or via docker + +```sh +cd ./nethermind/ # repository root +docker build . -f ./tools/SendBlobs/Dockerfile -t send-blobs +docker run send-blobs ``` -Usage: SendBlobs [options] [command] - -Options: - --help Show help information - --rpcurl Url of the Json RPC. - --bloboptions Options in format '10x1-2', '2x5-5' etc. for the blobs. - --privatekey The key to use for sending blobs. - --keyfile File containing private keys that each blob tx will be send from. - --receiveraddress Receiver address of the blobs. - --maxfeeperblobgas (Optional) Set the maximum fee per blob data. - --feemultiplier (Optional) A multiplier to use for gas fees. - --maxpriorityfee (Optional) The maximum priority fee for each transaction. - --fork (Optional) Fork rules: Cancun/Prague/Osaka - -Commands: - distribute Distribute funds from an address to a number of new addresses. - reclaim Reclaim funds distributed from the 'distribute' command. - - -Use "SendBlobs [command] --help" for more information about a command. - -Usage: SendBlobs __distribute__ [options] - -Options: - --help Show help information - --rpcurl Url of the Json RPC. - --privatekey The private key to distribute funds from. - --number The number of new addresses/keys to make. - --keyfile File where the newly generated keys are written. - --maxpriorityfee (Optional) The maximum priority fee for each transaction. - --maxfee (Optional) The maxFeePerGas fee paid for each transaction. - - -Usage: SendBlobs __reclaim__ [options] - -Options: - --help Show help information - --rpcurl Url of the Json RPC. - --receiveraddress The address to send the funds to. - --keyfile File of the private keys to reclaim from. - --maxpriorityfee (Optional) The maximum priority fee for each transaction. - --maxfee (Optional) The maxFeePerGas paid for each transaction. - -sh + +### Examples + +```sh ./SendBlobs --rpcurl http://localhost:8545 # url-that-does-not-require-auth-in-header --bloboptions 1000,5x6,100x2 # transaction count: just a number or a list of tx-count x blob-count --privatekey 0x0000..0000 # secret-key @@ -62,9 +48,12 @@ sh --feemultiplier 4 # fee multiplier to compete with other txs in the pool, 4 by default # send 5 transactions, 1 blob each -./SendBlobs --rpcurl http://localhost:8545 --bloboptions 5 \ - 0x0000000000000000000000000000000000000000000000000000000000000000 \ - 0x000000000000000000000000000000000000f1c1 10000 4 +./SendBlobs --rpcurl http://localhost:8545 \ + --bloboptions 5 \ + --privatekey 0x0000000000000000000000000000000000000000000000000000000000000000 \ + --receiveraddress 0x000000000000000000000000000000000000f1c1 \ + --maxfeeperblobgas 10000 \ + --feemultiplier 4 # send several transactions with 1 blob, with 6 blobs and than with 2 blobs ./SendBlobs --rpcurl http://localhost:8545 @@ -74,19 +63,21 @@ sh --maxfeeperblobgas 10000 \ --feemultiplier 4 -#send a couple of transactions +# send a couple of broken transactions ./SendBlobs --rpcurl http://localhost:8545 \ - --bloboptions 2x4-1 \ + --bloboptions 2x4-2 \ --privatekey 0x0000000000000000000000000000000000000000000000000000000000000000 \ --receiveraddress 0x0000000000000000000000000000000000000001 \ --maxfeeperblobgas 10000 \ --feemultiplier 4 ``` + ## Blob options -``` -< + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/SendBlobs/SetupCli.cs b/tools/SendBlobs/SetupCli.cs index d24f04ef84e3..4b68a8557ecd 100644 --- a/tools/SendBlobs/SetupCli.cs +++ b/tools/SendBlobs/SetupCli.cs @@ -1,9 +1,8 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Consensus; using Nethermind.Crypto; -using Nethermind.Int256; using Nethermind.Logging; using System.CommandLine; using Nethermind.Core.Specs; @@ -13,6 +12,7 @@ using Nethermind.Specs; namespace SendBlobs; + internal static class SetupCli { public static void SetupExecute(RootCommand command) @@ -178,12 +178,12 @@ public static void SetupDistributeCommand(Command root) Description = "File where the newly generated keys are written", HelpName = "path" }; - Option maxPriorityFeeGasOption = new("--maxpriorityfee") + Option maxPriorityFeeGasOption = new("--maxpriorityfee") { Description = "The maximum priority fee for each transaction", HelpName = "fee" }; - Option maxFeeOption = new("--maxfee") + Option maxFeeOption = new("--maxfee") { Description = "The maxFeePerGas fee paid for each transaction", HelpName = "fee" @@ -242,12 +242,12 @@ public static void SetupReclaimCommand(Command root) Description = "File of the private keys to reclaim from", HelpName = "path" }; - Option maxPriorityFeeGasOption = new("--maxpriorityfee") + Option maxPriorityFeeGasOption = new("--maxpriorityfee") { Description = "The maximum priority fee for each transaction", HelpName = "fee" }; - Option maxFeeOption = new("--maxfee") + Option maxFeeOption = new("--maxfee") { Description = "The maxFeePerGas fee paid for each transaction", HelpName = "fee" @@ -312,7 +312,7 @@ public static void SetupSendFileCommand(Command root) HelpName = "address", Required = true, }; - Option maxFeePerBlobGasOption = new("--maxfeeperblobgas") + Option maxFeePerBlobGasOption = new("--maxfeeperblobgas") { DefaultValueFactory = r => 1000, Description = "Set the maximum fee per blob data", @@ -324,7 +324,7 @@ public static void SetupSendFileCommand(Command root) Description = "A multiplier to use for gas fees", HelpName = "value" }; - Option maxPriorityFeeGasOption = new("--maxpriorityfee") + Option maxPriorityFeeGasOption = new("--maxpriorityfee") { Description = "The maximum priority fee for each transaction", HelpName = "fee"