diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml index 210889592d7..5b152ec6ee7 100644 --- a/.github/workflows/publish-packages.yml +++ b/.github/workflows/publish-packages.yml @@ -26,7 +26,7 @@ jobs: echo "Import GPG owner trust" echo ${{ secrets.GPG_OWNERTRUST }} | base64 --decode | gpg --import-ownertrust - name: Install PPA dependencies - run: sudo apt-get update && sudo apt-get install debhelper devscripts -y + run: sudo apt-get update && sudo apt-get install build-essential debhelper devscripts -y - name: Submit package env: PPA_GPG_KEYID: ${{ secrets.PPA_GPG_KEYID }} @@ -65,6 +65,8 @@ jobs: run: | sudo apt-get update sudo apt-get install debhelper devscripts ubuntu-dev-tools -y + - name: Set up Python + uses: actions/setup-python@v5 - name: Install launchpadlib run: pip install launchpadlib --upgrade - name: Copy to other series diff --git a/.github/workflows/sync-supported-chains.yml b/.github/workflows/sync-supported-chains.yml index 3ca8427a262..4e7047b9298 100644 --- a/.github/workflows/sync-supported-chains.yml +++ b/.github/workflows/sync-supported-chains.yml @@ -35,6 +35,7 @@ jobs: needs: [setup-matrix] strategy: fail-fast: false + max-parallel: 5 matrix: config: ${{fromJson(needs.setup-matrix.outputs.matrix)}} runs-on: ubuntu-latest @@ -52,13 +53,14 @@ jobs: linode_token: ${{ secrets.LINODE_TOKEN }} github_token: "${{ secrets.REPOSITORY_DISPATCH_TOKEN }}" action: "create" - runner_label: ${{ matrix.config.network }}-${{ matrix.config.cl }}-${{ github.run_id }} + runner_label: t-${{ github.run_id }}-${{ matrix.config.network }} root_password: ${{ secrets.LINODE_ROOT_PASSWORD }} machine_type: "${{ matrix.config.agent }}" image: "linode/ubuntu24.04" tags: "core, self-hosted, dynamic" organization: "NethermindEth" repo_name: "nethermind" + blocked_ports: "8545,8546,8551,8552" sync-chain: needs: [setup-matrix, create_a_runner] @@ -68,7 +70,7 @@ jobs: config: ${{fromJson(needs.setup-matrix.outputs.matrix)}} name: "Run sync of ${{ matrix.config.network }} chain" - runs-on: ${{ matrix.config.network }}-${{ matrix.config.cl }}-${{ github.run_id }} + runs-on: t-${{ github.run_id }}-${{ matrix.config.network }} timeout-minutes: ${{ matrix.config.timeout }} steps: @@ -102,6 +104,7 @@ jobs: working-directory: sedge run: | docker_image="" + network="${{ matrix.config.network }}" if [ -z "${{ inputs.nethermind_image }}" ]; then REF_NAME=${{ github.ref }} @@ -117,37 +120,89 @@ jobs: echo 'Generating sedge docker...' ./build/sedge deps install - ./build/sedge generate --logging none -p $GITHUB_WORKSPACE/sedge \ - full-node --map-all --no-mev-boost --no-validator --network ${{ matrix.config.network }} \ - -c ${{ matrix.config.cl }}:${{ matrix.config.cl_image }} -e nethermind:$docker_image \ - --el-extra-flag Sync.NonValidatorNode=true --el-extra-flag Sync.DownloadBodiesInFastSync=false \ - --el-extra-flag Sync.DownloadReceiptsInFastSync=false \ - --el-extra-flag JsonRpc.EnabledModules=[Eth,Subscribe,Trace,TxPool,Web3,Personal,Proof,Net,Parity,Health,Rpc,Debug] \ - --el-extra-flag Sync.SnapSync=true \ - --checkpoint-sync-url=${{ matrix.config.checkpoint-sync-url }} + + if [[ "$network" == base-* || "$network" == op-* ]]; then + if [[ "$network" == *mainnet* ]]; then + CONSENSUS_URL="${{ secrets.MAINNET_CONSENSUS_URL }}" + EXECUTION_URL="${{ secrets.MAINNET_EXECUTION_URL }}" + elif [[ "$network" == *sepolia* ]]; then + CONSENSUS_URL="${{ secrets.SEPOLIA_CONSENSUS_URL }}" + EXECUTION_URL="${{ secrets.SEPOLIA_EXECUTION_URL }}" + else + echo "Unknown network" + exit 1 + fi + + if [[ "$network" == base-* ]]; then + extra_param="--base" + fi + + stripped_network="${network#base-}" + stripped_network="${stripped_network#op-}" + + ./build/sedge generate \ + --logging none \ + -p $GITHUB_WORKSPACE/sedge \ + op-full-node \ + --op-execution opnethermind:$docker_image \ + --op-image op-node:us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:latest \ + --map-all \ + --network $stripped_network \ + --consensus-url $CONSENSUS_URL \ + --execution-api-url $EXECUTION_URL \ + $extra_param + else + ./build/sedge generate \ + --logging none \ + -p $GITHUB_WORKSPACE/sedge \ + full-node \ + -c ${{ matrix.config.cl }}:${{ matrix.config.cl_image }} \ + -e nethermind:$docker_image \ + --map-all \ + --no-mev-boost \ + --no-validator \ + --network ${{ matrix.config.network }} \ + --el-extra-flag Sync.NonValidatorNode=true \ + --el-extra-flag Sync.DownloadBodiesInFastSync=false \ + --el-extra-flag Sync.DownloadReceiptsInFastSync=false \ + --el-extra-flag JsonRpc.EnabledModules=[Eth,Subscribe,Trace,TxPool,Web3,Personal,Proof,Net,Parity,Health,Rpc,Debug] \ + --el-extra-flag Sync.SnapSync=true \ + --checkpoint-sync-url=${{ matrix.config.checkpoint-sync-url }} + fi + echo 'Running sedge...' ./build/sedge run -p $GITHUB_WORKSPACE/sedge - name: Wait for ${{ matrix.config.network }} to sync id: wait - timeout-minutes: 180 run: | declare -A bad_logs + declare -A good_logs + declare -A required_count + bad_logs["Corrupt"]=1 bad_logs["Exception"]=1 - - declare -A good_logs - good_logs["Synced Chain Head"]=0 + good_logs["Processed"]=0 - - declare -A required_count - required_count["Synced Chain Head"]=20 - required_count["Processed"]=20 + + required_count["Processed"]=20 + + network="${{ matrix.config.network }}" + if [[ "$network" != "joc-mainnet" && "$network" != "joc-testnet" ]]; then + good_logs["Synced Chain Head"]=0 + required_count["Synced Chain Head"]=20 + fi counter=0 found_bad_log=false - - docker logs -f sedge-execution-client | while read -r line; do + + if [[ "$network" == base-* || "$network" == op-* ]]; then + container_name="sedge-execution-op-l2-client" + else + container_name="sedge-execution-client" + fi + + docker logs -f "$container_name" | while read -r line; do echo "$line" if [[ "$line" == *"All done"* ]]; then @@ -195,9 +250,32 @@ jobs: done - name: Get Consensus Logs - if: always() + if: always() && matrix.config.network != 'joc-mainnet' && matrix.config.network != 'joc-testnet' run: | - docker logs sedge-consensus-client + network="${{ matrix.config.network }}" + if [[ "$network" == base-* || "$network" == op-* ]]; then + docker logs sedge-consensus-op-l2-client + else + docker logs sedge-consensus-client + fi + + - name: Check size of DB + run: | + du -h $GITHUB_WORKSPACE/sedge + + - name: Destroy VM + if: always() + id: run-linode-action + uses: kamilchodola/linode-github-runner/.github/actions/linode-machine-manager@main + with: + linode_token: ${{ secrets.LINODE_TOKEN }} + github_token: "${{ secrets.REPOSITORY_DISPATCH_TOKEN }}" + action: "destroy-machine" + runner_label: t-${{ github.run_id }}-${{ matrix.config.network }} + search_phrase: t-${{ github.run_id }}-${{ matrix.config.network }} + root_password: ${{ secrets.LINODE_ROOT_PASSWORD }} + organization: "NethermindEth" + repo_name: "nethermind" destroy_runner: needs: [setup-matrix, create_a_runner, sync-chain] @@ -208,19 +286,30 @@ jobs: config: ${{fromJson(needs.setup-matrix.outputs.matrix)}} runs-on: ubuntu-latest steps: - - name: Destroy a Runner - id: run-linode-action + - name: Destroy VM (make sure is removed if by any unexpected reason it did not removed it on ) + continue-on-error: true + uses: kamilchodola/linode-github-runner/.github/actions/linode-machine-manager@main + with: + linode_token: ${{ secrets.LINODE_TOKEN }} + github_token: "${{ secrets.REPOSITORY_DISPATCH_TOKEN }}" + action: "destroy-machine" + runner_label: t-${{ github.run_id }}-${{ matrix.config.network }} + search_phrase: t-${{ github.run_id }}-${{ matrix.config.network }} + root_password: ${{ secrets.LINODE_ROOT_PASSWORD }} + organization: "NethermindEth" + repo_name: "nethermind" + + - name: Destroy Runner uses: kamilchodola/linode-github-runner/.github/actions/linode-machine-manager@main with: linode_token: ${{ secrets.LINODE_TOKEN }} github_token: "${{ secrets.REPOSITORY_DISPATCH_TOKEN }}" - action: "destroy" - runner_label: ${{ matrix.config.network }}-${{ matrix.config.cl }}-${{ github.run_id }} - search_phrase: ${{ matrix.config.network }}-${{ matrix.config.cl }}-${{ github.run_id }} + action: "destroy-runner" + runner_label: t-${{ github.run_id }}-${{ matrix.config.network }} + search_phrase: t-${{ github.run_id }}-${{ matrix.config.network }} root_password: ${{ secrets.LINODE_ROOT_PASSWORD }} organization: "NethermindEth" repo_name: "nethermind" - trigger_tests: needs: [setup-matrix, create_a_runner, sync-chain, destroy_runner] if: success() diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml new file mode 100644 index 00000000000..b5c7b5616f1 --- /dev/null +++ b/.github/workflows/trivy.yml @@ -0,0 +1,46 @@ +name: Trivy scanner + +on: + pull_request: + branches: [master] + push: + branches: [master] + schedule: + - cron: '29 19 * * 4' + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: Build + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + actions: read + env: + IMAGE_TAG: nethermind:${{ github.sha }} + steps: + - name: Check out repository + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 #v4.2.1 + + - name: Build Docker image + run: docker build -t $IMAGE_TAG . + + - name: Scan + uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 #v0.28.0 + with: + image-ref: ${{ env.IMAGE_TAG }} + format: template + template: '@/contrib/sarif.tpl' + output: trivy-results.sarif + severity: CRITICAL,HIGH + env: + TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db + + - name: Upload scan results + uses: github/codeql-action/upload-sarif@cf5b0a9041d3c1d336516f1944c96d96598193cc #v2.19.1 + with: + sarif_file: trivy-results.sarif diff --git a/.github/workflows/update-fast-sync.yml b/.github/workflows/update-fast-sync.yml index f3755e04ea1..577cafe9fb6 100644 --- a/.github/workflows/update-fast-sync.yml +++ b/.github/workflows/update-fast-sync.yml @@ -16,10 +16,12 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 - name: Install dependencies run: | - pip3 install setuptools - pip3 install emoji + pip install setuptools + pip install emoji - name: Update config files run: python3 scripts/syncSettings.py ${{ secrets.ETHERSCAN_API_KEY }} - name: Create GitHub app token diff --git a/scripts/workflow_config/sync_testnets_matrix.json b/scripts/workflow_config/sync_testnets_matrix.json index 08a19888e01..758c25f29a7 100644 --- a/scripts/workflow_config/sync_testnets_matrix.json +++ b/scripts/workflow_config/sync_testnets_matrix.json @@ -19,7 +19,7 @@ "network": "sepolia", "cl": "prysm", "cl_image": "prysmaticlabs/prysm-beacon-chain:stable", - "checkpoint-sync-url": "https://beaconstate-sepolia.chainsafe.io", + "checkpoint-sync-url": "https://checkpoint-sync.sepolia.ethpandaops.io/", "timeout": 180, "agent": "g6-standard-6" }, @@ -35,8 +35,56 @@ "network": "mainnet", "cl": "lodestar", "cl_image": "chainsafe/lodestar:latest", - "checkpoint-sync-url": "https://mainnet.checkpoint.sigp.io", + "checkpoint-sync-url": "https://mainnet-checkpoint-sync.attestant.io/", "timeout": 180, "agent": "g6-standard-16" + }, + { + "network": "op-sepolia", + "cl": "", + "cl_image": "", + "checkpoint-sync-url": "", + "timeout": 180, + "agent": "g6-standard-8" + }, + { + "network": "base-sepolia", + "cl": "", + "cl_image": "", + "checkpoint-sync-url": "", + "timeout": 180, + "agent": "g6-standard-8" + }, + { + "network": "op-mainnet", + "cl": "", + "cl_image": "", + "checkpoint-sync-url": "", + "timeout": 600, + "agent": "g6-standard-16" + }, + { + "network": "base-mainnet", + "cl": "", + "cl_image": "", + "checkpoint-sync-url": "", + "timeout": 600, + "agent": "g6-standard-16" + }, + { + "network": "joc-mainnet", + "cl": "", + "cl_image": "", + "checkpoint-sync-url": "", + "timeout": 180, + "agent": "g6-standard-6" + }, + { + "network": "joc-testnet", + "cl": "", + "cl_image": "", + "checkpoint-sync-url": "", + "timeout": 180, + "agent": "g6-standard-6" } ] diff --git a/src/Nethermind/Chains/op-mainnet.json b/src/Nethermind/Chains/op-mainnet.json index ee17aa826f7..7f6a7765ef3 100644 --- a/src/Nethermind/Chains/op-mainnet.json +++ b/src/Nethermind/Chains/op-mainnet.json @@ -73,7 +73,7 @@ "rip7212TransitionTimestamp": "0x668eb001", "opGraniteTransitionTimestamp": "0x66e1be81", - "terminalTotalDifficulty": "0" + "terminalTotalDifficulty": "210470125" }, "genesis": { "seal": { @@ -93,6 +93,11 @@ "stateRoot": "0xeddb4c1786789419153a27c4c80ff44a2226b6eda04f7e22ce5bae892ea568eb" }, "nodes": [ + "enode://87a32fd13bd596b2ffca97020e31aef4ddcc1bbd4b95bb633d16c1329f654f34049ed240a36b449fda5e5225d70fe40bc667f53c304b71f8e68fc9d448690b51@3.231.138.188:30301", + "enode://ca21ea8f176adb2e229ce2d700830c844af0ea941a1d8152a9513b966fe525e809c3a6c73a2c18a12b74ed6ec4380edf91662778fe0b79f6a591236e49e176f9@184.72.129.189:30301", + "enode://acf4507a211ba7c1e52cdf4eef62cdc3c32e7c9c47998954f7ba024026f9a6b2150cd3f0b734d9c78e507ab70d59ba61dfe5c45e1078c7ad0775fb251d7735a2@3.220.145.177:30301", + "enode://8a5a5006159bf079d06a04e5eceab2a1ce6e0f721875b2a9c96905336219dbe14203d38f70f3754686a6324f786c2f9852d8c0dd3adac2d080f4db35efc678c5@3.231.11.52:30301", + "enode://cdadbe835308ad3557f9a1de8db411da1a260a98f8421d62da90e71da66e55e98aaa8e90aa7ce01b408a54e4bd2253d701218081ded3dbe5efbbc7b41d7cef79@54.198.153.150:30301", "enode://ca2774c3c401325850b2477fd7d0f27911efbf79b1e8b335066516e2bd8c4c9e0ba9696a94b1cb030a88eac582305ff55e905e64fb77fe0edcd70a4e5296d3ec@34.65.175.185:30305", "enode://dd751a9ef8912be1bfa7a5e34e2c3785cc5253110bd929f385e07ba7ac19929fb0e0c5d93f77827291f4da02b2232240fbc47ea7ce04c46e333e452f8656b667@34.65.107.0:30305", "enode://c5d289b56a77b6a2342ca29956dfd07aadf45364dde8ab20d1dc4efd4d1bc6b4655d902501daea308f4d8950737a4e93a4dfedd17b49cd5760ffd127837ca965@34.65.202.239:30305", diff --git a/src/Nethermind/Directory.Packages.props b/src/Nethermind/Directory.Packages.props index 253c598a39d..b7fae1de28d 100644 --- a/src/Nethermind/Directory.Packages.props +++ b/src/Nethermind/Directory.Packages.props @@ -6,6 +6,8 @@ + + @@ -36,6 +38,7 @@ + diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 6f8afe58043..fe5fb25c019 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -326,12 +326,8 @@ private void InitializeTestState(BlockchainTest test, IWorldState stateProvider, stateProvider.Set(new StorageCell(accountState.Key, storageItem.Key), storageItem.Value); } - stateProvider.CreateAccount(accountState.Key, accountState.Value.Balance); + stateProvider.CreateAccount(accountState.Key, accountState.Value.Balance, accountState.Value.Nonce); stateProvider.InsertCode(accountState.Key, accountState.Value.Code, specProvider.GenesisSpec); - for (int i = 0; i < accountState.Value.Nonce; i++) - { - stateProvider.IncrementNonce(accountState.Key); - } } stateProvider.Commit(specProvider.GenesisSpec); diff --git a/src/Nethermind/Ethereum.Trie.Test/TrieTests.cs b/src/Nethermind/Ethereum.Trie.Test/TrieTests.cs index ab7f94d6220..aedee4459aa 100644 --- a/src/Nethermind/Ethereum.Trie.Test/TrieTests.cs +++ b/src/Nethermind/Ethereum.Trie.Test/TrieTests.cs @@ -307,7 +307,7 @@ public void Delete_on_empty() { PatriciaTree patriciaTree = new PatriciaTree(_db, Keccak.EmptyTreeHash, false, true, NullLogManager.Instance); patriciaTree.Set(Keccak.Compute("1").Bytes, new byte[0]); - patriciaTree.Commit(0); + patriciaTree.Commit(); Assert.That(patriciaTree.RootHash, Is.EqualTo(PatriciaTree.EmptyTreeHash)); } diff --git a/src/Nethermind/Nethermind.Api/IApiWithBlockchain.cs b/src/Nethermind/Nethermind.Api/IApiWithBlockchain.cs index 61bceb25ae4..da24eaa918b 100644 --- a/src/Nethermind/Nethermind.Api/IApiWithBlockchain.cs +++ b/src/Nethermind/Nethermind.Api/IApiWithBlockchain.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only #nullable enable +using Autofac; using Nethermind.Blockchain; using Nethermind.Blockchain.Filters; using Nethermind.Blockchain.FullPruning; @@ -100,5 +101,12 @@ public interface IApiWithBlockchain : IApiWithStores, IBlockchainBridgeFactory INodeStorageFactory NodeStorageFactory { get; set; } BackgroundTaskScheduler BackgroundTaskScheduler { get; set; } CensorshipDetector CensorshipDetector { get; set; } + + public ContainerBuilder ConfigureContainerBuilderFromApiWithBlockchain(ContainerBuilder builder) + { + return ConfigureContainerBuilderFromApiWithStores(builder) + .AddPropertiesFrom(this) + .AddSingleton(NodeStorageFactory.WrapKeyValueStore(DbProvider!.StateDb)); + } } } diff --git a/src/Nethermind/Nethermind.Api/IApiWithNetwork.cs b/src/Nethermind/Nethermind.Api/IApiWithNetwork.cs index 0f5d2262535..0b0e05e2a6d 100644 --- a/src/Nethermind/Nethermind.Api/IApiWithNetwork.cs +++ b/src/Nethermind/Nethermind.Api/IApiWithNetwork.cs @@ -2,7 +2,9 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; +using Autofac; using Nethermind.Consensus; +using Nethermind.Core; using Nethermind.Core.PubSub; using Nethermind.Grpc; using Nethermind.JsonRpc; @@ -16,6 +18,7 @@ using Nethermind.Synchronization; using Nethermind.Synchronization.Peers; using Nethermind.Sockets; +using Nethermind.Synchronization.ParallelSync; namespace Nethermind.Api { @@ -41,12 +44,22 @@ public interface IApiWithNetwork : IApiWithBlockchain IJsonRpcLocalStats? JsonRpcLocalStats { get; set; } ISessionMonitor? SessionMonitor { get; set; } IStaticNodesManager? StaticNodesManager { get; set; } - ISynchronizer? Synchronizer { get; set; } + ISynchronizer? Synchronizer { get; } + ISyncModeSelector SyncModeSelector { get; } + ISyncProgressResolver? SyncProgressResolver { get; } IPivot? Pivot { get; set; } ISyncPeerPool? SyncPeerPool { get; set; } IPeerDifficultyRefreshPool? PeerDifficultyRefreshPool { get; set; } ISyncServer? SyncServer { get; set; } IWebSocketsManager WebSocketsManager { get; set; } ISubscriptionFactory? SubscriptionFactory { get; set; } + + IContainer? ApiWithNetworkServiceContainer { get; set; } + + public ContainerBuilder ConfigureContainerBuilderFromApiWithNetwork(ContainerBuilder builder) + { + return ConfigureContainerBuilderFromApiWithBlockchain(builder) + .AddPropertiesFrom(this); + } } } diff --git a/src/Nethermind/Nethermind.Api/IApiWithStores.cs b/src/Nethermind/Nethermind.Api/IApiWithStores.cs index fa2911c2ebe..eed8ed62430 100644 --- a/src/Nethermind/Nethermind.Api/IApiWithStores.cs +++ b/src/Nethermind/Nethermind.Api/IApiWithStores.cs @@ -1,11 +1,13 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Autofac; using Nethermind.Blockchain; using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Receipts; using Nethermind.Consensus; +using Nethermind.Core; using Nethermind.Crypto; using Nethermind.Db.Blooms; using Nethermind.Facade.Find; @@ -30,5 +32,11 @@ public interface IApiWithStores : IBasicApi IReceiptMonitor? ReceiptMonitor { get; set; } IWallet? Wallet { get; set; } IBlockStore? BadBlocksStore { get; set; } + + public ContainerBuilder ConfigureContainerBuilderFromApiWithStores(ContainerBuilder builder) + { + return ConfigureContainerBuilderFromBasicApi(builder) + .AddPropertiesFrom(this); + } } } diff --git a/src/Nethermind/Nethermind.Api/IBasicApi.cs b/src/Nethermind/Nethermind.Api/IBasicApi.cs index 3b78f16a865..1ba0adb91f9 100644 --- a/src/Nethermind/Nethermind.Api/IBasicApi.cs +++ b/src/Nethermind/Nethermind.Api/IBasicApi.cs @@ -1,11 +1,14 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; using System.IO.Abstractions; using System.Linq; +using Autofac; using Nethermind.Abi; using Nethermind.Api.Extensions; +using Nethermind.Blockchain.Synchronization; using Nethermind.Config; using Nethermind.Core; using Nethermind.Core.Specs; @@ -17,7 +20,6 @@ using Nethermind.Serialization.Json; using Nethermind.Specs.ChainSpecStyle; using Nethermind.Synchronization; -using Nethermind.Synchronization.ParallelSync; namespace Nethermind.Api { @@ -38,10 +40,9 @@ public interface IBasicApi ILogManager LogManager { get; set; } ProtectedPrivateKey? OriginalSignerKey { get; set; } IReadOnlyList Plugins { get; } + [SkipServiceCollection] string SealEngineType { get; set; } ISpecProvider? SpecProvider { get; set; } - ISyncModeSelector SyncModeSelector { get; set; } - ISyncProgressResolver? SyncProgressResolver { get; set; } IBetterPeerStrategy? BetterPeerStrategy { get; set; } ITimestamper Timestamper { get; } ITimerFactory TimerFactory { get; } @@ -57,5 +58,16 @@ public IEnumerable GetConsensusWrapperPlugins() => public IEnumerable GetSynchronizationPlugins() => Plugins.OfType(); + + public ContainerBuilder ConfigureContainerBuilderFromBasicApi(ContainerBuilder builder) + { + builder + .AddPropertiesFrom(this) + .AddSingleton(ConfigProvider.GetConfig()); + + DbProvider!.ConfigureServiceCollection(builder); + + return builder; + } } } diff --git a/src/Nethermind/Nethermind.Api/INethermindApi.cs b/src/Nethermind/Nethermind.Api/INethermindApi.cs index fff51021393..7c600b9f056 100644 --- a/src/Nethermind/Nethermind.Api/INethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/INethermindApi.cs @@ -5,6 +5,8 @@ using System; using Nethermind.Config; using Nethermind.Core; +using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Serialization.Rlp; using Nethermind.Serialization.Rlp.TxDecoders; using Nethermind.TxPool; @@ -23,12 +25,14 @@ public T Config() where T : IConfig public static class NethermindApiExtensions { - public static void RegisterTxType(this INethermindApi api, TxType type, ITxDecoder decoder, ITxValidator validator) + public static void RegisterTxType(this INethermindApi api, ITxDecoder decoder, ITxValidator validator) where T : TransactionForRpc, IFromTransaction { ArgumentNullException.ThrowIfNull(api.TxValidator); + if (decoder.Type != T.TxType) throw new ArgumentException($"TxType mismatch decoder: {decoder.Type}, RPC: {T.TxType}"); - api.TxValidator.RegisterValidator(type, validator); + api.TxValidator.RegisterValidator(T.TxType, validator); TxDecoder.Instance.RegisterDecoder(decoder); + TransactionForRpc.RegisterTransactionType(); } } } diff --git a/src/Nethermind/Nethermind.Api/NethermindApi.cs b/src/Nethermind/Nethermind.Api/NethermindApi.cs index 56193492d01..566a094eaa0 100644 --- a/src/Nethermind/Nethermind.Api/NethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/NethermindApi.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO.Abstractions; +using Autofac; using Nethermind.Abi; using Nethermind.Api.Extensions; using Nethermind.Blockchain; @@ -186,14 +187,14 @@ public ISealEngine SealEngine public ISessionMonitor? SessionMonitor { get; set; } public ISpecProvider? SpecProvider { get; set; } public IPoSSwitcher PoSSwitcher { get; set; } = NoPoS.Instance; - public ISyncModeSelector SyncModeSelector { get; set; } = null!; + public ISyncModeSelector SyncModeSelector => ApiWithNetworkServiceContainer?.Resolve()!; - public ISyncProgressResolver? SyncProgressResolver { get; set; } + public ISyncProgressResolver? SyncProgressResolver => ApiWithNetworkServiceContainer?.Resolve(); public IBetterPeerStrategy? BetterPeerStrategy { get; set; } public IPivot? Pivot { get; set; } public ISyncPeerPool? SyncPeerPool { get; set; } public IPeerDifficultyRefreshPool? PeerDifficultyRefreshPool { get; set; } - public ISynchronizer? Synchronizer { get; set; } + public ISynchronizer? Synchronizer => ApiWithNetworkServiceContainer?.Resolve(); public ISyncServer? SyncServer { get; set; } public IWorldState? WorldState { get; set; } public IReadOnlyStateProvider? ChainHeadStateProvider { get; set; } @@ -243,5 +244,7 @@ public ISealEngine SealEngine public CompositePruningTrigger PruningTrigger { get; } = new(); public IProcessExitSource? ProcessExit { get; set; } public CompositeTxGossipPolicy TxGossipPolicy { get; } = new(); + + public IContainer? ApiWithNetworkServiceContainer { get; set; } } } diff --git a/src/Nethermind/Nethermind.Benchmark/Store/PatriciaTreeBenchmarks.cs b/src/Nethermind/Nethermind.Benchmark/Store/PatriciaTreeBenchmarks.cs index 5d568377914..07d700e9352 100644 --- a/src/Nethermind/Nethermind.Benchmark/Store/PatriciaTreeBenchmarks.cs +++ b/src/Nethermind/Nethermind.Benchmark/Store/PatriciaTreeBenchmarks.cs @@ -56,14 +56,14 @@ public class PatriciaTreeBenchmarks tree.Set(TestItem.AddressA, _account0); tree.Set(TestItem.AddressB, _account0); tree.Set(TestItem.AddressC, _account0); - tree.Commit(1); + tree.Commit(); }), ("set_3_via_hash", tree => { tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb00000000"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), _account0); - tree.Commit(1); + tree.Commit(); }), ("set_3_delete_1", tree => { @@ -71,7 +71,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), null); - tree.Commit(1); + tree.Commit(); }), ("set_3_delete_2", tree => { @@ -80,7 +80,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), null); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), null); - tree.Commit(1); + tree.Commit(); }), ("set_3_delete_all", tree => { @@ -90,7 +90,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), null); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), null); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb00000000"), null); - tree.Commit(1); + tree.Commit(); }), ("extension_read_full_match", tree => { @@ -99,7 +99,7 @@ public class PatriciaTreeBenchmarks Account account = tree.Get(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb11111111")); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("extension_read_missing", tree => { @@ -108,7 +108,7 @@ public class PatriciaTreeBenchmarks Account account = tree.Get(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddd")); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("extension_new_branch", tree => { @@ -117,7 +117,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddd"), _account2); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("extension_delete_missing", tree => { @@ -126,7 +126,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddd"), null); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("extenson_create_new_extension", tree => { @@ -136,7 +136,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaaaaaaaaaaaaaaaab11111111"), _account3); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("leaf_new_value", tree => { @@ -144,7 +144,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("1111111111111111111111111111111111111111111111111111111111111111"), _account1); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("leaf_no_change", tree => { @@ -152,7 +152,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("1111111111111111111111111111111111111111111111111111111111111111"), _account0); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("leaf_delete", tree => { @@ -160,7 +160,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("1111111111111111111111111111111111111111111111111111111111111111"), null); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("leaf_delete_missing", tree => { @@ -168,7 +168,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("1111111111111111111111111111111ddddddddddddddddddddddddddddddddd"), null); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("leaf_update_extension", tree => { @@ -176,7 +176,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb00000000000000000000000000000000"), _account1); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("leaf_read", tree => { @@ -184,7 +184,7 @@ public class PatriciaTreeBenchmarks Account account = tree.Get(new Hash256("1111111111111111111111111111111111111111111111111111111111111111")); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("leaf_update_missing", tree => { @@ -192,7 +192,7 @@ public class PatriciaTreeBenchmarks Account account = tree.Get(new Hash256("111111111111111111111111111111111111111111111111111111111ddddddd")); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("branch_update_missing", tree => { @@ -201,7 +201,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb22222"), _account2); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("branch_read_missing", tree => { @@ -210,7 +210,7 @@ public class PatriciaTreeBenchmarks Account account = tree.Get(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb22222")); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), ("branch_delete_missing", tree => { @@ -219,7 +219,7 @@ public class PatriciaTreeBenchmarks tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb22222"), null); tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; - tree.Commit(1); + tree.Commit(); }), }; @@ -247,7 +247,7 @@ public void Setup() { tempTree.Set(_entries[i].Item1, _entries[i].Item2); } - tempTree.Commit(0); + tempTree.Commit(); _rootHash = tempTree.RootHash; _fullTree = new StateTree(); @@ -255,7 +255,7 @@ public void Setup() { _fullTree.Set(_entries[i].Item1, _entries[i].Item2); } - _fullTree.Commit(0); + _fullTree.Commit(); _uncommittedFullTree = new StateTree(); for (int i = 0; i < _entryCount; i++) @@ -330,23 +330,23 @@ public void InsertAndCommit() { tempTree.Set(_entries[i].Item1, _entries[i].Item2); } - tempTree.Commit(0); + tempTree.Commit(); } [Benchmark] public void InsertAndCommitRepeatedlyTimes() { - StateTree tempTree = new StateTree( - new TrieStore(new MemDb(), + TrieStore trieStore = new TrieStore(new MemDb(), Prune.WhenCacheReaches(1.MiB()), - Persist.IfBlockOlderThan(2), - NullLogManager.Instance), NullLogManager.Instance); + Persist.IfBlockOlderThan(2), NullLogManager.Instance); + StateTree tempTree = new StateTree(trieStore, NullLogManager.Instance); for (int i = 0; i < _largerEntryCount; i++) { if (i % 2000 == 0) { - tempTree.Commit(i / 2000); + using IBlockCommitter _ = trieStore.BeginBlockCommit(i / 2000); + tempTree.Commit(); } (bool isWrite, Hash256 address, Account value) = _largerEntriesAccess[i]; diff --git a/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs b/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs index 35c6c3fc1ac..cfee2a225fd 100644 --- a/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs +++ b/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs @@ -143,4 +143,7 @@ public interface ISyncConfig : IConfig [ConfigItem(Description = "_Technical._ MultiSyncModeSelector sync mode timer loop interval. Used for testing.", DefaultValue = "1000", HiddenFromDocs = true)] int MultiSyncModeSelectorLoopTimerMs { get; set; } + + [ConfigItem(Description = "_Technical._ MultiSyncModeSelector will wait for header to completely sync first.", DefaultValue = "false", HiddenFromDocs = true)] + bool NeedToWaitForHeader { get; set; } } diff --git a/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs b/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs index d2645336586..c467650ca7a 100644 --- a/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs +++ b/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs @@ -65,6 +65,7 @@ public string? PivotHash public int MallocTrimIntervalSec { get; set; } = 300; public bool? SnapServingEnabled { get; set; } = null; public int MultiSyncModeSelectorLoopTimerMs { get; set; } = 1000; + public bool NeedToWaitForHeader { get; set; } public bool TrieHealing { get; set; } = true; public override string ToString() diff --git a/src/Nethermind/Nethermind.Cli.Test/ProofCliModuleTests.cs b/src/Nethermind/Nethermind.Cli.Test/ProofCliModuleTests.cs index 3eac4d9207c..8f2f30409a8 100644 --- a/src/Nethermind/Nethermind.Cli.Test/ProofCliModuleTests.cs +++ b/src/Nethermind/Nethermind.Cli.Test/ProofCliModuleTests.cs @@ -7,10 +7,9 @@ using Nethermind.Cli.Modules; using Nethermind.Core.Crypto; using Nethermind.Core.Test.Builders; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc; using Nethermind.JsonRpc.Client; -using Nethermind.JsonRpc.Data; using Nethermind.Logging; using Nethermind.Serialization.Json; using NSubstitute; @@ -81,7 +80,7 @@ public void Get_transaction_receipt(bool includeHeader) public void Call() { Hash256 blockHash = TestItem.KeccakA; - TransactionForRpc tx = new() + LegacyTransactionForRpc tx = new() { From = TestItem.AddressA, To = TestItem.AddressB diff --git a/src/Nethermind/Nethermind.Cli/Modules/EthCliModule.cs b/src/Nethermind/Nethermind.Cli/Modules/EthCliModule.cs index 961ce37bfe9..94640022b6d 100644 --- a/src/Nethermind/Nethermind.Cli/Modules/EthCliModule.cs +++ b/src/Nethermind/Nethermind.Cli/Modules/EthCliModule.cs @@ -7,9 +7,8 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; -using Nethermind.JsonRpc.Data; namespace Nethermind.Cli.Modules { @@ -20,13 +19,15 @@ public class EthCliModule : CliModuleBase { long blockNumber = NodeManager.Post("eth_blockNumber").Result; - TransactionForRpc tx = new(); - tx.Value = amountInWei; - tx.Gas = Transaction.BaseTxGasCost; - tx.GasPrice = (UInt256)Engine.JintEngine.GetValue("gasPrice").AsNumber(); - tx.To = address; - tx.Nonce = (ulong)NodeManager.Post("eth_getTransactionCount", from, blockNumber).Result; - tx.From = from; + LegacyTransactionForRpc tx = new() + { + Value = amountInWei, + Gas = Transaction.BaseTxGasCost, + GasPrice = (UInt256)Engine.JintEngine.GetValue("gasPrice").AsNumber(), + To = address, + Nonce = (ulong)NodeManager.Post("eth_getTransactionCount", from, blockNumber).Result, + From = from + }; Hash256? keccak = NodeManager.Post("eth_sendTransaction", tx).Result; return keccak?.Bytes.ToHexString(); diff --git a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs index 368524c6bb1..3acd55ff4f2 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs @@ -136,7 +136,7 @@ private bool ValidateGasUsed(BlockHeader header, ref string? error) return true; } - private bool ValidateParent(BlockHeader header, BlockHeader? parent, ref string? error) + protected bool ValidateParent(BlockHeader header, BlockHeader? parent, ref string? error) { if (parent is null) { diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.Tree.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.Tree.cs index aa440794481..64b9d22d833 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.Tree.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.Tree.cs @@ -66,7 +66,7 @@ public static void FillStateTreeWithTestAccounts(StateTree stateTree) stateTree.Set(AccountsWithPaths[3].Path, AccountsWithPaths[3].Account); stateTree.Set(AccountsWithPaths[4].Path, AccountsWithPaths[4].Account); stateTree.Set(AccountsWithPaths[5].Path, AccountsWithPaths[5].Account); - stateTree.Commit(0); + stateTree.Commit(); } public static void FillStateTreeMultipleAccount(StateTree stateTree, int accountNumber) @@ -76,7 +76,7 @@ public static void FillStateTreeMultipleAccount(StateTree stateTree, int account Account acc = Build.An.Account.WithBalance((UInt256)i).TestObject; stateTree.Set(Keccak.Compute(i.ToBigEndianByteArray()), acc); } - stateTree.Commit(0); + stateTree.Commit(); } public static (StateTree stateTree, StorageTree storageTree, Hash256 accountAddr) GetTrees(ITrieStore? store) @@ -92,13 +92,13 @@ public static (StateTree stateTree, StorageTree storageTree, Hash256 accountAddr storageTree.Set(SlotsWithPaths[4].Path, SlotsWithPaths[4].SlotRlpValue, false); storageTree.Set(SlotsWithPaths[5].Path, SlotsWithPaths[5].SlotRlpValue, false); - storageTree.Commit(0); + storageTree.Commit(); var account = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; var stateTree = new StateTree(store.GetTrieStore(null), LimboLogs.Instance); stateTree.Set(AccountAddress0, account); - stateTree.Commit(0); + stateTree.Commit(); return (stateTree, storageTree, AccountAddress0); } @@ -117,13 +117,13 @@ public static (StateTree stateTree, StorageTree storageTree, Hash256 accountAddr false); } - storageTree.Commit(0); + storageTree.Commit(); var account = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; var stateTree = new StateTree(store, LimboLogs.Instance); stateTree.Set(AccountAddress0, account); - stateTree.Commit(0); + stateTree.Commit(); return (stateTree, storageTree, AccountAddress0); } diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs index ac8cf8e0313..65cd94de826 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs @@ -39,6 +39,13 @@ static TestItem() Keccaks[i - 1] = Keccak.Compute(PublicKeys[i - 1].Bytes); ValueKeccaks[i - 1] = Keccaks[i - 1]; } + + byte[] r = new byte[32]; + byte[] s = new byte[32]; + r[1] = 1; + s[2] = 2; + RandomSignatureA = new Signature(r, s, 27); + RandomSignatureB = new Signature(r, s, 28); } public static Hash256 KeccakFromNumber(int i) @@ -93,6 +100,9 @@ public static Hash256 KeccakFromNumber(int i) public static Address AddressE = PublicKeyE.Address; public static Address AddressF = PublicKeyF.Address; + public static readonly Signature RandomSignatureA; + public static readonly Signature RandomSignatureB; + public static Withdrawal WithdrawalA_1Eth = new() { Address = AddressA, Index = 1, ValidatorIndex = 2001, AmountInGwei = 1_000_000_000 }; public static Withdrawal WithdrawalB_2Eth = new() { Address = AddressB, Index = 2, ValidatorIndex = 2002, AmountInGwei = 2_000_000_000 }; public static Withdrawal WithdrawalC_3Eth = new() { Address = AddressC, Index = 3, ValidatorIndex = 2003, AmountInGwei = 3_000_000_000 }; diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs index cf81f10a348..2246e7c319d 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs @@ -63,7 +63,7 @@ public TransactionBuilder WithCode(byte[] data) return this; } - public TransactionBuilder WithChainId(ulong chainId) + public TransactionBuilder WithChainId(ulong? chainId) { TestObjectInternal.ChainId = chainId; return this; @@ -298,6 +298,12 @@ public TransactionBuilder WithIsServiceTransaction(bool isServiceTransaction) return this; } + public TransactionBuilder WithSourceHash(Hash256? sourceHash) + { + TestObjectInternal.SourceHash = sourceHash; + return this; + } + public TransactionBuilder From(T item) { TestObjectInternal = item; diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TrieBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TrieBuilder.cs index 691f2debad1..3f2bdcf9688 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TrieBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TrieBuilder.cs @@ -36,7 +36,7 @@ public TrieBuilder WithAccountsByIndex(int start, int count) TestObjectInternal.Set(key.Bytes, value); } - TestObjectInternal.Commit(0); + TestObjectInternal.Commit(); TestObjectInternal.UpdateRootHash(); return this; diff --git a/src/Nethermind/Nethermind.Core.Test/ContainerBuilderExtensionsTests.cs b/src/Nethermind/Nethermind.Core.Test/ContainerBuilderExtensionsTests.cs new file mode 100644 index 00000000000..28e890c8499 --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/ContainerBuilderExtensionsTests.cs @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Autofac; +using FluentAssertions; +using NUnit.Framework; + +namespace Nethermind.Core.Test; + +public class ContainerBuilderExtensionsTests +{ + [Test] + public void AddPropertiesFrom_CanAddProperties() + { + ITestInterface interfaceImplementation = new InterfaceImplementation(); + IContainer sp = new ContainerBuilder() + .AddPropertiesFrom(interfaceImplementation) + .Build(); + + sp.ResolveOptional().Should().NotBeNull(); + sp.ResolveOptional().Should().BeNull(); + sp.ResolveOptional().Should().BeNull(); + sp.ResolveOptional().Should().BeNull(); + } + + [Test] + public void TestRegisterNamedComponent() + { + IContainer sp = new ContainerBuilder() + .AddScoped() + .AddScoped() + .RegisterNamedComponentInItsOwnLifetime("custom", cfg => + { + // Override it in custom + cfg.AddScoped(); + }) + .Build(); + + using (ILifetimeScope scope = sp.BeginLifetimeScope()) + { + scope.Resolve().Property.Should().BeOfType(); + } + + MainComponentDependency customMainComponentDependency = sp.ResolveNamed("custom").Property; + sp.ResolveNamed("custom").Property.Should().BeOfType(); + + sp.Dispose(); + + customMainComponentDependency.WasDisposed.Should().BeTrue(); + } + + private class MainComponent(MainComponentDependency mainComponentDependency, ILifetimeScope scope) : IDisposable + { + public MainComponentDependency Property => mainComponentDependency; + + public void Dispose() + { + scope.Dispose(); + } + } + + private class MainComponentDependency : IDisposable + { + public bool WasDisposed { get; set; } + + public void Dispose() + { + WasDisposed = true; + } + } + + private class MainComponentDependencySubClass : MainComponentDependency + { + } + + private class InterfaceImplementation : ITestInterface + { + public DeclaredService TheService { get; set; } = new DeclaredService(); + public DeclaredButNullService? NullService { get; set; } = null; + public Ignored IgnoredService { get; set; } = new Ignored(); + public DeclaredInBase BaseService { get; set; } = new DeclaredInBase(); + } + + private interface ITestInterface : ITestInterfaceBase + { + DeclaredService TheService { get; set; } + DeclaredButNullService? NullService { get; set; } + + [SkipServiceCollection] + Ignored IgnoredService { get; set; } + } + + private interface ITestInterfaceBase + { + DeclaredInBase BaseService { get; set; } + } + + private class DeclaredInBase { } + private class DeclaredService { } + private class DeclaredButNullService { } + private class Ignored { } + + private class DisposableService : IDisposable + { + public bool WasDisposed { get; set; } = false; + + public void Dispose() + { + WasDisposed = true; + } + } +} diff --git a/src/Nethermind/Nethermind.Core/ContainerBuilderExtensions.cs b/src/Nethermind/Nethermind.Core/ContainerBuilderExtensions.cs new file mode 100644 index 00000000000..1c30a830b55 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/ContainerBuilderExtensions.cs @@ -0,0 +1,125 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Autofac; +using Autofac.Features.AttributeFilters; + +namespace Nethermind.Core; + +public static class ContainerBuilderExtensions +{ + /// + /// Add all properties as singleton. It get them ahead of time instead of lazily to prevent the final service provider + /// from disposing it. To prevent a property from being included, use . + /// + /// + /// + /// + /// + public static ContainerBuilder AddPropertiesFrom(this ContainerBuilder configuration, T source) where T : class + { + Type t = typeof(T); + + IEnumerable properties = t + .GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly) + .Where(p => p.GetCustomAttribute() == null); + + foreach (PropertyInfo propertyInfo in properties) + { + object? val = propertyInfo.GetValue(source); + if (val != null) + { + configuration.RegisterInstance(val).As(propertyInfo.PropertyType); + } + } + + return configuration; + } + + public static ContainerBuilder AddSingleton(this ContainerBuilder builder) where T : notnull + { + builder.RegisterType() + .As() + .WithAttributeFiltering() + .SingleInstance(); + + return builder; + } + + public static ContainerBuilder AddSingleton(this ContainerBuilder builder, T instance) where T : class + { + builder.RegisterInstance(instance) + .As() + .SingleInstance(); + + return builder; + } + + public static ContainerBuilder AddSingleton(this ContainerBuilder builder) where TImpl : notnull where T : notnull + { + builder.RegisterType() + .As() + .AsSelf() + .WithAttributeFiltering() + .SingleInstance(); + + return builder; + } + + public static ContainerBuilder AddKeyedSingleton(this ContainerBuilder builder, string key, T instance) where T : class + { + builder.RegisterInstance(instance) + .Named(key) + .SingleInstance(); + + return builder; + } + + public static ContainerBuilder AddScoped(this ContainerBuilder builder) where T : notnull + { + builder.RegisterType() + .As() + .AsSelf() + .WithAttributeFiltering() + .InstancePerLifetimeScope(); + + return builder; + } + + public static ContainerBuilder AddScoped(this ContainerBuilder builder) where TImpl : notnull where T : notnull + { + builder.RegisterType() + .As() + .WithAttributeFiltering() + .InstancePerLifetimeScope(); + + return builder; + } + + /// + /// A convenient way of creating a service whose member can be configured indipendent of other instance of the same + /// type (assuming the type is of lifetime scope). This is useful for same type with multiple configuration + /// or a graph of multiple same type. The T is expected to be of a main container of sort that contains the + /// main service of interest. + /// Note: The T should dispose an injected ILifetimeScope on dispose as ILifetimeScope is not automatically disposed + /// when parent scope is disposed. + /// + public static ContainerBuilder RegisterNamedComponentInItsOwnLifetime(this ContainerBuilder builder, string name, Action configurator) where T : notnull + { + builder.Register(ctx => ctx.BeginLifetimeScope(configurator).Resolve()) + .Named(name); + + return builder; + } +} + +/// +/// Mark a property so that it is not picked up by `AddPropertiesFrom`. +/// +public class SkipServiceCollectionAttribute : Attribute +{ +} diff --git a/src/Nethermind/Nethermind.Core/Extensions/UInt256Extensions.cs b/src/Nethermind/Nethermind.Core/Extensions/UInt256Extensions.cs new file mode 100644 index 00000000000..b0f06b73cd1 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Extensions/UInt256Extensions.cs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Int256; + +namespace Nethermind.Core.Extensions; + +public static class UInt256Extensions +{ + // value?.IsZero == false <=> x > 0 + public static bool IsPositive(this UInt256? @this) => @this?.IsZero == false; +} diff --git a/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj b/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj index de2832e6ea5..930e96abe48 100644 --- a/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj +++ b/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj @@ -5,7 +5,10 @@ + + + diff --git a/src/Nethermind/Nethermind.Db/IDbProvider.cs b/src/Nethermind/Nethermind.Db/IDbProvider.cs index 7786271bc74..6a0380b0446 100644 --- a/src/Nethermind/Nethermind.Db/IDbProvider.cs +++ b/src/Nethermind/Nethermind.Db/IDbProvider.cs @@ -3,6 +3,9 @@ using System; using System.Collections.Generic; +using Autofac; +using Microsoft.Extensions.DependencyInjection; +using Nethermind.Core; namespace Nethermind.Db { @@ -30,5 +33,34 @@ public interface IDbProvider : IDisposable void RegisterDb(string dbName, T db) where T : class, IDb; void RegisterColumnDb(string dbName, IColumnsDb db); IEnumerable> GetAllDbMeta(); + + void ConfigureServiceCollection(ContainerBuilder sc) + { + sc.AddSingleton(this); + + // TODO: Have hooks that automatically get these + string[] dbNames = [ + DbNames.State, + DbNames.Code, + DbNames.Metadata, + DbNames.Blocks, + DbNames.Headers, + DbNames.BlockInfos, + DbNames.BadBlocks, + DbNames.Bloom, + DbNames.Metadata, + ]; + foreach (string dbName in dbNames) + { + var db = GetDb(dbName); + sc.AddKeyedSingleton(dbName, db); + sc.AddKeyedSingleton(dbName, db); + sc.AddKeyedSingleton(dbName, db as ITunableDb ?? new NoopTunableDb()); + } + + IColumnsDb receiptColumnDb = GetColumnDb(DbNames.Receipts); + sc.AddSingleton>(receiptColumnDb); + sc.AddKeyedSingleton(DbNames.Receipts, receiptColumnDb as ITunableDb ?? new NoopTunableDb()); + } } } diff --git a/src/Nethermind/Nethermind.Db/ITunableDb.cs b/src/Nethermind/Nethermind.Db/ITunableDb.cs index 7584364badd..00b0edf0f0c 100644 --- a/src/Nethermind/Nethermind.Db/ITunableDb.cs +++ b/src/Nethermind/Nethermind.Db/ITunableDb.cs @@ -3,7 +3,7 @@ namespace Nethermind.Db; -public interface ITunableDb : IDb +public interface ITunableDb { public void Tune(TuneType type); @@ -18,3 +18,10 @@ enum TuneType HashDb } } + +public class NoopTunableDb : ITunableDb +{ + public void Tune(ITunableDb.TuneType type) + { + } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/AccessListItemForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/AccessListItemForRpc.cs deleted file mode 100644 index 0269aeeb0e6..00000000000 --- a/src/Nethermind/Nethermind.Facade/Eth/AccessListItemForRpc.cs +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using System.Linq; -using Nethermind.Core; -using Nethermind.Core.Collections; -using Nethermind.Core.Eip2930; -using Nethermind.Int256; -using Nethermind.Serialization.Json; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Nethermind.Facade.Eth -{ - public struct AccessListItemForRpc : IEquatable - { - [JsonConstructor] - public AccessListItemForRpc() { } - - public AccessListItemForRpc(Address address, IEnumerable? storageKeys) - { - Address = address; - StorageKeys = storageKeys; - } - - public Address Address { get; set; } - [JsonConverter(typeof(StorageCellIndexConverter))] - public IEnumerable? StorageKeys { get; set; } - - public static IEnumerable FromAccessList(AccessList accessList) => - accessList.Select(tuple => new AccessListItemForRpc(tuple.Address, tuple.StorageKeys)); - - public static AccessList ToAccessList(IEnumerable accessList) - { - AccessList.Builder builder = new(); - foreach (AccessListItemForRpc accessListItem in accessList) - { - builder.AddAddress(accessListItem.Address); - if (accessListItem.StorageKeys is not null) - { - foreach (UInt256 index in accessListItem.StorageKeys) - { - builder.AddStorage(index); - } - } - } - - return builder.Build(); - } - - public readonly bool Equals(AccessListItemForRpc other) => Equals(Address, other.Address) && StorageKeys.NullableSequenceEqual(other.StorageKeys); - public override readonly bool Equals(object? obj) => obj is AccessListItemForRpc other && Equals(other); - public override readonly int GetHashCode() => HashCode.Combine(Address, StorageKeys); - } -} diff --git a/src/Nethermind/Nethermind.Facade/Eth/AuthorizationTupleForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/AuthorizationTupleForRpc.cs deleted file mode 100644 index c35c97b1d56..00000000000 --- a/src/Nethermind/Nethermind.Facade/Eth/AuthorizationTupleForRpc.cs +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core; -using Nethermind.Int256; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json.Serialization; - -namespace Nethermind.Facade.Eth -{ - public struct AuthorizationTupleForRpc - { - [JsonConstructor] - public AuthorizationTupleForRpc() - { - } - public AuthorizationTupleForRpc(UInt256 chainId, ulong nonce, Address address, UInt256? yParity, UInt256? s, UInt256? r) - { - ChainId = chainId; - Nonce = nonce; - Address = address; - YParity = yParity; - S = s; - R = r; - } - - public UInt256 ChainId { get; set; } - public ulong Nonce { get; set; } - public Address Address { get; set; } - public UInt256? YParity { get; set; } - public UInt256? S { get; set; } - public UInt256? R { get; set; } - - public static IEnumerable FromAuthorizationList(AuthorizationTuple[] authorizationList) => - authorizationList.Select(tuple => new AuthorizationTupleForRpc(tuple.ChainId, - tuple.Nonce, - tuple.CodeAddress, - tuple.AuthoritySignature.RecoveryId, - new UInt256(tuple.AuthoritySignature.S), - new UInt256(tuple.AuthoritySignature.R))); - } -} diff --git a/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs index b0904d5552f..c194e214615 100644 --- a/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs +++ b/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs @@ -12,6 +12,7 @@ using Nethermind.Serialization.Rlp; using System.Text.Json.Serialization; using System.Runtime.CompilerServices; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Core.ConsensusRequests; namespace Nethermind.Facade.Eth; @@ -78,7 +79,10 @@ public BlockForRpc(Block block, bool includeFullTransactionData, ISpecProvider s StateRoot = block.StateRoot; Timestamp = block.Timestamp; TotalDifficulty = block.TotalDifficulty ?? 0; - Transactions = includeFullTransactionData ? block.Transactions.Select((t, idx) => new TransactionForRpc(block.Hash, block.Number, idx, t, block.BaseFeePerGas)).ToArray() : block.Transactions.Select(t => t.Hash).OfType().ToArray(); + Transactions = (includeFullTransactionData + ? block.Transactions.Select((t, idx) => TransactionForRpc.FromTransaction(t, block.Hash, block.Number, idx, block.BaseFeePerGas, specProvider.ChainId)) + : block.Transactions.Select(t => t.Hash).OfType()) + .ToArray(); TransactionsRoot = block.TxRoot; Uncles = block.Uncles.Select(o => o.Hash); Withdrawals = block.Withdrawals; diff --git a/src/Nethermind/Nethermind.Facade/Eth/IFromTransaction.cs b/src/Nethermind/Nethermind.Facade/Eth/IFromTransaction.cs new file mode 100644 index 00000000000..cebf8c7b8d2 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/IFromTransaction.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth; + +public interface IFromTransaction : ITxTyped where T : TransactionForRpc +{ + static abstract T FromTransaction(Transaction tx, TransactionConverterExtraData extraData); +} + +public readonly struct TransactionConverterExtraData +{ + public ulong? ChainId { get; init; } + public Hash256? BlockHash { get; init; } + public long? BlockNumber { get; init; } + public int? TxIndex { get; init; } + public UInt256? BaseFee { get; init; } + public TxReceipt Receipt { get; init; } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/ITxTyped.cs b/src/Nethermind/Nethermind.Facade/Eth/ITxTyped.cs new file mode 100644 index 00000000000..72674318cca --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/ITxTyped.cs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; + +namespace Nethermind.Facade.Eth; + +public interface ITxTyped +{ + static abstract TxType TxType { get; } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs new file mode 100644 index 00000000000..05e3a368451 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Int256; +using Nethermind.Serialization.Json; +using System.Text.Json.Serialization; +using System.Text.Json; +using System; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +[JsonConverter(typeof(JsonConverter))] +public class AccessListForRpc +{ + private readonly IEnumerable _items; + + [JsonConstructor] + public AccessListForRpc() { } + + private AccessListForRpc(IEnumerable items) + { + _items = items; + } + + private class Item + { + public Address Address { get; set; } + + [JsonConverter(typeof(StorageCellIndexConverter))] + public IEnumerable? StorageKeys { get; set; } + + [JsonConstructor] + public Item() { } + + public Item(Address address, IEnumerable storageKeys) + { + Address = address; + StorageKeys = storageKeys; + } + } + + public static AccessListForRpc FromAccessList(AccessList? accessList) => + accessList is null + ? new AccessListForRpc([]) + : new AccessListForRpc(accessList.Select(item => new Item(item.Address, [.. item.StorageKeys]))); + + public AccessList ToAccessList() + { + AccessList.Builder builder = new(); + foreach (Item item in _items) + { + builder.AddAddress(item.Address); + foreach (UInt256 index in item.StorageKeys ?? []) + { + builder.AddStorage(index); + } + } + + return builder.Build(); + } + + public class JsonConverter : JsonConverter + { + public override AccessListForRpc? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + List? list = JsonSerializer.Deserialize>(ref reader, options); + return list is null ? null : new AccessListForRpc(list); + } + + public override void Write(Utf8JsonWriter writer, AccessListForRpc value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value._items, options); + } + } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListTransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListTransactionForRpc.cs new file mode 100644 index 00000000000..586769ba437 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListTransactionForRpc.cs @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class AccessListTransactionForRpc : LegacyTransactionForRpc, IFromTransaction +{ + public new static TxType TxType => TxType.AccessList; + + public override TxType? Type => TxType; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public AccessListForRpc? AccessList { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? YParity { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public sealed override ulong? ChainId { get; set; } + + [JsonConstructor] + public AccessListTransactionForRpc() { } + + public AccessListTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, ulong? chainId = null) + : base(transaction, txIndex, blockHash, blockNumber) + { + AccessList = AccessListForRpc.FromAccessList(transaction.AccessList); + YParity = transaction.Signature?.RecoveryId ?? 0; + ChainId = transaction.ChainId ?? chainId ?? BlockchainIds.Mainnet; + V = YParity ?? 0; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.AccessList = AccessList?.ToAccessList() ?? Core.Eip2930.AccessList.Empty; + + return tx; + } + + public new static AccessListTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, chainId: extraData.ChainId); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs new file mode 100644 index 00000000000..37f7d88d15b --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core; +using Nethermind.Int256; +using System.Text.Json.Serialization; +using System.Text.Json; +using System; +using Nethermind.Core.Crypto; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +[JsonConverter(typeof(JsonConverter))] +public class AuthorizationListForRpc +{ + private readonly IEnumerable _tuples; + + [JsonConstructor] + public AuthorizationListForRpc() { } + + private AuthorizationListForRpc(IEnumerable tuples) + { + _tuples = tuples; + } + + private class Tuple + { + public UInt256 ChainId { get; set; } + public ulong Nonce { get; set; } + public Address Address { get; set; } + public UInt256 YParity { get; set; } + public UInt256 S { get; set; } + public UInt256 R { get; set; } + + [JsonConstructor] + public Tuple() { } + + public Tuple(UInt256 chainId, ulong nonce, Address address, UInt256 yParity, UInt256 s, UInt256 r) + { + ChainId = chainId; + Nonce = nonce; + Address = address; + YParity = yParity; + S = s; + R = r; + } + } + + public static AuthorizationListForRpc FromAuthorizationList(IEnumerable? authorizationList) => + authorizationList is null + ? new AuthorizationListForRpc([]) + : new AuthorizationListForRpc(authorizationList.Select(tuple => + new Tuple(tuple.ChainId, + tuple.Nonce, + tuple.CodeAddress, + tuple.AuthoritySignature.RecoveryId, + new UInt256(tuple.AuthoritySignature.S), + new UInt256(tuple.AuthoritySignature.R)))); + + public AuthorizationTuple[] ToAuthorizationList() => _tuples + .Select(tuple => new AuthorizationTuple( + (ulong)tuple.ChainId, + tuple.Address, + tuple.Nonce, + new Signature(tuple.R, tuple.S, (ulong)tuple.YParity)) + ).ToArray(); + + public class JsonConverter : JsonConverter + { + public override AuthorizationListForRpc? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + List? list = JsonSerializer.Deserialize>(ref reader, options); + return list is null ? null : new AuthorizationListForRpc(list); + } + + public override void Write(Utf8JsonWriter writer, AuthorizationListForRpc value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value._tuples, options); + } + } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/BlobTransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/BlobTransactionForRpc.cs new file mode 100644 index 00000000000..4b7cd41f095 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/BlobTransactionForRpc.cs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class BlobTransactionForRpc : EIP1559TransactionForRpc, IFromTransaction +{ + public new static TxType TxType => TxType.Blob; + + public override TxType? Type => TxType; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? MaxFeePerBlobGas { get; set; } + + // TODO: Each item should be a 32 byte array + // Currently we don't enforce this (hashes can have any length) + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public byte[][]? BlobVersionedHashes { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public byte[]? Blobs { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? GasPrice { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? V { get; set; } + + [JsonConstructor] + public BlobTransactionForRpc() { } + + public BlobTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, UInt256? baseFee = null, ulong? chainId = null) + : base(transaction, txIndex, blockHash, blockNumber, baseFee, chainId) + { + MaxFeePerBlobGas = transaction.MaxFeePerBlobGas ?? 0; + BlobVersionedHashes = transaction.BlobVersionedHashes ?? []; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.MaxFeePerBlobGas = MaxFeePerBlobGas; + tx.BlobVersionedHashes = BlobVersionedHashes; + + return tx; + } + + public new static BlobTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, baseFee: extraData.BaseFee, chainId: extraData.ChainId); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/EIP1559TransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/EIP1559TransactionForRpc.cs new file mode 100644 index 00000000000..fe5c98f4625 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/EIP1559TransactionForRpc.cs @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Extensions; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class EIP1559TransactionForRpc : AccessListTransactionForRpc, IFromTransaction +{ + public new static TxType TxType => TxType.EIP1559; + + public override TxType? Type => TxType.EIP1559; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? MaxPriorityFeePerGas { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? MaxFeePerGas { get; set; } + + [JsonConstructor] + public EIP1559TransactionForRpc() { } + + public EIP1559TransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, UInt256? baseFee = null, ulong? chainId = null) + : base(transaction, txIndex, blockHash, blockNumber, chainId) + { + MaxFeePerGas = transaction.MaxFeePerGas; + MaxPriorityFeePerGas = transaction.MaxPriorityFeePerGas; + GasPrice = baseFee is not null + ? transaction.CalculateEffectiveGasPrice(eip1559Enabled: true, baseFee.Value) + : transaction.MaxFeePerGas; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.GasPrice = MaxPriorityFeePerGas ?? 0; + tx.DecodedMaxFeePerGas = MaxFeePerGas ?? 0; + + return tx; + } + + public override bool ShouldSetBaseFee() => + base.ShouldSetBaseFee() || MaxFeePerGas.IsPositive() || MaxPriorityFeePerGas.IsPositive(); + + public new static EIP1559TransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, baseFee: extraData.BaseFee, chainId: extraData.ChainId); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/LegacyTransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/LegacyTransactionForRpc.cs new file mode 100644 index 00000000000..3b77fc2dc88 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/LegacyTransactionForRpc.cs @@ -0,0 +1,114 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class LegacyTransactionForRpc : TransactionForRpc, ITxTyped, IFromTransaction +{ + public static TxType TxType => TxType.Legacy; + + public override TxType? Type => TxType; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? Nonce { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Address? To { get; set; } + + // NOTE: This field exists only during deserialization according to the Ethereum JSON-RPC spec. + // No transaction types include a `From` field when serializing. + // For backwards compatibility with previous Nethermind versions we also serialize it. + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Address? From { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public long? Gas { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? Value { get; set; } + + // Required for compatibility with some CLs like Prysm + // Accept during deserialization, ignore during serialization + // See: https://github.com/NethermindEth/nethermind/pull/6067 + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public byte[]? Data { set { Input = value; } private get { return null; } } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public byte[]? Input { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? GasPrice { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public virtual ulong? ChainId { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? V { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? R { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? S { get; set; } + + [JsonConstructor] + public LegacyTransactionForRpc() { } + + public LegacyTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null) + : base(transaction, txIndex, blockHash, blockNumber) + { + Nonce = transaction.Nonce; + To = transaction.To; + From = transaction.SenderAddress; + Gas = transaction.GasLimit; + Value = transaction.Value; + Input = transaction.Data.AsArray() ?? []; + GasPrice = transaction.GasPrice; + ChainId = transaction.ChainId; + + R = new UInt256(transaction.Signature?.R ?? [], true); + S = new UInt256(transaction.Signature?.S ?? [], true); + V = transaction.Signature?.V ?? 0; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.Nonce = Nonce ?? 0; // TODO: Should we pick the last nonce? + tx.To = To; + tx.GasLimit = Gas ?? 90_000; + tx.Value = Value ?? 0; + tx.Data = Input; + tx.GasPrice = GasPrice ?? 20.GWei(); + tx.ChainId = ChainId; + tx.SenderAddress = From ?? Address.SystemUser; + + return tx; + } + + // TODO: Can we remove this code? + public override void EnsureDefaults(long? gasCap) + { + if (gasCap is null || gasCap == 0) + gasCap = long.MaxValue; + + Gas = Gas is null || Gas == 0 + ? gasCap + : Math.Min(gasCap.Value, Gas.Value); + + From ??= Address.SystemUser; + } + + public override bool ShouldSetBaseFee() => GasPrice.IsPositive(); + + public static LegacyTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) => + new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/SetCodeTransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/SetCodeTransactionForRpc.cs new file mode 100644 index 00000000000..5dcf8d65fc2 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/SetCodeTransactionForRpc.cs @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class SetCodeTransactionForRpc : EIP1559TransactionForRpc, IFromTransaction +{ + public new static TxType TxType => TxType.SetCode; + + public override TxType? Type => TxType; + + public AuthorizationListForRpc AuthorizationList { get; set; } + + #region Deprecated fields + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? GasPrice { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? V { get; set; } + #endregion + + [JsonConstructor] + public SetCodeTransactionForRpc() { } + + public SetCodeTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, UInt256? baseFee = null, ulong? chainId = null) + : base(transaction, txIndex, blockHash, blockNumber, baseFee, chainId) + { + AuthorizationList = AuthorizationListForRpc.FromAuthorizationList(transaction.AuthorizationList); + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.AuthorizationList = AuthorizationList?.ToAuthorizationList() ?? []; + + return tx; + } + + public new static SetCodeTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, baseFee: extraData.BaseFee, chainId: extraData.ChainId); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/TransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/TransactionForRpc.cs new file mode 100644 index 00000000000..9a614aca1b4 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/TransactionForRpc.cs @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +/// +/// Base class for all output Nethermind RPC Transactions. +/// +/// +/// Input: +/// JSON -> (through , a registry of [ => subtypes) +/// -> (through an overloaded method) +/// Output: +/// -> (through , a registry of [ => ) +/// -> JSON (Derived by System.Text.JSON using the runtime type) +/// +[JsonConverter(typeof(JsonConverter))] +public abstract class TransactionForRpc +{ + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual TxType? Type => null; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Hash256? Hash { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public long? TransactionIndex { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Hash256? BlockHash { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public long? BlockNumber { get; set; } + + [JsonConstructor] + protected TransactionForRpc() { } + + protected TransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null) + { + Hash = transaction.Hash; + TransactionIndex = txIndex; + BlockHash = blockHash; + BlockNumber = blockNumber; + } + + public virtual Transaction ToTransaction() + { + return new Transaction + { + Type = Type ?? default, + }; + } + + public abstract void EnsureDefaults(long? gasCap); + public abstract bool ShouldSetBaseFee(); + + internal class JsonConverter : JsonConverter + { + // NOTE: Should we default to a specific TxType? + private const TxType DefaultTxType = TxType.Legacy; + private static readonly Type[] _types = new Type[Transaction.MaxTxType + 1]; + + public JsonConverter() + { + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + } + + internal static void RegisterTransactionType() where T : TransactionForRpc, ITxTyped + => _types[(int)T.TxType] = typeof(T); + + public override TransactionForRpc? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Copy the reader so we can do a double parse: + // The first parse extract the `Type` while the second parses the entire Transaction + Utf8JsonReader txTypeReader = reader; + TransactionType type = JsonSerializer.Deserialize(ref txTypeReader, options); + + TxType discriminator = (TxType)(type.Type ?? (ulong)DefaultTxType); + + return _types.TryGetByTxType(discriminator, out Type concreteTxType) + ? (TransactionForRpc?)JsonSerializer.Deserialize(ref reader, concreteTxType, options) + : throw new JsonException("Unknown transaction type"); + } + + public override void Write(Utf8JsonWriter writer, TransactionForRpc value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, value.GetType(), options); + } + } + + /// + /// Used only for finding the field when deserializing a + /// + private struct TransactionType + { + // Hex value + public ulong? Type { get; set; } + } + + internal class TransactionConverter + { + private delegate TransactionForRpc FromTransactionFunc(Transaction tx, TransactionConverterExtraData extraData); + + private static readonly FromTransactionFunc?[] _fromTransactionFuncs = new FromTransactionFunc?[Transaction.MaxTxType + 1]; + + static TransactionConverter() + { + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + } + + internal static void RegisterTransactionType() where T : TransactionForRpc, IFromTransaction + => _fromTransactionFuncs[(byte)T.TxType] = T.FromTransaction; + + public static TransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + { + return _fromTransactionFuncs.TryGetByTxType(tx.Type, out var FromTransaction) + ? FromTransaction(tx, extraData) + : throw new ArgumentException("No converter for transaction type"); + } + } + + public static void RegisterTransactionType() where T : TransactionForRpc, IFromTransaction + { + JsonConverter.RegisterTransactionType(); + TransactionConverter.RegisterTransactionType(); + } + + public static TransactionForRpc FromTransaction(Transaction transaction, Hash256? blockHash = null, long? blockNumber = null, int? txIndex = null, UInt256? baseFee = null, ulong? chainId = null) => + TransactionConverter.FromTransaction(transaction, new() + { + ChainId = chainId, + TxIndex = txIndex, + BlockHash = blockHash, + BlockNumber = blockNumber, + BaseFee = baseFee + }); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs deleted file mode 100644 index 5b054f48106..00000000000 --- a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs +++ /dev/null @@ -1,257 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Collections.Generic; -using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Core.Eip2930; -using Nethermind.Core.Extensions; -using Nethermind.Int256; - -namespace Nethermind.Facade.Eth; - -public class TransactionForRpc -{ - // HACK: To ensure that serialized Txs always have a `ChainId` we keep the last loaded `ChainSpec`. - // See: https://github.com/NethermindEth/nethermind/pull/6061#discussion_r1321634914 - public static UInt256? DefaultChainId { get; set; } - - public TransactionForRpc(Transaction transaction) : this(null, null, null, transaction) { } - - public TransactionForRpc(Hash256? blockHash, long? blockNumber, int? txIndex, Transaction transaction, UInt256? baseFee = null) - { - Hash = transaction.Hash; - Nonce = transaction.Nonce; - BlockHash = blockHash; - BlockNumber = blockNumber; - TransactionIndex = txIndex; - From = transaction.SenderAddress; - To = transaction.To; - Value = transaction.Value; - GasPrice = transaction.GasPrice; - Gas = transaction.GasLimit; - Input = Data = transaction.Data.AsArray(); - if (transaction.Supports1559) - { - GasPrice = baseFee is not null - ? transaction.CalculateEffectiveGasPrice(true, baseFee.Value) - : transaction.MaxFeePerGas; - MaxFeePerGas = transaction.MaxFeePerGas; - MaxPriorityFeePerGas = transaction.MaxPriorityFeePerGas; - } - if (transaction.Type > TxType.Legacy) - { - ChainId = transaction.ChainId - ?? DefaultChainId - ?? BlockchainIds.Mainnet; - } - else - { - ChainId = transaction.ChainId; - } - Type = transaction.Type; - if (transaction.SupportsAccessList) - { - AccessList = transaction.AccessList is null ? Array.Empty() : AccessListItemForRpc.FromAccessList(transaction.AccessList); - } - else - { - AccessList = null; - } - AuthorizationList = transaction.SupportsAuthorizationList - ? transaction.AuthorizationList is null - ? Array.Empty() - : AuthorizationTupleForRpc.FromAuthorizationList(transaction.AuthorizationList) - : null; - MaxFeePerBlobGas = transaction.MaxFeePerBlobGas; - BlobVersionedHashes = transaction.BlobVersionedHashes; - - Signature? signature = transaction.Signature; - if (signature is not null) - { - - YParity = transaction.SupportsAccessList ? signature.RecoveryId : null; - R = new UInt256(signature.R, true); - S = new UInt256(signature.S, true); - // V must be null for non-legacy transactions. Temporarily set to recovery id for Geth compatibility. - // See https://github.com/ethereum/go-ethereum/issues/27727 - V = transaction.Type == TxType.Legacy ? signature.V : signature.RecoveryId; - } - } - - // ReSharper disable once UnusedMember.Global - [JsonConstructor] - public TransactionForRpc() { } - - public Hash256? SourceHash { get; set; } - public UInt256? Mint { get; set; } - public bool? IsSystemTx { get; set; } // this is the IsOpSystemTransaction flag - - public Hash256? Hash { get; set; } - public UInt256? Nonce { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public Hash256? BlockHash { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public long? BlockNumber { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public long? TransactionIndex { get; set; } - - public Address? From { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public Address? To { get; set; } - - public UInt256? Value { get; set; } - public UInt256? GasPrice { get; set; } - - public UInt256? MaxPriorityFeePerGas { get; set; } - - public UInt256? MaxFeePerGas { get; set; } - public long? Gas { get; set; } - - // Required for compatibility with some CLs like Prysm - // Accept during deserialization, ignore during serialization - // See: https://github.com/NethermindEth/nethermind/pull/6067 - [JsonPropertyName(nameof(Data))] - [JsonConverter(typeof(DataConverter))] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public byte[]? Data { set { Input = value; } private get { return null; } } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public byte[]? Input { get; set; } - - public UInt256? ChainId { get; set; } - - public TxType Type { get; set; } - - public IEnumerable? AccessList { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public IEnumerable? AuthorizationList { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public UInt256? MaxFeePerBlobGas { get; set; } // eip4844 - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public byte[][]? BlobVersionedHashes { get; set; } // eip4844 - - public UInt256? V { get; set; } - - public UInt256? S { get; set; } - - public UInt256? R { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public UInt256? YParity { get; set; } - - public Transaction ToTransactionWithDefaults(ulong? chainId = null) => ToTransactionWithDefaults(chainId); - - public T ToTransactionWithDefaults(ulong? chainId = null) where T : Transaction, new() - { - T tx = new() - { - GasLimit = Gas ?? 90000, - GasPrice = GasPrice ?? 20.GWei(), - Nonce = (ulong)(Nonce ?? 0), // here pick the last nonce? - To = To, - SenderAddress = From, - Value = Value ?? 0, - Data = Input, - Type = Type, - AccessList = TryGetAccessList(), - ChainId = chainId, - DecodedMaxFeePerGas = MaxFeePerGas ?? 0, - Hash = Hash - }; - - if (tx.Supports1559) - { - tx.GasPrice = MaxPriorityFeePerGas ?? 0; - } - - if (tx.SupportsBlobs) - { - tx.MaxFeePerBlobGas = MaxFeePerBlobGas; - tx.BlobVersionedHashes = BlobVersionedHashes; - } - - return tx; - } - - public Transaction ToTransaction(ulong? chainId = null) => ToTransaction(); - - public T ToTransaction(ulong? chainId = null) where T : Transaction, new() - { - byte[]? data = Input; - - T tx = new() - { - GasLimit = Gas ?? 0, - GasPrice = GasPrice ?? 0, - Nonce = (ulong)(Nonce ?? 0), // here pick the last nonce? - To = To, - SenderAddress = From, - Value = Value ?? 0, - Data = (Memory?)data, - Type = Type, - AccessList = TryGetAccessList(), - ChainId = chainId, - }; - - if (data is null) - { - tx.Data = null; // Yes this is needed... really. Try a debugger. - } - - if (tx.Supports1559) - { - tx.GasPrice = MaxPriorityFeePerGas ?? 0; - tx.DecodedMaxFeePerGas = MaxFeePerGas ?? 0; - } - - if (tx.SupportsBlobs) - { - tx.MaxFeePerBlobGas = MaxFeePerBlobGas; - tx.BlobVersionedHashes = BlobVersionedHashes; - } - - return tx; - } - - private AccessList? TryGetAccessList() => - !Type.IsTxTypeWithAccessList() || AccessList is null - ? null - : AccessListItemForRpc.ToAccessList(AccessList); - - public void EnsureDefaults(long? gasCap) - { - if (gasCap is null || gasCap == 0) - gasCap = long.MaxValue; - - Gas = Gas is null || Gas == 0 - ? gasCap - : Math.Min(gasCap.Value, Gas.Value); - - From ??= Address.SystemUser; - } - - private class DataConverter : JsonConverter - { - public override bool HandleNull { get; } = false; - - public override byte[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return JsonSerializer.Deserialize(ref reader, options); - } - - public override void Write(Utf8JsonWriter writer, byte[]? value, JsonSerializerOptions options) - { - throw new NotSupportedException(); - } - } -} diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs index 7f4636d56be..db2854c0003 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs @@ -3,9 +3,13 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Nethermind.Api; using Nethermind.Api.Extensions; using Nethermind.Blockchain.Synchronization; @@ -126,39 +130,22 @@ private async Task Initialize(CancellationToken cancellationToken) if (_api.Synchronizer is null) { - BlockDownloaderFactory blockDownloaderFactory = new BlockDownloaderFactory( - _api.SpecProvider!, - _api.BlockValidator!, - _api.SealValidator!, - _api.BetterPeerStrategy!, - _api.LogManager); + if (_api.ChainSpec.SealEngineType == SealEngineType.Clique) + _syncConfig.NeedToWaitForHeader = true; // Should this be in chainspec itself? - _api.Synchronizer ??= new Synchronizer( - _api.DbProvider, - _api.NodeStorageFactory.WrapKeyValueStore(_api.DbProvider.StateDb), - _api.SpecProvider!, - _api.BlockTree, - _api.ReceiptStorage!, - _api.SyncPeerPool, - _api.NodeStatsManager!, - _syncConfig, - blockDownloaderFactory, - _api.Pivot, - _api.ProcessExit!, - _api.BetterPeerStrategy, - _api.ChainSpec, - _api.StateReader!, - _api.LogManager); - } + ContainerBuilder builder = new ContainerBuilder(); + _api.ConfigureContainerBuilderFromApiWithNetwork(builder) + .AddSingleton(No.BeaconSync); + builder.RegisterModule(new SynchronizerModule(_syncConfig)); + IContainer container = builder.Build(); - _api.SyncModeSelector = _api.Synchronizer.SyncModeSelector; - _api.SyncProgressResolver = _api.Synchronizer.SyncProgressResolver; + _api.ApiWithNetworkServiceContainer = container; + _api.DisposeStack.Append(container); + } _api.EthSyncingInfo = new EthSyncingInfo(_api.BlockTree, _api.ReceiptStorage!, _syncConfig, - _api.SyncModeSelector, _api.SyncProgressResolver, _api.LogManager); + _api.SyncModeSelector!, _api.SyncProgressResolver!, _api.LogManager); _api.TxGossipPolicy.Policies.Add(new SyncedTxGossipPolicy(_api.SyncModeSelector)); - _api.DisposeStack.Push(_api.SyncModeSelector); - _api.DisposeStack.Push(_api.Synchronizer); ISyncServer syncServer = _api.SyncServer = new SyncServer( _api.TrieStore!.TrieNodeRlpStore, diff --git a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs index db5341d33b4..bc77defb9d5 100644 --- a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs +++ b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs @@ -144,7 +144,7 @@ public virtual async Task Execute(CancellationToken cancellationToken) StepDependencyException.ThrowIfNull(_api.TxPoolInfoProvider); - TxPoolRpcModule txPoolRpcModule = new(_api.TxPoolInfoProvider, _api.LogManager); + TxPoolRpcModule txPoolRpcModule = new(_api.TxPoolInfoProvider, _api.SpecProvider); rpcModuleProvider.RegisterSingle(txPoolRpcModule); StepDependencyException.ThrowIfNull(_api.SyncServer); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Data/AccessListItemForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Data/AccessListItemForRpcTests.cs deleted file mode 100644 index ae948e1d693..00000000000 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Data/AccessListItemForRpcTests.cs +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; -using FluentAssertions; -using Nethermind.Core; -using Nethermind.Core.Eip2930; -using Nethermind.Core.Test.Builders; -using Nethermind.Facade.Eth; -using Nethermind.Int256; -using Nethermind.JsonRpc.Data; -using NUnit.Framework; - -namespace Nethermind.JsonRpc.Test.Data; - -public class AccessListItemForRpcTests -{ - [Test] - public void Single_address_with_no_storage() - { - Address address = TestItem.AddressA; - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - AccessListItemForRpc[] expected = { new(address, new UInt256[] { }) }; - forRpc.Should().BeEquivalentTo(expected); - } - - [Test] - public void Single_address_with_multiple_storage_keys() - { - Address address = TestItem.AddressA; - UInt256 storageKey1 = (UInt256)1; - UInt256 storageKey2 = (UInt256)2; - UInt256 storageKey3 = (UInt256)3; - - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey2) - .AddStorage(storageKey3) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - AccessListItemForRpc[] expected = { new(address, new[] { storageKey1, storageKey2, storageKey3 }) }; - forRpc.Should().BeEquivalentTo(expected); - } - - [Test] - public void Single_address_with_duplicated_storage_keys() - { - Address address = TestItem.AddressA; - UInt256 storageKey1 = (UInt256)1; - UInt256 storageKey2 = (UInt256)2; - UInt256 storageKey3 = (UInt256)3; - - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey2) - .AddStorage(storageKey3) - .AddStorage(storageKey1) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - - AccessListItemForRpc[] expected = { new(address, new[] { storageKey1, storageKey2, storageKey3, storageKey1 }) }; - forRpc.Should().BeEquivalentTo(expected); - } - - [Test] - public void Duplicated_address_with_multiple_storage_keys() - { - Address address = TestItem.AddressA; - UInt256 storageKey1 = (UInt256)1; - UInt256 storageKey2 = (UInt256)2; - UInt256 storageKey3 = (UInt256)3; - - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey2) - .AddAddress(address) - .AddStorage(storageKey3) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - - AccessListItemForRpc[] expected = { new(address, new[] { storageKey1, storageKey2 }), new(address, new[] { storageKey3 }) }; - forRpc.Should().BeEquivalentTo(expected); - } - - [Test] - public void Duplicated_address_with_duplicated_storage_keys() - { - Address address = TestItem.AddressA; - UInt256 storageKey1 = (UInt256)1; - UInt256 storageKey2 = (UInt256)2; - UInt256 storageKey3 = (UInt256)3; - - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey2) - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey3) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - AccessListItemForRpc[] expected = { new(address, new[] { storageKey1, storageKey2 }), new(address, new[] { storageKey1, storageKey3 }) }; - forRpc.Should().BeEquivalentTo(expected); - } -} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs index f34daa6963f..858fc63ff34 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs @@ -1,16 +1,14 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Collections.Generic; +using System; using FluentAssertions; using FluentAssertions.Json; using Nethermind.Core; using Nethermind.Core.Eip2930; using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; -using Nethermind.Facade.Eth; -using Nethermind.Int256; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Serialization.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; @@ -44,11 +42,12 @@ public void can_serialize_valid_accessList(TxType txType, string txJson) Type = txType, AccessList = GetTestAccessList(), }; - TransactionForRpc transactionForRpc = new(transaction); - + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(transactionForRpc); - JToken.Parse(serialized).Should().BeEquivalentTo(JToken.Parse(txJson)); + var actual = JObject.Parse(serialized).Property("accessList"); + var expected = JObject.Parse(txJson).Property("accessList"); + actual.Should().BeEquivalentTo(expected); } [TestCase(TxType.AccessList, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01","chainId":"0x01","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x1","0x2","0x3","0x5","0x8"]},{"address":"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358","storageKeys":["0x2a"]}]}""")] @@ -60,7 +59,7 @@ public void can_deserialize_valid_accessList(TxType txType, string txJson) Type = txType, AccessList = GetTestAccessList(), }; - TransactionForRpc transactionForRpc = new(transaction); + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(transaction); TransactionForRpc deserializedTxForRpc = _serializer.Deserialize(txJson); @@ -74,7 +73,7 @@ public void can_serialize_null_accessList_to_nothing(TxType txType) { Type = txType, }; - TransactionForRpc rpc = new(transaction); + var rpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(rpc); @@ -89,7 +88,7 @@ public void can_serialize_null_accessList_to_empty_array(TxType txType) { Type = txType, }; - TransactionForRpc rpc = new(transaction); + var rpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(rpc); @@ -101,10 +100,10 @@ public void can_deserialize_null_accessList() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01"}"""; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); + var transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.AccessList); - transactionForRpc.AccessList.Should().BeNull(); + ((AccessListTransactionForRpc)transactionForRpc).AccessList.Should().BeNull(); } [Test] @@ -113,92 +112,100 @@ public void can_deserialize_no_accessList() string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x0"}"""; TransactionForRpc transactionForRpc = _serializer.Deserialize(json); + var transaction = transactionForRpc.ToTransaction(); - transactionForRpc.Type.Should().Be(TxType.Legacy); - transactionForRpc.AccessList.Should().BeNull(); + transaction.Type.Should().Be(TxType.Legacy); + transaction.AccessList.Should().BeNull(); } [TestCase(TxType.AccessList, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x1","chainId":"0x1","accessList":[]}""")] [TestCase(TxType.EIP1559, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","maxPriorityFeePerGas":"0x0","maxFeePerGas":"0x0","gas":"0x0","input":null,"type":"0x2","chainId":"0x1","accessList":[]}""")] public void can_serialize_empty_accessList(TxType txType, string txJson) { - AccessList.Builder builder = new(); Transaction transaction = new() { Type = txType, - AccessList = builder.Build(), + AccessList = AccessList.Empty, }; - TransactionForRpc transactionForRpc = new(transaction); - + var transactionForRpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(transactionForRpc); - JToken.Parse(serialized).Should().BeEquivalentTo(JToken.Parse(txJson)); + var actual = JObject.Parse(serialized).Property("accessList"); + var expected = JObject.Parse(txJson).Property("accessList"); + actual.Should().BeEquivalentTo(expected); } [Test] public void can_deserialize_empty_accessList() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01","accessList":[]}"""; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.AccessList); - transactionForRpc.AccessList!.Should().BeEmpty(); + + var expected = AccessList.Empty; + var actual = ((AccessListTransactionForRpc)transactionForRpc).AccessList!.ToAccessList(); + actual.Should().BeEquivalentTo(expected); } [TestCase(TxType.AccessList, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x1","chainId":"0x1","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":[]}]}""")] [TestCase(TxType.EIP1559, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","maxPriorityFeePerGas":"0x0","maxFeePerGas":"0x0","gas":"0x0","input":null,"type":"0x2","chainId":"0x1","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":[]}]}""")] public void can_serialize_accessList_with_empty_storageKeys(TxType txType, string txJson) { - AccessList.Builder builder = new AccessList.Builder() - .AddAddress(TestItem.AddressA); + AccessList accessList = new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .Build(); Transaction transaction = new() { Type = txType, - AccessList = builder.Build(), + AccessList = accessList, }; - TransactionForRpc transactionForRpc = new(transaction); - + var transactionForRpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(transactionForRpc); - JToken.Parse(serialized).Should().BeEquivalentTo(JToken.Parse(txJson)); + var actual = JObject.Parse(serialized).Property("accessList"); + var expected = JObject.Parse(txJson).Property("accessList"); + actual.Should().BeEquivalentTo(expected); } [Test] public void can_deserialize_accessList_with_empty_storageKeys() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":[]}]}"""; - object[] accessList = { new AccessListItemForRpc(TestItem.AddressA, new HashSet()) }; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.AccessList); - transactionForRpc.AccessList.Should().BeEquivalentTo(accessList); + + AccessList expected = new AccessList.Builder().AddAddress(TestItem.AddressA).Build(); + AccessList actual = ((AccessListTransactionForRpc)transactionForRpc).AccessList!.ToAccessList(); + expected.Should().BeEquivalentTo(actual); } [Test] public void can_deserialize_accessList_with_null_storageKeys() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099"}]}"""; - object[] accessList = { new AccessListItemForRpc(TestItem.AddressA, null) }; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.AccessList); - transactionForRpc.AccessList.Should().BeEquivalentTo(accessList); + + AccessList expected = new AccessList.Builder().AddAddress(TestItem.AddressA).Build(); + AccessList actual = ((AccessListTransactionForRpc)transactionForRpc).AccessList!.ToAccessList(); + expected.Should().BeEquivalentTo(actual); } [Test] public void can_deserialize_accessList_with_null_storageKeys_and_eip1559_txType() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","maxFeePerGas":"0x10","gas":"0x0","input":null,"type":"0x02","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099"}]}"""; - AccessListItemForRpc[] accessList = { new(TestItem.AddressA, null) }; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.EIP1559); - transactionForRpc.AccessList.Should().BeEquivalentTo(accessList); + + AccessList expected = new AccessList.Builder().AddAddress(TestItem.AddressA).Build(); + AccessList actual = ((EIP1559TransactionForRpc)transactionForRpc).AccessList!.ToAccessList(); + expected.Should().BeEquivalentTo(actual); } [Test] @@ -208,8 +215,8 @@ public void can_deserialize_not_provided_txType() TransactionForRpc transactionForRpc = _serializer.Deserialize(json); - // if there is not TxType provided, default value should be TxType.Legacy equal 0 - transactionForRpc.Type.Should().Be(0); + // If there is not `TxType` provided, default value should be `TxType.Legacy` + transactionForRpc.Type.Should().Be(TxType.Legacy); } [Test] @@ -219,8 +226,8 @@ public void can_deserialize_direct_null_txType() TransactionForRpc transactionForRpc = _serializer.Deserialize(json); - // if there is null TxType provided, still default value should be TxType.Legacy equal 0 - transactionForRpc.Type.Should().Be(0); + // If there is not `TxType` provided, default value should be `TxType.Legacy` + transactionForRpc.Type.Should().Be(TxType.Legacy); } [TestCase(TxType.AccessList)] @@ -231,8 +238,11 @@ public void can_convert_fromTransaction_toTransactionForRpc_and_back(TxType txTy { Type = txType, AccessList = GetTestAccessList(), + ChainId = BlockchainIds.Mainnet, + SenderAddress = Address.SystemUser, + Data = Memory.Empty, }; - TransactionForRpc transactionForRpc = new(transaction); + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(transaction); Transaction afterConversion = transactionForRpc.ToTransaction(); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Eip1186/ProofConverterTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Eip1186/ProofConverterTests.cs index cb0b0637829..986fd030146 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Eip1186/ProofConverterTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Eip1186/ProofConverterTests.cs @@ -38,14 +38,14 @@ public void Storage_proofs_have_values_set_complex_3_setup() storageTree.Set(Keccak.Compute(c).Bytes, Rlp.Encode(Bytes.FromHexString("0xab56000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(d).Bytes, Rlp.Encode(Bytes.FromHexString("0xab78000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(e).Bytes, Rlp.Encode(Bytes.FromHexString("0xab9a000000000000000000000000000000000000000000000000000000000000000000000000000000"))); - storageTree.Commit(0); + storageTree.Commit(); byte[] code = new byte[] { 1, 2, 3 }; Account account1 = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA, new byte[][] { a, b, c, d, e }); tree.Accept(accountProofCollector, tree.RootHash); @@ -72,14 +72,14 @@ public void Does_not_fail_when_proofs_are_longer_than_number_of_proofs_regressio storageTree.Set(Keccak.Compute(c).Bytes, Rlp.Encode(Bytes.FromHexString("0xab56000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(d).Bytes, Rlp.Encode(Bytes.FromHexString("0xab78000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(e).Bytes, Rlp.Encode(Bytes.FromHexString("0xab9a000000000000000000000000000000000000000000000000000000000000000000000000000000"))); - storageTree.Commit(0); + storageTree.Commit(); byte[] code = new byte[] { 1, 2, 3 }; Account account1 = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA, new byte[][] { a }); tree.Accept(accountProofCollector, tree.RootHash); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcServiceTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcServiceTests.cs index 272ad1d2a8e..c8e1ac1dabd 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcServiceTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcServiceTests.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.IO.Abstractions; -using System.Linq; -using System.Reflection; using System.Text.Json; using FluentAssertions; @@ -13,13 +11,12 @@ using Nethermind.Config; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Int256; -using Nethermind.JsonRpc.Data; using Nethermind.JsonRpc.Modules; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.JsonRpc.Modules.Net; @@ -56,19 +53,19 @@ public void TearDown() private ILogManager _logManager = null!; private JsonRpcContext _context = null!; - private JsonRpcResponse TestRequest(T module, string method, params string[]? parameters) where T : IRpcModule + private JsonRpcResponse TestRequest(T module, string method, params object?[]? parameters) where T : IRpcModule { var pool = new SingletonModulePool(new SingletonFactory(module), true); return TestRequestWithPool(pool, method, parameters); } - private JsonRpcResponse TestRequestWithPool(IRpcModulePool pool, string method, params string[]? parameters) where T : IRpcModule + private JsonRpcResponse TestRequestWithPool(IRpcModulePool pool, string method, params object?[]? parameters) where T : IRpcModule { RpcModuleProvider moduleProvider = new(new FileSystem(), _configurationProvider.GetConfig(), LimboLogs.Instance); moduleProvider.Register(pool); _jsonRpcService = new JsonRpcService(moduleProvider, _logManager, _configurationProvider.GetConfig()); - JsonRpcRequest request = RpcTest.GetJsonRequest(method, parameters); + JsonRpcRequest request = RpcTest.BuildJsonRequest(method, parameters); JsonRpcResponse response = _jsonRpcService.SendRequestAsync(request, _context).Result; Assert.That(response.Id, Is.EqualTo(request.Id)); return response; @@ -111,11 +108,9 @@ public void CanRunEthSimulateV1Empty() [Test] public void CanHandleOptionalArguments() { - EthereumJsonSerializer serializer = new(); - string serialized = serializer.Serialize(new TransactionForRpc()); IEthRpcModule ethRpcModule = Substitute.For(); ethRpcModule.eth_call(Arg.Any()).ReturnsForAnyArgs(_ => ResultWrapper.Success("0x1")); - JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", serialized) as JsonRpcSuccessResponse; + JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", new LegacyTransactionForRpc()) as JsonRpcSuccessResponse; Assert.That(response?.Result, Is.EqualTo("0x1")); } @@ -175,13 +170,10 @@ public void GetNewFilterTest() [Test] public void Eth_call_is_working_with_implicit_null_as_the_last_argument() { - EthereumJsonSerializer serializer = new(); IEthRpcModule ethRpcModule = Substitute.For(); ethRpcModule.eth_call(Arg.Any(), Arg.Any()).ReturnsForAnyArgs(x => ResultWrapper.Success("0x")); - string serialized = serializer.Serialize(new TransactionForRpc()); - - JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", serialized) as JsonRpcSuccessResponse; + JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", new LegacyTransactionForRpc()) as JsonRpcSuccessResponse; Assert.That(response?.Result, Is.EqualTo("0x")); } @@ -189,13 +181,10 @@ public void Eth_call_is_working_with_implicit_null_as_the_last_argument() [TestCase(null)] public void Eth_call_is_working_with_explicit_null_as_the_last_argument(string? nullValue) { - EthereumJsonSerializer serializer = new(); IEthRpcModule ethRpcModule = Substitute.For(); ethRpcModule.eth_call(Arg.Any(), Arg.Any()).ReturnsForAnyArgs(x => ResultWrapper.Success("0x")); - string serialized = serializer.Serialize(new TransactionForRpc()); - - JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", serialized, nullValue!) as JsonRpcSuccessResponse; + JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", new LegacyTransactionForRpc(), nullValue) as JsonRpcSuccessResponse; Assert.That(response?.Result, Is.EqualTo("0x")); } @@ -204,17 +193,13 @@ public void Eth_getTransactionReceipt_properly_fails_given_wrong_parameters() { IEthRpcModule ethRpcModule = Substitute.For(); - string[] parameters = { - """["0x80757153e93d1b475e203406727b62a501187f63e23b8fa999279e219ee3be71"]""" - }; - JsonRpcResponse response = TestRequest(ethRpcModule, "eth_getTransactionReceipt", parameters); - - response.Should() - .BeAssignableTo() - .Which - .Error.Should().NotBeNull(); - Error error = (response as JsonRpcErrorResponse)!.Error!; - error.Code.Should().Be(ErrorCodes.InvalidParams); + string[] parameters = + [ + """["0x80757153e93d1b475e203406727b62a501187f63e23b8fa999279e219ee3be71"]""" + ]; + JsonRpcErrorResponse? response = TestRequest(ethRpcModule, "eth_getTransactionReceipt", parameters) as JsonRpcErrorResponse; + + Assert.That(response?.Error?.Code, Is.EqualTo(ErrorCodes.InvalidParams)); } [Test] @@ -263,13 +248,10 @@ public void BlockForRpc_should_expose_withdrawals_if_any((bool Expected, Block B new[] { (true, Build.A.Block - .WithWithdrawals(new[] - { - Build.A.Withdrawal - .WithAmount(1) - .WithRecipient(TestItem.AddressA) - .TestObject - }) + .WithWithdrawals(Build.A.Withdrawal + .WithAmount(1) + .WithRecipient(TestItem.AddressA) + .TestObject) .TestObject ), diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcSocketsClientTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcSocketsClientTests.cs index 34b6648bf5e..e011481dbb2 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcSocketsClientTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcSocketsClientTests.cs @@ -80,7 +80,7 @@ static async Task CountNumberOfMessages(Socket socket, CancellationToken to byte[] buffer = new byte[10]; while (true) { - ReceiveResult? result = await stream.ReceiveAsync(buffer); + ReceiveResult? result = await stream.ReceiveAsync(buffer).ConfigureAwait(false); if (result?.EndOfMessage == true) { messages++; @@ -108,7 +108,7 @@ static async Task CountNumberOfMessages(Socket socket, CancellationToken to Task sendMessages = Task.Run(async () => { using Socket socket = new(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(ipEndPoint); + await socket.ConnectAsync(ipEndPoint).ConfigureAwait(false); using IpcSocketMessageStream stream = new(socket); using JsonRpcSocketsClient client = new( @@ -124,17 +124,17 @@ static async Task CountNumberOfMessages(Socket socket, CancellationToken to for (int i = 0; i < messageCount; i++) { using JsonRpcResult result = JsonRpcResult.Single(RandomSuccessResponse(1_000, () => disposeCount++), default); - await client.SendJsonRpcResult(result); - await Task.Delay(10); + await client.SendJsonRpcResult(result).ConfigureAwait(false); + await Task.Delay(1).ConfigureAwait(false); } disposeCount.Should().Be(messageCount); - await cts.CancelAsync(); + await cts.CancelAsync().ConfigureAwait(false); return messageCount; }); - await Task.WhenAll(sendMessages, receiveMessages); + await Task.WhenAll(sendMessages, receiveMessages).ConfigureAwait(false); int sent = sendMessages.Result; int received = receiveMessages.Result; @@ -158,7 +158,7 @@ async Task ReadMessages(Socket socket, IList receivedMessages, Canc byte[] buffer = new byte[bufferSize]; while (true) { - ReceiveResult? result = await stream.ReceiveAsync(buffer); + ReceiveResult? result = await stream.ReceiveAsync(buffer).ConfigureAwait(false); if (result is not null) { msg.AddRange(buffer.Take(result.Read)); @@ -190,14 +190,14 @@ async Task ReadMessages(Socket socket, IList receivedMessages, Canc Task receiveMessages = OneShotServer( ipEndPoint, - async socket => await ReadMessages(socket, receivedMessages, cts.Token) + async socket => await ReadMessages(socket, receivedMessages, cts.Token).ConfigureAwait(false) ); Task sendMessages = Task.Run(async () => { int messageCount = 0; using Socket socket = new(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(ipEndPoint); + await socket.ConnectAsync(ipEndPoint).ConfigureAwait(false); using IpcSocketMessageStream stream = new(socket); using JsonRpcSocketsClient client = new( @@ -216,20 +216,20 @@ async Task ReadMessages(Socket socket, IList receivedMessages, Canc messageCount++; var msg = Enumerable.Range(11, i).Select(x => (byte)x).ToArray(); sentMessages.Add(msg); - await stream.WriteAsync(msg); - await stream.WriteEndOfMessageAsync(); + await stream.WriteAsync(msg).ConfigureAwait(false); + await stream.WriteEndOfMessageAsync().ConfigureAwait(false); if (i % 10 == 0) { - await Task.Delay(10); + await Task.Delay(1).ConfigureAwait(false); } } stream.Close(); - await cts.CancelAsync(); + await cts.CancelAsync().ConfigureAwait(false); return messageCount; }); - await Task.WhenAll(sendMessages, receiveMessages); + await Task.WhenAll(sendMessages, receiveMessages).ConfigureAwait(false); int sent = sendMessages.Result; int received = receiveMessages.Result; diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/AdminModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/AdminModuleTests.cs index 92f84ca3e9f..dc40547d637 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/AdminModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/AdminModuleTests.cs @@ -96,8 +96,8 @@ public async Task Smoke_test_peers() { string unused0 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_addPeer", _enodeString); string unused1 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_removePeer", _enodeString); - string unused2 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_addPeer", _enodeString, "true"); - string unused3 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_removePeer", _enodeString, "true"); + string unused2 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_addPeer", _enodeString, true); + string unused3 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_removePeer", _enodeString, true); string unused4 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_peers"); } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs index 234acd6e729..fed0e3ccfba 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs @@ -19,7 +19,7 @@ using Nethermind.Db; using Nethermind.Evm.Tracing.GethStyle; using Nethermind.Evm.Tracing.GethStyle.Custom; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; using Nethermind.JsonRpc.Modules.DebugModule; using Nethermind.JsonRpc.Modules.Eth; @@ -50,8 +50,7 @@ public async Task Get_from_db() IConfigProvider configProvider = Substitute.For(); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); using var response = - await RpcTest.TestRequest(rpcModule, "debug_getFromDb", "STATE", key.ToHexString(true)) as - JsonRpcSuccessResponse; + await RpcTest.TestRequest(rpcModule, "debug_getFromDb", "STATE", key) as JsonRpcSuccessResponse; byte[]? result = response?.Result as byte[]; } @@ -65,15 +64,15 @@ public async Task Get_from_db_null_value() DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); byte[] key = new byte[] { 1, 2, 3 }; using var response = - await RpcTest.TestRequest(rpcModule, "debug_getFromDb", "STATE", key.ToHexString(true)) as + await RpcTest.TestRequest(rpcModule, "debug_getFromDb", "STATE", key) as JsonRpcSuccessResponse; Assert.That(response, Is.Not.Null); } - [TestCase("1")] - [TestCase("0x1")] - public async Task Get_chain_level(string parameter) + [TestCase(1)] + [TestCase(0x1)] + public async Task Get_chain_level(object parameter) { debugBridge.GetLevelInfo(1).Returns( new ChainLevelInfo( @@ -100,7 +99,7 @@ public async Task Get_block_rlp_by_hash() debugBridge.GetBlockRlp(new BlockParameter(Keccak.Zero)).Returns(rlp.Bytes); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlpByHash", $"{Keccak.Zero.Bytes.ToHexString()}") as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlpByHash", Keccak.Zero) as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -113,7 +112,7 @@ public async Task Get_raw_Header() debugBridge.GetBlock(new BlockParameter((long)0)).Returns(blk); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawHeader", $"{Keccak.Zero.Bytes.ToHexString()}") as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawHeader", 0) as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -126,7 +125,7 @@ public async Task Get_block_rlp() localDebugBridge.GetBlockRlp(new BlockParameter(1)).Returns(rlp.Bytes); DebugRpcModule rpcModule = new(LimboLogs.Instance, localDebugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlp", "1") as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlp", 1) as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -140,7 +139,7 @@ public async Task Get_rawblock() localDebugBridge.GetBlockRlp(new BlockParameter(1)).Returns(rlp.Bytes); DebugRpcModule rpcModule = new(LimboLogs.Instance, localDebugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", "1") as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", 1) as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -151,7 +150,7 @@ public async Task Get_block_rlp_when_missing() debugBridge.GetBlockRlp(new BlockParameter(1)).ReturnsNull(); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlp", "1") as JsonRpcErrorResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlp", 1) as JsonRpcErrorResponse; Assert.That(response?.Error?.Code, Is.EqualTo(-32001)); } @@ -162,7 +161,7 @@ public async Task Get_rawblock_when_missing() debugBridge.GetBlockRlp(new BlockParameter(1)).ReturnsNull(); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", "1") as JsonRpcErrorResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", 1) as JsonRpcErrorResponse; Assert.That(response?.Error?.Code, Is.EqualTo(-32001)); } @@ -175,7 +174,7 @@ public async Task Get_block_rlp_by_hash_when_missing() debugBridge.GetBlockRlp(new BlockParameter(Keccak.Zero)).ReturnsNull(); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlpByHash", $"{Keccak.Zero.Bytes.ToHexString()}") as JsonRpcErrorResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlpByHash", Keccak.Zero) as JsonRpcErrorResponse; Assert.That(response?.Error?.Code, Is.EqualTo(-32001)); } @@ -213,7 +212,7 @@ public async Task Get_trace() debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Any()).Returns(trace); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA.ToString(true), "{}"); + string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA, new object()); Assert.That(response, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"gas\":\"0x0\",\"failed\":false,\"returnValue\":\"0xa2\",\"structLogs\":[{\"pc\":0,\"op\":\"STOP\",\"gas\":22000,\"gasCost\":1,\"depth\":1,\"error\":null,\"stack\":[\"0000000000000000000000000000000000000000000000000000000000000007\",\"0000000000000000000000000000000000000000000000000000000000000008\"],\"memory\":[\"0000000000000000000000000000000000000000000000000000000000000005\",\"0000000000000000000000000000000000000000000000000000000000000006\"],\"storage\":{\"0000000000000000000000000000000000000000000000000000000000000001\":\"0000000000000000000000000000000000000000000000000000000000000002\",\"0000000000000000000000000000000000000000000000000000000000000003\":\"0000000000000000000000000000000000000000000000000000000000000004\"}}]},\"id\":67}")); } @@ -226,7 +225,7 @@ public async Task Get_js_trace() debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Any()).Returns(trace); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA.ToString(true), "{}"); + string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA, new object()); Assert.That(response, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"customProperty\":1},\"id\":67}")); } @@ -262,7 +261,7 @@ public async Task Get_trace_with_options() debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Any()).Returns(trace); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA.ToString(true), "{\"disableStack\" : true}"); + string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA, new { disableStack = true }); Assert.That(response, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"gas\":\"0x0\",\"failed\":false,\"returnValue\":\"0xa2\",\"structLogs\":[{\"pc\":0,\"op\":\"STOP\",\"gas\":22000,\"gasCost\":1,\"depth\":1,\"error\":null,\"stack\":[],\"memory\":[\"0000000000000000000000000000000000000000000000000000000000000005\",\"0000000000000000000000000000000000000000000000000000000000000006\"],\"storage\":{\"0000000000000000000000000000000000000000000000000000000000000001\":\"0000000000000000000000000000000000000000000000000000000000000002\",\"0000000000000000000000000000000000000000000000000000000000000003\":\"0000000000000000000000000000000000000000000000000000000000000004\"}}]},\"id\":67}")); } @@ -314,7 +313,7 @@ public async Task Get_trace_with_javascript_setup() debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Do(arg => passedOption = arg)) .Returns(new GethLikeTxTrace()); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA.ToString(true), "{\"disableStack\" : true, \"tracerConfig\" : {\"a\":true} }"); + await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA, new { disableStack = true, tracerConfig = new { a = true } }); passedOption.TracerConfig!.ToString().Should().Be("{\"a\":true}"); } @@ -348,7 +347,7 @@ public void Debug_traceCall_test() GethTraceOptions gtOptions = new(); Transaction transaction = Build.A.Transaction.WithTo(TestItem.AddressA).WithHash(TestItem.KeccakA).TestObject; - TransactionForRpc txForRpc = new(transaction); + TransactionForRpc txForRpc = TransactionForRpc.FromTransaction(transaction); debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()).Returns(trace); @@ -399,7 +398,7 @@ public async Task Migrate_receipts() { debugBridge.MigrateReceipts(Arg.Any()).Returns(true); IDebugRpcModule rpcModule = new DebugRpcModule(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_migrateReceipts", "100"); + string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_migrateReceipts", 100); Assert.That(response, Is.Not.Null); } @@ -408,7 +407,7 @@ public async Task Update_head_block() { debugBridge.UpdateHeadBlock(Arg.Any()); IDebugRpcModule rpcModule = new DebugRpcModule(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - await RpcTest.TestSerializedRequest(rpcModule, "debug_resetHead", TestItem.KeccakA.ToString()); + await RpcTest.TestSerializedRequest(rpcModule, "debug_resetHead", TestItem.KeccakA); debugBridge.Received().UpdateHeadBlock(TestItem.KeccakA); } 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 0bc1a37fb09..1087a3534c9 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs @@ -9,9 +9,7 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; using Nethermind.Evm; -using Nethermind.Evm.Tracing; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; @@ -32,7 +30,7 @@ public async Task Eth_estimateGas_web3_should_return_insufficient_balance_error( TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500}"); string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + 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}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); @@ -47,7 +45,7 @@ public async Task Eth_estimateGas_web3_sample_not_enough_gas_system_account() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(Address.SystemUser).Should().BeFalse(); } @@ -61,7 +59,7 @@ public async Task Eth_estimateGas_web3_sample_not_enough_gas_other_account() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } @@ -75,7 +73,7 @@ public async Task Eth_estimateGas_web3_above_block_gas_limit() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gas\":\"0x100000000\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } @@ -88,17 +86,17 @@ public async Task Eth_create_access_list_calculates_proper_gas(bool optimize, lo var test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] accessList) = GetTestAccessList(loads); + (byte[] code, _) = GetTestAccessList(loads); - TransactionForRpc transaction = - test.JsonSerializer.Deserialize( + AccessListTransactionForRpc transaction = + test.JsonSerializer.Deserialize( $"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); string serializedCreateAccessList = await test.TestEthRpc("eth_createAccessList", - test.JsonSerializer.Serialize(transaction), "0x0", optimize.ToString().ToLower()); + transaction, "0x0", optimize.ToString().ToLower()); - transaction.AccessList = test.JsonSerializer.Deserialize(JToken.Parse(serializedCreateAccessList).SelectToken("result.accessList")!.ToString()); + transaction.AccessList = test.JsonSerializer.Deserialize(JToken.Parse(serializedCreateAccessList).SelectToken("result.accessList")!.ToString()); string serializedEstimateGas = - await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); var gasUsedEstimateGas = JToken.Parse(serializedEstimateGas).Value("result"); var gasUsedCreateAccessList = @@ -117,17 +115,17 @@ public async Task Eth_estimate_gas_with_accessList(bool senderAccessList, long g var test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithConfig(new JsonRpcConfig() { EstimateErrorMargin = 0 }) .Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] accessList) = GetTestAccessList(2, senderAccessList); + (byte[] code, AccessListForRpc accessList) = GetTestAccessList(2, senderAccessList); - TransactionForRpc transaction = - test.JsonSerializer.Deserialize( + AccessListTransactionForRpc transaction = + test.JsonSerializer.Deserialize( $"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); - string serialized = await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + string serialized = await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); Assert.That( serialized, Is.EqualTo($"{{\"jsonrpc\":\"2.0\",\"result\":\"{gasPriceWithoutAccessList.ToHexString(true)}\",\"id\":67}}")); transaction.AccessList = accessList; - serialized = await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + serialized = await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); Assert.That( serialized, Is.EqualTo($"{{\"jsonrpc\":\"2.0\",\"result\":\"{gasPriceWithAccessList.ToHexString(true)}\",\"id\":67}}")); } @@ -138,18 +136,18 @@ public async Task Eth_estimate_gas_is_lower_with_optimized_access_list() var test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] accessList) = GetTestAccessList(2, true); - (byte[] _, AccessListItemForRpc[] optimizedAccessList) = GetTestAccessList(2, false); + (byte[] code, AccessListForRpc accessList) = GetTestAccessList(2, true); + (byte[] _, AccessListForRpc optimizedAccessList) = GetTestAccessList(2, false); - TransactionForRpc transaction = - test.JsonSerializer.Deserialize( + AccessListTransactionForRpc transaction = + test.JsonSerializer.Deserialize( $"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); transaction.AccessList = accessList; - string serialized = await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + string serialized = await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); long estimateGas = Convert.ToInt64(JToken.Parse(serialized).Value("result"), 16); transaction.AccessList = optimizedAccessList; - serialized = await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + serialized = await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); long optimizedEstimateGas = Convert.ToInt64(JToken.Parse(serialized).Value("result"), 16); optimizedEstimateGas.Should().BeLessThan(estimateGas); @@ -161,7 +159,7 @@ public async Task Estimate_gas_without_gas_pricing() using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); } @@ -171,7 +169,7 @@ public async Task Estimate_gas_with_gas_pricing() using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); } @@ -181,7 +179,7 @@ public async Task Estimate_gas_without_gas_pricing_after_1559_legacy() using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); } @@ -191,7 +189,7 @@ public async Task Estimate_gas_without_gas_pricing_after_1559_new_type_of_transa using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); byte[] code = Prepare.EvmCode .Op(Instruction.BASEFEE) @@ -217,7 +215,7 @@ public async Task Estimate_gas_with_base_fee_opcode() string dataStr = code.ToHexString(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( $"{{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\", \"data\": \"{dataStr}\"}}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0xe891\",\"id\":67}")); } @@ -236,7 +234,7 @@ public async Task Estimate_gas_with_revert() string dataStr = code.ToHexString(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( $"{{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\", \"data\": \"{dataStr}\"}}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"revert\"},\"id\":67}")); } @@ -249,12 +247,14 @@ public async Task should_estimate_transaction_with_deployed_code_when_eip3607_en using Context ctx = await Context.Create(specProvider); Transaction tx = Build.A.Transaction.SignedAndResolved(TestItem.PrivateKeyA).TestObject; - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, tx); + LegacyTransactionForRpc transaction = new LegacyTransactionForRpc(tx, 1, Keccak.Zero, 1L) + { + To = TestItem.AddressB + }; ctx.Test.State.InsertCode(TestItem.AddressA, "H"u8.ToArray(), London.Instance); - transaction.To = TestItem.AddressB; string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_estimateGas", transaction, "latest"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); } } 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 060c4e855ad..adbf92f2ca3 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs @@ -8,7 +8,7 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; using Nethermind.Evm; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; @@ -26,7 +26,7 @@ public async Task Eth_call_web3_sample() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "0x0"); + await ctx.Test.TestEthRpc("eth_call", transaction, "0x0"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -38,7 +38,7 @@ public async Task Eth_call_web3_sample_not_enough_gas_system_account() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "0x0"); + await ctx.Test.TestEthRpc("eth_call", transaction, "0x0"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(Address.SystemUser).Should().BeFalse(); } @@ -51,7 +51,7 @@ public async Task Eth_call_web3_should_return_insufficient_balance_error() ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + 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}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); @@ -66,7 +66,7 @@ public async Task Eth_call_web3_sample_not_enough_gas_other_account() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "0x0"); + await ctx.Test.TestEthRpc("eth_call", transaction, "0x0"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } @@ -75,11 +75,13 @@ public async Task Eth_call_web3_sample_not_enough_gas_other_account() public async Task Eth_call_no_sender() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.To = TestItem.AddressB; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + To = TestItem.AddressB + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -87,12 +89,14 @@ public async Task Eth_call_no_sender() public async Task Eth_call_no_recipient_should_work_as_init() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - transaction.Input = new byte[] { 1, 2, 3 }; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA, + Input = [1, 2, 3] + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"StackUnderflow\"},\"id\":67}")); } @@ -106,12 +110,14 @@ public async Task should_not_reject_transactions_with_deployed_code_when_eip3607 using Context ctx = await Context.Create(specProvider); Transaction tx = Build.A.Transaction.SignedAndResolved(TestItem.PrivateKeyA).TestObject; - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, tx); + LegacyTransactionForRpc transaction = new(tx, 1, Keccak.Zero, 1L) + { + To = TestItem.AddressB + }; ctx.Test.State.InsertCode(TestItem.AddressA, "H"u8.ToArray(), London.Instance); - transaction.To = TestItem.AddressB; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -129,12 +135,14 @@ public async Task Eth_call_ethereum_recipient() public async Task Eth_call_ok() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - transaction.To = TestItem.AddressB; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA, + To = TestItem.AddressB + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -142,12 +150,14 @@ public async Task Eth_call_ok() public async Task Eth_call_with_blockhash_ok() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - transaction.To = TestItem.AddressB; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA, + To = TestItem.AddressB + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "{\"blockHash\":\"0xf0b3f69cbd4e1e8d9b0ef02ff5d1384d18e19d251a4052f5f90bab190c5e8937\"}"); + await ctx.Test.TestEthRpc("eth_call", transaction, "{\"blockHash\":\"0xf0b3f69cbd4e1e8d9b0ef02ff5d1384d18e19d251a4052f5f90bab190c5e8937\"}"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32001,\"message\":\"0xf0b3f69cbd4e1e8d9b0ef02ff5d1384d18e19d251a4052f5f90bab190c5e8937 could not be found\"},\"id\":67}")); } @@ -155,11 +165,12 @@ public async Task Eth_call_with_blockhash_ok() public async Task Eth_call_create_tx_with_empty_data() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); serialized.Should().BeEquivalentTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"Contract creation without any data provided.\"},\"id\":67}"); } @@ -167,15 +178,17 @@ public async Task Eth_call_create_tx_with_empty_data() public async Task Eth_call_missing_state_after_fast_sync() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - transaction.To = TestItem.AddressB; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA, + To = TestItem.AddressB + }; ctx.Test.StateDb.Clear(); ctx.Test.TrieStore.ClearCache(); string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); serialized.Should().StartWith("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32002,"); } @@ -185,14 +198,14 @@ public async Task Eth_call_with_accessList() var test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] accessList) = GetTestAccessList(); + (byte[] code, AccessListForRpc accessList) = GetTestAccessList(); - TransactionForRpc transaction = - test.JsonSerializer.Deserialize( + AccessListTransactionForRpc transaction = + test.JsonSerializer.Deserialize( $"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); transaction.AccessList = accessList; - string serialized = await test.TestEthRpc("eth_call", test.JsonSerializer.Serialize(transaction), "0x0"); + string serialized = await test.TestEthRpc("eth_call", transaction, "0x0"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x010203\",\"id\":67}")); } @@ -202,7 +215,7 @@ public async Task Eth_call_without_gas_pricing() using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -212,7 +225,7 @@ public async Task Eth_call_with_gas_pricing() using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -222,7 +235,7 @@ public async Task Eth_call_without_gas_pricing_after_1559_legacy() using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -232,7 +245,7 @@ public async Task Eth_call_without_gas_pricing_after_1559_new_type_of_transactio using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); byte[] code = Prepare.EvmCode .Op(Instruction.BASEFEE) @@ -258,7 +271,7 @@ public async Task Eth_call_with_base_fee_opcode_should_return_0() string dataStr = code.ToHexString(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( $"{{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\", \"data\": \"{dataStr}\"}}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"id\":67}")); } @@ -277,7 +290,7 @@ public async Task Eth_call_with_revert() string dataStr = code.ToHexString(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( $"{{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\", \"data\": \"{dataStr}\"}}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"revert\"},\"id\":67}")); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs index 843406aad94..6f5bab4328c 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs @@ -8,12 +8,14 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; +using FluentAssertions.Json; using Nethermind.Blockchain; using Nethermind.Blockchain.Filters; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Receipts; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Eip2930; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Core.Test.Blockchain; @@ -22,6 +24,7 @@ using Nethermind.Evm; using Nethermind.Facade; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Filters; using Nethermind.Int256; using Nethermind.Serialization.Json; @@ -30,6 +33,7 @@ using Nethermind.Specs.Forks; using Nethermind.Specs.Test; using Nethermind.TxPool; +using Newtonsoft.Json.Linq; using NSubstitute; using NSubstitute.ExceptionExtensions; using NUnit.Framework; @@ -73,7 +77,7 @@ public async Task Eth_get_transaction_by_block_hash_and_index() { using Context ctx = await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByBlockHashAndIndex", ctx.Test.BlockTree.FindHeadBlock()!.Hash!.ToString(), "1"); - Assert.That(serialized, Is.EqualTo("{\"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\",\"type\":\"0x0\",\"v\":\"0x25\",\"s\":\"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb\",\"r\":\"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd\"},\"id\":67}"), serialized.Replace("\"", "\\\"")); + 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","type":"0x0","v":"0x25","s":"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb","r":"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd"},"id":67}"""); } [Test] @@ -81,7 +85,7 @@ public async Task Eth_get_transaction_by_hash() { using Context ctx = await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByHash", ctx.Test.BlockTree.FindHeadBlock()!.Transactions.Last().Hash!.ToString()); - Assert.That(serialized, Is.EqualTo("{\"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\",\"type\":\"0x0\",\"v\":\"0x25\",\"s\":\"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb\",\"r\":\"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd\"},\"id\":67}"), serialized.Replace("\"", "\\\"")); + 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","type":"0x0","v":"0x25","s":"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb","r":"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd"},"id":67}"""); } [Test] @@ -107,7 +111,7 @@ public async Task Eth_pending_transactions() using Context ctx = await Context.Create(); ctx.Test.AddTransactions(Build.A.Transaction.SignedAndResolved(TestItem.PrivateKeyD).TestObject); string serialized = await ctx.Test.TestEthRpc("eth_pendingTransactions"); - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":[{\"hash\":\"0x190d9a78dbc61b1856162ab909976a1b28ba4a41ee041341576ea69686cd3b29\",\"nonce\":\"0x0\",\"blockHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"blockNumber\":null,\"transactionIndex\":null,\"from\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\",\"v\":\"0x26\",\"s\":\"0x2d04e55699fa32e6b65a22189f7571f5030d636d7d44a8b53fe016a2c3ecde24\",\"r\":\"0xda3978c3a1430bd902cf5bbca73c5a1eca019b3f003c95ee16657fd0bb89534c\"}],\"id\":67}"), serialized.Replace("\"", "\\\"")); + JToken.Parse(serialized).Should().BeEquivalentTo("""{"jsonrpc":"2.0","result":[{"hash":"0x190d9a78dbc61b1856162ab909976a1b28ba4a41ee041341576ea69686cd3b29","nonce":"0x0","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":null,"transactionIndex":null,"from":"0x475674cb523a0a2736b7f7534390288fce16982c","to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","type":"0x0","v":"0x26","s":"0x2d04e55699fa32e6b65a22189f7571f5030d636d7d44a8b53fe016a2c3ecde24","r":"0xda3978c3a1430bd902cf5bbca73c5a1eca019b3f003c95ee16657fd0bb89534c"}],"id":67}"""); } [Test] @@ -115,9 +119,8 @@ public async Task Eth_pending_transactions_1559_tx() { using Context ctx = await Context.CreateWithLondonEnabled(); ctx.Test.AddTransactions(Build.A.Transaction.WithMaxPriorityFeePerGas(6.GWei()).WithMaxFeePerGas(11.GWei()).WithType(TxType.EIP1559).SignedAndResolved(TestItem.PrivateKeyC).TestObject); - const string addedTx = "\"hash\":\"0x7544f95c68426cb8a8a5a54889c60849ed96ff317835beb63b4d745cbc078cec\",\"nonce\":\"0x0\",\"blockHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"blockNumber\":null,\"transactionIndex\":null,\"from\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x28fa6ae00\",\"maxPriorityFeePerGas\":\"0x165a0bc00\",\"maxFeePerGas\":\"0x28fa6ae00\",\"gas\":\"0x5208\",\"input\":\"0x\",\"chainId\":\"0x1\",\"type\":\"0x2\",\"accessList\":[],\"v\":\"0x0\",\"s\":\"0x606b869eab1c9d01ff462f887826cb8f349ea8f1b59d0635ae77155b3b84ad86\",\"r\":\"0x63b08cc0a06c88fb1dd79f273736b3463af12c6754f9df764aa222d2693a5d43\",\"yParity\":\"0x0\""; string serialized = await ctx.Test.TestEthRpc("eth_pendingTransactions"); - serialized.Contains(addedTx).Should().BeTrue(); + JToken.Parse(serialized).Should().ContainSubtree("""{"result": [{"hash":"0x7544f95c68426cb8a8a5a54889c60849ed96ff317835beb63b4d745cbc078cec","nonce":"0x0","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":null,"transactionIndex":null,"from":"0x76e68a8696537e4141926f3e528733af9e237d69","to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x28fa6ae00","maxPriorityFeePerGas":"0x165a0bc00","maxFeePerGas":"0x28fa6ae00","gas":"0x5208","input":"0x","chainId":"0x1","type":"0x2","accessList":[],"v":"0x0","s":"0x606b869eab1c9d01ff462f887826cb8f349ea8f1b59d0635ae77155b3b84ad86","r":"0x63b08cc0a06c88fb1dd79f273736b3463af12c6754f9df764aa222d2693a5d43","yParity":"0x0"}]}"""); } [Test] @@ -125,9 +128,8 @@ public async Task Eth_pending_transactions_2930_tx() { using Context ctx = await Context.CreateWithLondonEnabled(); ctx.Test.AddTransactions(Build.A.Transaction.WithMaxPriorityFeePerGas(6.GWei()).WithMaxFeePerGas(11.GWei()).WithType(TxType.AccessList).SignedAndResolved(TestItem.PrivateKeyC).TestObject); - const string addedTx = "\"hash\":\"0x4eabe360dc515aadc8e35f75b23803bb86e7186ebf2e58412555b3d0c7750dcc\",\"nonce\":\"0x0\",\"blockHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"blockNumber\":null,\"transactionIndex\":null,\"from\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x165a0bc00\",\"gas\":\"0x5208\",\"input\":\"0x\",\"chainId\":\"0x1\",\"type\":\"0x1\",\"accessList\":[],\"v\":\"0x0\",\"s\":\"0x27e3dde7b07d6d6b50e0d11b29085036e9c8adc12dea52f6f07dd7a0551ff22a\",\"r\":\"0x619cb31fd4aa1c38ae36b31c5d8310f74d9f8ddd94389db91a68deb26737f2dc\",\"yParity\":\"0x0\""; string serialized = await ctx.Test.TestEthRpc("eth_pendingTransactions"); - serialized.Contains(addedTx).Should().BeTrue(); + JToken.Parse(serialized).Should().ContainSubtree("""{"result": [{"hash":"0x4eabe360dc515aadc8e35f75b23803bb86e7186ebf2e58412555b3d0c7750dcc","nonce":"0x0","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":null,"transactionIndex":null,"from":"0x76e68a8696537e4141926f3e528733af9e237d69","to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x165a0bc00","gas":"0x5208","input":"0x","chainId":"0x1","type":"0x1","accessList":[],"v":"0x0","s":"0x27e3dde7b07d6d6b50e0d11b29085036e9c8adc12dea52f6f07dd7a0551ff22a","r":"0x619cb31fd4aa1c38ae36b31c5d8310f74d9f8ddd94389db91a68deb26737f2dc","yParity":"0x0"}]}"""); } [Test] @@ -144,7 +146,7 @@ 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"); - Assert.That(serialized, Is.EqualTo("{\"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\",\"type\":\"0x0\",\"v\":\"0x25\",\"s\":\"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb\",\"r\":\"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd\"},\"id\":67}"), serialized.Replace("\"", "\\\"")); + 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","type":"0x0","v":"0x25","s":"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb","r":"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd"},"id":67}"""); } [TestCase(false, "{\"jsonrpc\":\"2.0\",\"result\":{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0xa2a9f03b9493046696099d27b2612b99497aa1f392ec966716ab393c715a5bb6\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x2ba5557a4c62a513c7e56d1bf13373e0da6bec016755483e91589fe1c6d212e2\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x0\",\"parentHash\":\"0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x201\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"totalDifficulty\":\"0x0\",\"timestamp\":\"0xf4240\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]},\"id\":67}")] @@ -314,7 +316,7 @@ void handleNewBlock(object? sender, BlockEventArgs e) } test.BlockTree.NewHeadBlock += handleNewBlock; - using JsonRpcResponse newFilterResp = await RpcTest.TestRequest(test.EthRpcModule, "eth_newFilter", "{\"fromBlock\":\"latest\"}"); + using JsonRpcResponse newFilterResp = await RpcTest.TestRequest(test.EthRpcModule, "eth_newFilter", new { fromBlock = "latest" }); string getFilterLogsSerialized1 = await test.TestEthRpc("eth_getFilterChanges", (newFilterResp as JsonRpcSuccessResponse)!.Result?.ToString() ?? "0x0"); //expect empty - no changes so far @@ -512,7 +514,7 @@ void HandleNewBlock(object? sender, BlockReplacementEventArgs e) string getLogsSerialized = await test.TestEthRpc("eth_getLogs", $"{{\"fromBlock\":\"{blockHash}\"}}"); - using JsonRpcResponse? newFilterResp = await RpcTest.TestRequest(test.EthRpcModule, "eth_newFilter", $"{{\"fromBlock\":\"{blockHash}\"}}"); + using JsonRpcResponse? newFilterResp = await RpcTest.TestRequest(test.EthRpcModule, "eth_newFilter", new { fromBlock = blockHash }); Assert.That(newFilterResp is not null && newFilterResp is JsonRpcSuccessResponse, Is.True); @@ -647,7 +649,7 @@ public async Task Eth_get_block_by_hash_with_tx(string blockParameter, bool with { using Context ctx = await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getBlockByHash", ctx.Test.BlockTree.Head!.Hash!.ToString(), withTxData.ToString()); - Assert.That(serialized, Is.EqualTo(expectedResult), serialized.Replace("\"", "\\\"")); + JToken.Parse(serialized).Should().BeEquivalentTo(expectedResult); } [TestCase(false, "earliest", "{\"jsonrpc\":\"2.0\",\"result\":{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0x2167088a0f0de66028d2b728235af6d467108c1750c3e11a8f6e6cd60fddb0e4\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x201\",\"stateRoot\":\"0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f\",\"totalDifficulty\":\"0xf4240\",\"timestamp\":\"0xf4240\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]},\"id\":67}")] @@ -662,7 +664,7 @@ public async Task Eth_get_block_by_number(bool eip1559, string blockParameter, s { using Context ctx = eip1559 ? await Context.CreateWithLondonEnabled() : await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getBlockByNumber", blockParameter, "true"); - Assert.That(serialized, Is.EqualTo(expectedResult), serialized.Replace("\"", "\\\"")); + JToken.Parse(serialized).Should().BeEquivalentTo(expectedResult); } [TestCase("earliest", "{\"jsonrpc\":\"2.0\",\"result\":{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0x2167088a0f0de66028d2b728235af6d467108c1750c3e11a8f6e6cd60fddb0e4\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x201\",\"stateRoot\":\"0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f\",\"totalDifficulty\":\"0xf4240\",\"timestamp\":\"0xf4240\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]},\"id\":67}")] @@ -816,11 +818,10 @@ public async Task Eth_get_block_by_number_with_recovering_sender_from_receipts() Block block = Build.A.Block.WithNumber(1) .WithStateRoot(new Hash256("0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f")) - .WithTransactions(new[] { Build.A.Transaction.TestObject }) + .WithTransactions(Build.A.Transaction.TestObject) .TestObject; - LogEntry[] entries = new[] - { + LogEntry[] entries = { Build.A.LogEntry.TestObject, Build.A.LogEntry.TestObject }; @@ -836,7 +837,7 @@ public async Task Eth_get_block_by_number_with_recovering_sender_from_receipts() ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithBlockFinder(blockFinder).WithReceiptFinder(receiptFinder).Build(); string serialized = await ctx.Test.TestEthRpc("eth_getBlockByNumber", TestItem.KeccakA.ToString(), "true"); - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0xe3026a6708b90d5cb25557ac38ddc3f5ef550af10f31e1cf771524da8553fa1c\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x2ba5557a4c62a513c7e56d1bf13373e0da6bec016755483e91589fe1c6d212e2\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x1\",\"parentHash\":\"0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x221\",\"stateRoot\":\"0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f\",\"totalDifficulty\":\"0x0\",\"timestamp\":\"0xf4240\",\"transactions\":[{\"nonce\":\"0x0\",\"blockHash\":\"0xe3026a6708b90d5cb25557ac38ddc3f5ef550af10f31e1cf771524da8553fa1c\",\"blockNumber\":\"0x1\",\"transactionIndex\":\"0x0\",\"from\":\"0x2d36e6c27c34ea22620e7b7c45de774599406cf3\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\"}],\"transactionsRoot\":\"0x29cc403075ed3d1d6af940d577125cc378ee5a26f7746cbaf87f1cf4a38258b5\",\"uncles\":[]},\"id\":67}")); + JToken.Parse(serialized).Should().BeEquivalentTo("""{"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0xf4240","extraData":"0x010203","gasLimit":"0x3d0900","gasUsed":"0x0","hash":"0xe3026a6708b90d5cb25557ac38ddc3f5ef550af10f31e1cf771524da8553fa1c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x2ba5557a4c62a513c7e56d1bf13373e0da6bec016755483e91589fe1c6d212e2","nonce":"0x00000000000003e8","number":"0x1","parentHash":"0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x221","stateRoot":"0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f","totalDifficulty":"0x0","timestamp":"0xf4240","transactions":[{"nonce":"0x0","blockHash":"0xe3026a6708b90d5cb25557ac38ddc3f5ef550af10f31e1cf771524da8553fa1c","blockNumber":"0x1","transactionIndex":"0x0","from":"0x2d36e6c27c34ea22620e7b7c45de774599406cf3","to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","type":"0x0","v":"0x0","r":"0x0","s":"0x0","hash":null}],"transactionsRoot":"0x29cc403075ed3d1d6af940d577125cc378ee5a26f7746cbaf87f1cf4a38258b5","uncles":[]},"id":67}"""); } [TestCase(false)] @@ -1071,10 +1072,9 @@ public async Task Send_transaction_without_signature_will_not_set_nonce_when_zer ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .WithBlockchainBridge(bridge).WithTxSender(txSender).Build(); - Transaction tx = Build.A.Transaction.TestObject; - TransactionForRpc rpcTx = new(tx); - rpcTx.Nonce = 0; - string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", new EthereumJsonSerializer().Serialize(rpcTx)); + Transaction tx = Build.A.Transaction.WithNonce(0).TestObject; + TransactionForRpc rpcTx = TransactionForRpc.FromTransaction(tx); + string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", rpcTx); // TODO: actual test missing now await txSender.Received().SendTransaction(Arg.Any(), TxHandlingOptions.PersistentBroadcast); Assert.That(serialized, Is.EqualTo($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}")); @@ -1092,9 +1092,9 @@ public async Task Send_transaction_without_signature_will_manage_nonce_when_null ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .WithBlockchainBridge(bridge).WithTxSender(txSender).Build(); Transaction tx = Build.A.Transaction.TestObject; - TransactionForRpc rpcTx = new(tx); + LegacyTransactionForRpc rpcTx = (LegacyTransactionForRpc)TransactionForRpc.FromTransaction(tx); rpcTx.Nonce = null; - string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", new EthereumJsonSerializer().Serialize(rpcTx)); + string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", rpcTx); await txSender.Received().SendTransaction(Arg.Any(), TxHandlingOptions.PersistentBroadcast | TxHandlingOptions.ManagedNonce); Assert.That(serialized, Is.EqualTo($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}")); @@ -1105,9 +1105,9 @@ public async Task Send_transaction_should_return_ErrorCode_if_tx_not_added() { using Context ctx = await Context.Create(); Transaction tx = Build.A.Transaction.WithValue(10000).SignedAndResolved(new PrivateKey("0x0000000000000000000000000000000000000000000000000000000000000001")).WithNonce(0).TestObject; - TransactionForRpc txForRpc = new(tx); + TransactionForRpc txForRpc = TransactionForRpc.FromTransaction(tx); - string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", new EthereumJsonSerializer().Serialize(txForRpc)); + string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", txForRpc); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32010,\"message\":\"InsufficientFunds, Balance is zero, cannot pay gas\"},\"id\":67}")); } @@ -1139,16 +1139,16 @@ public async Task Eth_create_access_list_sample(AccessListProvided accessListPro { TestRpcBlockchain test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] _) = GetTestAccessList(loads); + (byte[] code, AccessListForRpc _) = GetTestAccessList(loads); - TransactionForRpc transaction = test.JsonSerializer.Deserialize($"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); + AccessListTransactionForRpc transaction = test.JsonSerializer.Deserialize($"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); if (accessListProvided != AccessListProvided.None) { transaction.AccessList = GetTestAccessList(2, accessListProvided == AccessListProvided.Full).AccessList; } - string serialized = await test.TestEthRpc("eth_createAccessList", test.JsonSerializer.Serialize(transaction), "0x0", optimize.ToString().ToLower()); + string serialized = await test.TestEthRpc("eth_createAccessList", transaction, "0x0", optimize); Assert.That(serialized, Is.EqualTo(expected)); } @@ -1156,13 +1156,36 @@ public async Task Eth_create_access_list_sample(AccessListProvided accessListPro [TestCase(0)] public static void Should_handle_gasCap_as_max_if_null_or_zero(long? gasCap) { - TransactionForRpc rpcTx = new TransactionForRpc(); + LegacyTransactionForRpc rpcTx = new LegacyTransactionForRpc(); rpcTx.EnsureDefaults(gasCap); Assert.That(rpcTx.Gas, Is.EqualTo(long.MaxValue), "Gas must be set to max if gasCap is null or 0"); } + [Ignore(reason: "Shows disparity across 'default' methods")] + [TestCase(null)] + [TestCase(0)] + public static void ToTransactionWithDefaults_and_EnsureDefaults_same_GasLimit(long? gasCap) + { + long toTransactionWitDefaultsGasLimit; + { + var rpcTx = new LegacyTransactionForRpc(); + Transaction tx = rpcTx.ToTransaction(); + toTransactionWitDefaultsGasLimit = tx.GasLimit; + } + + long ensureDefaultsGasLimit; + { + var rpcTx = new LegacyTransactionForRpc(); + rpcTx.EnsureDefaults(gasCap); + var tx = rpcTx.ToTransaction(); + ensureDefaultsGasLimit = tx.GasLimit; + } + + toTransactionWitDefaultsGasLimit.Should().Be(ensureDefaultsGasLimit); + } + [Test] public async Task eth_getBlockByNumber_should_return_withdrawals_correctly() { @@ -1296,14 +1319,19 @@ public AllowNullAuthorizationTuple(ulong chainId, Address? codeAddress, ulong no } } - private static (byte[] ByteCode, AccessListItemForRpc[] AccessList) GetTestAccessList(long loads = 2, bool allowSystemUser = true) + private static (byte[] ByteCode, AccessListForRpc AccessList) GetTestAccessList(long loads = 2, bool allowSystemUser = true) { - AccessListItemForRpc[] accessList = allowSystemUser - ? new[] { - new AccessListItemForRpc(Address.SystemUser, Enumerable.Range(1, (int)loads).Select(i => (UInt256)i).ToArray()), - new AccessListItemForRpc(TestItem.AddressC, Array.Empty()), + var builder = new AccessList.Builder(); + if (allowSystemUser) + { + builder.AddAddress(Address.SystemUser); + for (int i = 0; i < (int)loads; i++) + { + builder.AddStorage((UInt256)(i + 1)); } - : new[] { new AccessListItemForRpc(TestItem.AddressC, Array.Empty()) }; + } + builder.AddAddress(TestItem.AddressC); + AccessList accessList = builder.Build(); Prepare code = Prepare.EvmCode; @@ -1326,7 +1354,7 @@ private static (byte[] ByteCode, AccessListItemForRpc[] AccessList) GetTestAcces .PushData(0) .Op(Instruction.RETURN) .Done; - return (byteCode, accessList); + return (byteCode, AccessListForRpc.FromAccessList(accessList)); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcSimulateTestsBase.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcSimulateTestsBase.cs index b3d887961c1..50a822efecf 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcSimulateTestsBase.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcSimulateTestsBase.cs @@ -13,9 +13,8 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Crypto; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; -using Nethermind.JsonRpc.Data; using Nethermind.Logging; using Nethermind.Specs; using Nethermind.Specs.Forks; @@ -142,7 +141,7 @@ protected static byte[] GenerateTransactionDataForEcRecover(Hash256 keccak, Sign { SystemTransaction transaction = new() { Data = bytes, To = toAddress, SenderAddress = senderAddress }; transaction.Hash = transaction.CalculateHash(); - TransactionForRpc transactionForRpc = new(transaction); + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(transaction); ResultWrapper mainChainResult = testRpcBlockchain.EthRpcModule.eth_call(transactionForRpc, BlockParameter.Pending); return ParseEcRecoverAddress(Bytes.FromHexString(mainChainResult.Data)); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthSimulateTestsPrecompilesWithRedirection.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthSimulateTestsPrecompilesWithRedirection.cs index f25ebc9c145..c79fb4f5f9e 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthSimulateTestsPrecompilesWithRedirection.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthSimulateTestsPrecompilesWithRedirection.cs @@ -13,7 +13,7 @@ using Nethermind.Core.Test.Builders; using Nethermind.Evm; using Nethermind.Evm.Precompiles; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.JsonRpc.Modules.Eth; @@ -37,7 +37,8 @@ public async Task Test_eth_simulate_create() GasPrice = 20.GWei() }; - TransactionForRpc transactionForRpc = new(systemTransactionForModifiedVm) { Nonce = null }; + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(systemTransactionForModifiedVm); + ((LegacyTransactionForRpc)transactionForRpc).Nonce = null; SimulatePayload payload = new() { @@ -143,17 +144,15 @@ function ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public returns( Assert.That(headHash != chain.BlockFinder.Head!.Hash!); chain.State.StateRoot = chain.BlockFinder.Head!.StateRoot!; - TransactionForRpc transactionForRpc = new(new Transaction + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(new Transaction { Data = transactionData, To = contractAddress, SenderAddress = TestItem.AddressA, GasLimit = 3_500_000, GasPrice = 20.GWei() - }) - { - Nonce = null - }; + }); + ((LegacyTransactionForRpc)transactionForRpc).Nonce = null; SimulatePayload payload = new() { diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsBlocksAndTransactions.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsBlocksAndTransactions.cs index e653e6914c2..bcfa68f448a 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsBlocksAndTransactions.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsBlocksAndTransactions.cs @@ -12,7 +12,7 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Int256; @@ -59,7 +59,7 @@ public async Task Test_eth_simulate_serialisation() new() { BlockOverrides = new BlockOverride { Number = 10 }, - Calls = [new TransactionForRpc(txToFail), new TransactionForRpc(tx)], + Calls = [TransactionForRpc.FromTransaction(txToFail), TransactionForRpc.FromTransaction(tx)], StateOverrides = new Dictionary { { TestItem.AddressA, new AccountOverride { Balance = Math.Max(420_000_004_000_001UL, 1_000_000_004_000_001UL) } } @@ -117,7 +117,8 @@ public async Task Test_eth_simulate_eth_moved() FeeRecipient = TestItem.AddressC, BaseFeePerGas = 0 }, - Calls = new[] { new TransactionForRpc(txAtoB1), new TransactionForRpc(txAtoB2) } + Calls = [TransactionForRpc.FromTransaction(txAtoB1), TransactionForRpc.FromTransaction(txAtoB2) + ] }, new() { @@ -129,7 +130,7 @@ public async Task Test_eth_simulate_eth_moved() FeeRecipient = TestItem.AddressC, BaseFeePerGas = 0 }, - Calls = new[] { new TransactionForRpc(txAtoB3), new TransactionForRpc(txAtoB4) } + Calls = [TransactionForRpc.FromTransaction(txAtoB3), TransactionForRpc.FromTransaction(txAtoB4)] } }, TraceTransfers = true @@ -180,8 +181,12 @@ public async Task Test_eth_simulate_transactions_forced_fail() //shall fail Transaction txAtoB2 = GetTransferTxData(nonceA + 2, chain.EthereumEcdsa, TestItem.PrivateKeyA, TestItem.AddressB, UInt256.MaxValue); - TransactionForRpc transactionForRpc = new(txAtoB2) { Nonce = null }; - TransactionForRpc transactionForRpc2 = new(txAtoB1) { Nonce = null }; + + LegacyTransactionForRpc transactionForRpc = (LegacyTransactionForRpc)TransactionForRpc.FromTransaction(txAtoB2); + transactionForRpc.Nonce = null; + LegacyTransactionForRpc transactionForRpc2 = (LegacyTransactionForRpc)TransactionForRpc.FromTransaction(txAtoB1); + transactionForRpc2.Nonce = null; + SimulatePayload payload = new() { BlockStateCalls = new List> @@ -196,7 +201,7 @@ public async Task Test_eth_simulate_transactions_forced_fail() FeeRecipient = TestItem.AddressC, BaseFeePerGas = 0 }, - Calls = new[] { transactionForRpc2 } + Calls = [transactionForRpc2] }, new() { diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsHiveBase.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsHiveBase.cs index 16bd45f4f5d..5653e15fffa 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsHiveBase.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsHiveBase.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Nethermind.Blockchain.Find; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Serialization.Json; using NUnit.Framework; diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs index 708dd5575c1..b2603727410 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs @@ -316,7 +316,7 @@ public async Task parity_enode() [Test] public async Task parity_setEngineSigner() { - string serialized = await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_setEngineSigner", TestItem.AddressA.ToString(), "password"); + string serialized = await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_setEngineSigner", TestItem.AddressA, "password"); string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":67}"; Assert.That(serialized, Is.EqualTo(expectedResult)); _signerStore.Address.Should().Be(TestItem.AddressA); @@ -336,7 +336,7 @@ public async Task parity_setEngineSignerSecret() [Test] public async Task parity_clearEngineSigner() { - await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_setEngineSigner", TestItem.AddressA.ToString(), "password"); + await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_setEngineSigner", TestItem.AddressA, "password"); string serialized = await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_clearEngineSigner"); string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":67}"; serialized.Should().Be(expectedResult); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs index 1d5dab0e6b9..1919d4dc2f7 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs @@ -15,7 +15,6 @@ using Nethermind.Evm; using Nethermind.JsonRpc.Modules.Proof; using Nethermind.Logging; -using Nethermind.Serialization.Json; using Nethermind.Serialization.Rlp; using Nethermind.Specs; using Nethermind.Specs.Forks; @@ -28,7 +27,7 @@ using FluentAssertions; using Nethermind.Consensus.Processing; using Nethermind.Core.Buffers; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.State.Tracing; using NSubstitute; @@ -88,7 +87,7 @@ public async Task Setup() public async Task Can_get_transaction(bool withHeader) { Hash256 txHash = _blockTree.FindBlock(1)!.Transactions[0].Hash!; - TransactionWithProof txWithProof = _proofRpcModule.proof_getTransactionByHash(txHash, withHeader).Data; + TransactionForRpcWithProof txWithProof = _proofRpcModule.proof_getTransactionByHash(txHash, withHeader).Data; Assert.That(txWithProof.Transaction, Is.Not.Null); Assert.That(txWithProof.TxProof.Length, Is.EqualTo(2)); if (withHeader) @@ -100,7 +99,7 @@ public async Task Can_get_transaction(bool withHeader) Assert.That(txWithProof.BlockHeader, Is.Null); } - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", txHash, withHeader); Assert.That(response.Contains("\"result\""), Is.True); } @@ -109,7 +108,7 @@ public async Task Can_get_transaction(bool withHeader) public async Task When_getting_non_existing_tx_correct_error_code_is_returned(bool withHeader) { Hash256 txHash = TestItem.KeccakH; - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", txHash, withHeader); Assert.That(response.Contains($"{ErrorCodes.ResourceNotFound}"), Is.True); } @@ -118,7 +117,7 @@ public async Task When_getting_non_existing_tx_correct_error_code_is_returned(bo public async Task When_getting_non_existing_receipt_correct_error_code_is_returned(bool withHeader) { Hash256 txHash = TestItem.KeccakH; - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash, withHeader); Assert.That(response.Contains($"{ErrorCodes.ResourceNotFound}"), Is.True); } @@ -128,23 +127,23 @@ public async Task On_incorrect_params_returns_correct_error_code() Hash256 txHash = TestItem.KeccakH; // missing with header - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "missing"); // too many - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", "true", "false"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash, true, false); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "too many"); // missing with header - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", txHash); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "missing"); // too many - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}", "true", "false"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", txHash, true, false); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "too many"); // all wrong - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{txHash}"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", txHash); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "missing"); } @@ -166,7 +165,7 @@ public async Task Can_get_receipt(bool withHeader, string expectedResult) Assert.That(receiptWithProof.BlockHeader, Is.Null); } - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash, withHeader); response.Should().Be(expectedResult); } @@ -236,7 +235,7 @@ public async Task Get_receipt_when_block_has_few_receipts(bool withHeader, strin Assert.That(receiptWithProof.BlockHeader, Is.Null); } - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash, withHeader); response.Should().Be(expectedResult); } @@ -251,7 +250,7 @@ public async Task Can_call() // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { From = TestItem.AddressA, To = TestItem.AddressB, @@ -260,8 +259,7 @@ public async Task Can_call() _proofRpcModule.proof_call(tx, new BlockParameter(block.Number)); - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{block.Number}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, block.Number); Assert.That(response.Contains("\"result\""), Is.True); } @@ -276,7 +274,7 @@ public async Task Can_call_by_hash() // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { From = TestItem.AddressA, To = TestItem.AddressB, @@ -284,8 +282,7 @@ public async Task Can_call_by_hash() }; _proofRpcModule.proof_call(tx, new BlockParameter(block.Hash!)); - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{block.Hash}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, block.Hash); Assert.That(response.Contains("\"result\""), Is.True); } @@ -300,18 +297,17 @@ public async Task Can_call_by_hash_canonical() // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { From = TestItem.AddressA, To = TestItem.AddressB, GasPrice = _useNonZeroGasPrice ? 10.GWei() : 0 }; - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{{\"blockHash\" : \"{block.Hash}\", \"requireCanonical\" : true}}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, new { blockHash = block.Hash, requireCanonical = true }); Assert.That(response.Contains("-32000"), Is.True); - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{{\"blockHash\" : \"{TestItem.KeccakG}\", \"requireCanonical\" : true}}"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, new { blockHash = TestItem.KeccakG, requireCanonical = true }); Assert.That(response.Contains("-32001"), Is.True); } @@ -780,7 +776,7 @@ private async Task TestCallWithCode(byte[] code, Address? f // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { From = from, To = TestItem.AddressB, @@ -799,8 +795,7 @@ private async Task TestCallWithCode(byte[] code, Address? f } } - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{blockOnTop.Number}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, blockOnTop.Number); Assert.That(response.Contains("\"result\""), Is.True); return callResultWithProof; @@ -827,7 +822,7 @@ private async Task TestCallWithStorageAndCode(byte[] code, UInt256 gasPrice, Add // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { // we are testing system transaction here when From is null From = from, @@ -874,8 +869,7 @@ private async Task TestCallWithStorageAndCode(byte[] code, UInt256 gasPrice, Add } } - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{blockOnTop.Number}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, blockOnTop.Number); Assert.That(response.Contains("\"result\""), Is.True); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListForRpcTests.cs new file mode 100644 index 00000000000..599ff7b0f47 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListForRpcTests.cs @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using FluentAssertions.Json; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Int256; +using Nethermind.Serialization.Json; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public class AccessListForRpcTests +{ + private readonly EthereumJsonSerializer _serializer = new(); + + [Test] + public void Single_address_with_no_storage() + { + Address address = TestItem.AddressA; + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":[]}]"""); + } + + [Test] + public void Single_address_with_multiple_storage_keys() + { + Address address = TestItem.AddressA; + UInt256 storageKey1 = (UInt256)1; + UInt256 storageKey2 = (UInt256)2; + UInt256 storageKey3 = (UInt256)3; + + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey2) + .AddStorage(storageKey3) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000003"]}]"""); + } + + [Test] + public void Single_address_with_duplicated_storage_keys() + { + Address address = TestItem.AddressA; + UInt256 storageKey1 = (UInt256)1; + UInt256 storageKey2 = (UInt256)2; + UInt256 storageKey3 = (UInt256)3; + + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey2) + .AddStorage(storageKey3) + .AddStorage(storageKey1) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000000000000000000000000000000000000001"]}]"""); + } + + [Test] + public void Duplicated_address_with_multiple_storage_keys() + { + Address address = TestItem.AddressA; + UInt256 storageKey1 = (UInt256)1; + UInt256 storageKey2 = (UInt256)2; + UInt256 storageKey3 = (UInt256)3; + + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey2) + .AddAddress(address) + .AddStorage(storageKey3) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002"]},{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000003"]}]"""); + } + + [Test] + public void Duplicated_address_with_duplicated_storage_keys() + { + Address address = TestItem.AddressA; + UInt256 storageKey1 = (UInt256)1; + UInt256 storageKey2 = (UInt256)2; + UInt256 storageKey3 = (UInt256)3; + + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey2) + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey3) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002"]},{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000003"]}]"""); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListTransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListTransactionForRpcTests.cs new file mode 100644 index 00000000000..d1ad395d10b --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListTransactionForRpcTests.cs @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class AccessListTransactionForRpcTests +{ + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction.WithType(TxType.AccessList); + public static readonly Transaction[] Transactions = + [ + Build.TestObject, + + Build.WithNonce(UInt256.Zero).TestObject, + Build.WithNonce((UInt256)123).TestObject, + Build.WithNonce(UInt256.MaxValue).TestObject, + + Build.WithTo(null).TestObject, + Build.WithTo(TestItem.AddressA).TestObject, + Build.WithTo(TestItem.AddressB).TestObject, + Build.WithTo(TestItem.AddressC).TestObject, + Build.WithTo(TestItem.AddressD).TestObject, + Build.WithTo(TestItem.AddressE).TestObject, + Build.WithTo(TestItem.AddressF).TestObject, + + Build.WithGasLimit(0).TestObject, + Build.WithGasLimit(123).TestObject, + Build.WithGasLimit(long.MaxValue).TestObject, + + Build.WithValue(UInt256.Zero).TestObject, + Build.WithValue((UInt256)123).TestObject, + Build.WithValue(UInt256.MaxValue).TestObject, + + Build.WithData(TestItem.RandomDataA).TestObject, + Build.WithData(TestItem.RandomDataB).TestObject, + Build.WithData(TestItem.RandomDataC).TestObject, + Build.WithData(TestItem.RandomDataD).TestObject, + + Build.WithGasPrice(UInt256.Zero).TestObject, + Build.WithGasPrice((UInt256)123).TestObject, + Build.WithGasPrice(UInt256.MaxValue).TestObject, + + Build.WithChainId(null).TestObject, + Build.WithChainId(BlockchainIds.Mainnet).TestObject, + Build.WithChainId(BlockchainIds.Sepolia).TestObject, + Build.WithChainId(0).TestObject, + Build.WithChainId(ulong.MaxValue).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddAddress(TestItem.AddressB) + .AddStorage(3) + .Build()).TestObject, + + Build.WithSignature(TestItem.RandomSignatureA).TestObject, + Build.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x1$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("gasPrice").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + // Suprising inconsistency in `FluentAssertions` where `AllSatisfy` fails on empty collections. + // This requires wrapping the assertion in a condition. + // See: https://github.com/fluentassertions/fluentassertions/discussions/2143#discussioncomment-9677309 + var accessList = json.GetProperty("accessList").EnumerateArray(); + if (accessList.Any()) + { + accessList.Should().AllSatisfy(item => + { + item.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + item.GetProperty("storageKeys").EnumerateArray().Should().AllSatisfy(key => + key.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + }); + } + json.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var yParity = json.GetProperty("yParity").GetString(); + yParity.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + if (json.TryGetProperty("v", out var v)) + { + v.GetString().Should().Be(yParity); + } + json.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/BlobTransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/BlobTransactionForRpcTests.cs new file mode 100644 index 00000000000..add25e10ad9 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/BlobTransactionForRpcTests.cs @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class BlobTransactionForRpcTests +{ + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction + .WithType(TxType.Blob) + // NOTE: We require to initialize these properties to non-null values + .WithMaxFeePerBlobGas(UInt256.Zero) + .WithBlobVersionedHashes([]); + + public static readonly Transaction[] Transactions = + [ + Build.TestObject, + + Build.WithNonce(UInt256.Zero).TestObject, + Build.WithNonce(123).TestObject, + Build.WithNonce(UInt256.MaxValue).TestObject, + + Build.WithTo(null).TestObject, + Build.WithTo(TestItem.AddressA).TestObject, + Build.WithTo(TestItem.AddressB).TestObject, + Build.WithTo(TestItem.AddressC).TestObject, + Build.WithTo(TestItem.AddressD).TestObject, + Build.WithTo(TestItem.AddressE).TestObject, + Build.WithTo(TestItem.AddressF).TestObject, + + Build.WithGasLimit(0).TestObject, + Build.WithGasLimit(123).TestObject, + Build.WithGasLimit(long.MaxValue).TestObject, + + Build.WithValue(UInt256.Zero).TestObject, + Build.WithValue(123).TestObject, + Build.WithValue(UInt256.MaxValue).TestObject, + + Build.WithData(TestItem.RandomDataA).TestObject, + Build.WithData(TestItem.RandomDataB).TestObject, + Build.WithData(TestItem.RandomDataC).TestObject, + Build.WithData(TestItem.RandomDataD).TestObject, + + Build.WithMaxPriorityFeePerGas(UInt256.Zero).TestObject, + Build.WithMaxPriorityFeePerGas(123).TestObject, + Build.WithMaxPriorityFeePerGas(UInt256.MaxValue).TestObject, + + Build.WithMaxFeePerGas(UInt256.Zero).TestObject, + Build.WithMaxFeePerGas(123).TestObject, + Build.WithMaxFeePerGas(UInt256.MaxValue).TestObject, + + Build.WithMaxFeePerBlobGas(UInt256.Zero).TestObject, + Build.WithMaxFeePerBlobGas(123).TestObject, + Build.WithMaxFeePerBlobGas(UInt256.MaxValue).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddAddress(TestItem.AddressB) + .AddStorage(3) + .Build()).TestObject, + + Build.WithBlobVersionedHashes(0).TestObject, + Build.WithBlobVersionedHashes(1).TestObject, + Build.WithBlobVersionedHashes(50).TestObject, + + Build.WithChainId(null).TestObject, + Build.WithChainId(BlockchainIds.Mainnet).TestObject, + Build.WithChainId(BlockchainIds.Sepolia).TestObject, + Build.WithChainId(0).TestObject, + Build.WithChainId(ulong.MaxValue).TestObject, + + Build.WithSignature(TestItem.RandomSignatureA).TestObject, + Build.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x3$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("maxPriorityFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("maxFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("maxFeePerBlobGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var accessList = json.GetProperty("accessList").EnumerateArray(); + if (accessList.Any()) + { + accessList.Should().AllSatisfy(item => + { + item.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + item.GetProperty("storageKeys").EnumerateArray().Should().AllSatisfy(key => + key.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + }); + } + var blobVersionedHashes = json.GetProperty("blobVersionedHashes").EnumerateArray(); + if (blobVersionedHashes.Any()) + { + blobVersionedHashes.Should().AllSatisfy(hash => + hash.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + } + json.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("yParity").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + + // Assert deprecated fields are no longer serialized + json.TryGetProperty("v", out _).Should().BeFalse(); + json.TryGetProperty("gasPrice", out _).Should().BeFalse(); + + // Assert deserialization-only are not serialized + json.TryGetProperty("blobs", out _).Should().BeFalse(); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/EIP1559TransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/EIP1559TransactionForRpcTests.cs new file mode 100644 index 00000000000..13beb3b643b --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/EIP1559TransactionForRpcTests.cs @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class EIP1559TransactionForRpcTests +{ + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction.WithType(TxType.EIP1559); + public static readonly Transaction[] Transactions = + [ + Build.TestObject, + + Build.WithNonce(UInt256.Zero).TestObject, + Build.WithNonce(123).TestObject, + Build.WithNonce(UInt256.MaxValue).TestObject, + + Build.WithTo(null).TestObject, + Build.WithTo(TestItem.AddressA).TestObject, + Build.WithTo(TestItem.AddressB).TestObject, + Build.WithTo(TestItem.AddressC).TestObject, + Build.WithTo(TestItem.AddressD).TestObject, + Build.WithTo(TestItem.AddressE).TestObject, + Build.WithTo(TestItem.AddressF).TestObject, + + Build.WithGasLimit(0).TestObject, + Build.WithGasLimit(123).TestObject, + Build.WithGasLimit(long.MaxValue).TestObject, + + Build.WithValue(UInt256.Zero).TestObject, + Build.WithValue(123).TestObject, + Build.WithValue(UInt256.MaxValue).TestObject, + + Build.WithData(TestItem.RandomDataA).TestObject, + Build.WithData(TestItem.RandomDataB).TestObject, + Build.WithData(TestItem.RandomDataC).TestObject, + Build.WithData(TestItem.RandomDataD).TestObject, + + Build.WithMaxPriorityFeePerGas(UInt256.Zero).TestObject, + Build.WithMaxPriorityFeePerGas(123).TestObject, + Build.WithMaxPriorityFeePerGas(UInt256.MaxValue).TestObject, + + Build.WithMaxFeePerGas(UInt256.Zero).TestObject, + Build.WithMaxFeePerGas(123).TestObject, + Build.WithMaxFeePerGas(UInt256.MaxValue).TestObject, + + Build.WithGasPrice(UInt256.Zero).TestObject, + Build.WithGasPrice(123).TestObject, + Build.WithGasPrice(UInt256.MaxValue).TestObject, + + Build.WithChainId(null).TestObject, + Build.WithChainId(BlockchainIds.Mainnet).TestObject, + Build.WithChainId(BlockchainIds.Sepolia).TestObject, + Build.WithChainId(0).TestObject, + Build.WithChainId(ulong.MaxValue).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddAddress(TestItem.AddressB) + .AddStorage(3) + .Build()).TestObject, + + Build.WithSignature(TestItem.RandomSignatureA).TestObject, + Build.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x2$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("maxPriorityFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("maxFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("gasPrice").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var accessList = json.GetProperty("accessList").EnumerateArray(); + if (accessList.Any()) + { + accessList.Should().AllSatisfy(item => + { + item.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + item.GetProperty("storageKeys").EnumerateArray().Should().AllSatisfy(key => + key.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + }); + } + json.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var yParity = json.GetProperty("yParity").GetString(); + yParity.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + if (json.TryGetProperty("v", out var v)) + { + v.GetString().Should().Be(yParity); + } + json.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/LegacyTransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/LegacyTransactionForRpcTests.cs new file mode 100644 index 00000000000..3173927512c --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/LegacyTransactionForRpcTests.cs @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class LegacyTransactionForRpcTests +{ + private static TransactionBuilder BuildALegacyTransaction => Build.A.Transaction.WithType(TxType.Legacy); + public static readonly Transaction[] Transactions = + [ + BuildALegacyTransaction.TestObject, + + BuildALegacyTransaction.WithNonce(UInt256.Zero).TestObject, + BuildALegacyTransaction.WithNonce((UInt256)123).TestObject, + BuildALegacyTransaction.WithNonce(UInt256.MaxValue).TestObject, + + BuildALegacyTransaction.WithTo(null).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressA).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressB).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressC).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressD).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressE).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressF).TestObject, + + BuildALegacyTransaction.WithGasLimit(0).TestObject, + BuildALegacyTransaction.WithGasLimit(123).TestObject, + BuildALegacyTransaction.WithGasLimit(long.MaxValue).TestObject, + + BuildALegacyTransaction.WithValue(UInt256.Zero).TestObject, + BuildALegacyTransaction.WithValue((UInt256)123).TestObject, + BuildALegacyTransaction.WithValue(UInt256.MaxValue).TestObject, + + BuildALegacyTransaction.WithData(TestItem.RandomDataA).TestObject, + BuildALegacyTransaction.WithData(TestItem.RandomDataB).TestObject, + BuildALegacyTransaction.WithData(TestItem.RandomDataC).TestObject, + BuildALegacyTransaction.WithData(TestItem.RandomDataD).TestObject, + + BuildALegacyTransaction.WithGasPrice(UInt256.Zero).TestObject, + BuildALegacyTransaction.WithGasPrice((UInt256)123).TestObject, + BuildALegacyTransaction.WithGasPrice(UInt256.MaxValue).TestObject, + + BuildALegacyTransaction.WithChainId(null).TestObject, + BuildALegacyTransaction.WithChainId(BlockchainIds.Mainnet).TestObject, + BuildALegacyTransaction.WithChainId(BlockchainIds.Sepolia).TestObject, + BuildALegacyTransaction.WithChainId(0).TestObject, + BuildALegacyTransaction.WithChainId(ulong.MaxValue).TestObject, + + BuildALegacyTransaction.WithSignature(TestItem.RandomSignatureA).TestObject, + BuildALegacyTransaction.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x0$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("from").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("gasPrice").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + bool hasChainId = json.TryGetProperty("chainId", out var chainId); + if (hasChainId) + { + chainId.GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } + json.GetProperty("v").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/SetCodeTransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/SetCodeTransactionForRpcTests.cs new file mode 100644 index 00000000000..296b60471a7 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/SetCodeTransactionForRpcTests.cs @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class SetCodeTransactionForRpcTests +{ + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction.WithType(TxType.SetCode) + // NOTE: We require to initialize the authorization list + .WithAuthorizationCodeIfAuthorizationListTx(); + + public static readonly Transaction[] Transactions = + [ + Build.TestObject, + + Build.WithNonce(UInt256.Zero).TestObject, + Build.WithNonce((UInt256)123).TestObject, + Build.WithNonce(UInt256.MaxValue).TestObject, + + Build.WithTo(null).TestObject, + Build.WithTo(TestItem.AddressA).TestObject, + Build.WithTo(TestItem.AddressB).TestObject, + Build.WithTo(TestItem.AddressC).TestObject, + Build.WithTo(TestItem.AddressD).TestObject, + Build.WithTo(TestItem.AddressE).TestObject, + Build.WithTo(TestItem.AddressF).TestObject, + + Build.WithGasLimit(0).TestObject, + Build.WithGasLimit(123).TestObject, + Build.WithGasLimit(long.MaxValue).TestObject, + + Build.WithValue(UInt256.Zero).TestObject, + Build.WithValue((UInt256)123).TestObject, + Build.WithValue(UInt256.MaxValue).TestObject, + + Build.WithData(TestItem.RandomDataA).TestObject, + Build.WithData(TestItem.RandomDataB).TestObject, + Build.WithData(TestItem.RandomDataC).TestObject, + Build.WithData(TestItem.RandomDataD).TestObject, + + Build.WithGasPrice(UInt256.Zero).TestObject, + Build.WithGasPrice((UInt256)123).TestObject, + Build.WithGasPrice(UInt256.MaxValue).TestObject, + + Build.WithChainId(null).TestObject, + Build.WithChainId(BlockchainIds.Mainnet).TestObject, + Build.WithChainId(BlockchainIds.Sepolia).TestObject, + Build.WithChainId(0).TestObject, + Build.WithChainId(ulong.MaxValue).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddAddress(TestItem.AddressB) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAuthorizationCode(new AuthorizationTuple( + chainId: BlockchainIds.Mainnet, + codeAddress: TestItem.AddressA, + nonce: 123, + sig: TestItem.RandomSignatureA + )).TestObject, + + Build.WithAuthorizationCode(new AuthorizationTuple( + chainId: BlockchainIds.Sepolia, + codeAddress: TestItem.AddressA, + nonce: ulong.MaxValue, + sig: TestItem.RandomSignatureA + )).WithAuthorizationCode(new AuthorizationTuple( + chainId: BlockchainIds.Sepolia, + codeAddress: TestItem.AddressB, + nonce: 0, + sig: TestItem.RandomSignatureB + )).TestObject, + + Build.WithSignature(TestItem.RandomSignatureA).TestObject, + Build.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x4$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("maxPriorityFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("maxFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var accessList = json.GetProperty("accessList").EnumerateArray(); + if (accessList.Any()) + { + accessList.Should().AllSatisfy(item => + { + item.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + item.GetProperty("storageKeys").EnumerateArray().Should().AllSatisfy(key => + key.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + }); + } + json.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("yParity").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + // NOTE: Empty authorization lists are considered invalid + json.GetProperty("authorizationList").EnumerateArray().Should().AllSatisfy(tuple => + { + tuple.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + tuple.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + tuple.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + tuple.GetProperty("yParity").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + tuple.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + tuple.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + }); + + // Assert deprecated fields are no longer serialized + json.TryGetProperty("gasPrice", out _).Should().BeFalse(); + json.TryGetProperty("v", out _).Should().BeFalse(); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/TransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/TransactionForRpcTests.cs new file mode 100644 index 00000000000..23e9aeb10b2 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/TransactionForRpcTests.cs @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Json; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Serialization.Json; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public class TransactionForRpcTests +{ + private readonly IJsonSerializer _serializer = new EthereumJsonSerializer(); + + public static readonly ulong SomeChainId = 123ul; + + public static readonly Transaction[] Transactions = + [ + .. LegacyTransactionForRpcTests.Transactions, + .. AccessListTransactionForRpcTests.Transactions, + .. EIP1559TransactionForRpcTests.Transactions, + .. BlobTransactionForRpcTests.Transactions, + .. SetCodeTransactionForRpcTests.Transactions, + ]; + + [Test] + public void R_and_s_are_quantity_and_not_data() + { + byte[] r = new byte[32]; + byte[] s = new byte[32]; + r[1] = 1; + s[2] = 2; + + Transaction tx = new() + { + Signature = new Signature(r, s, 27) + }; + + var txForRpc = TransactionForRpc.FromTransaction(tx); + + EthereumJsonSerializer serializer = new(); + string serialized = serializer.Serialize(txForRpc); + + var json = JObject.Parse(serialized); + var expectedS = JObject.Parse("""{ "s": "0x20000000000000000000000000000000000000000000000000000000000"}"""); + var expectedR = JObject.Parse("""{ "r": "0x1000000000000000000000000000000000000000000000000000000000000"}"""); + + json.Should().ContainSubtree(expectedS); + json.Should().ContainSubtree(expectedR); + } + + [TestCaseSource(nameof(Transactions))] + public void Serialized_JSON_satisfies_schema(Transaction transaction) + { + TransactionForRpc rpcTransaction = TransactionForRpc.FromTransaction(transaction, chainId: SomeChainId); + string serialized = _serializer.Serialize(rpcTransaction); + using var jsonDocument = JsonDocument.Parse(serialized); + JsonElement json = jsonDocument.RootElement; + + switch (transaction.Type) + { + case TxType.Legacy: + LegacyTransactionForRpcTests.ValidateSchema(json); + break; + case TxType.AccessList: + AccessListTransactionForRpcTests.ValidateSchema(json); + break; + case TxType.EIP1559: + EIP1559TransactionForRpcTests.ValidateSchema(json); + break; + case TxType.Blob: + BlobTransactionForRpcTests.ValidateSchema(json); + break; + case TxType.SetCode: + SetCodeTransactionForRpcTests.ValidateSchema(json); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + [TestCaseSource(nameof(Transactions))] + public void Serialized_JSON_satisfies_Nethermind_fields_schema(Transaction transaction) + { + TransactionForRpc rpcTransaction = TransactionForRpc.FromTransaction(transaction, chainId: SomeChainId); + string serialized = _serializer.Serialize(rpcTransaction); + using var jsonDocument = JsonDocument.Parse(serialized); + JsonElement json = jsonDocument.RootElement; + + json.GetProperty("hash").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{64}$"); + json.GetProperty("transactionIndex").GetString()?.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("blockHash").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{64}$"); + json.GetProperty("blockNumber").GetString()?.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs index 1d132d4bf33..9abd1ff32e3 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; +using FluentAssertions.Json; using Nethermind.Blockchain; using Nethermind.Blockchain.Filters; using Nethermind.Blockchain.Find; @@ -29,7 +30,7 @@ using Nethermind.Specs; using Nethermind.Synchronization.ParallelSync; using Nethermind.TxPool; - +using Newtonsoft.Json.Linq; using NSubstitute; using NUnit.Framework; @@ -93,6 +94,8 @@ public void Setup() BlockHeader toBlock = Build.A.BlockHeader.WithNumber(77777).TestObject; _blockTree.FindHeader(Arg.Any()).Returns(fromBlock); _blockTree.FindHeader(Arg.Any(), true).Returns(toBlock); + + _specProvider.ChainId.Returns((ulong)BlockchainIds.Mainnet); } [TearDown] @@ -145,7 +148,7 @@ private List GetLogsSubscriptionResult(Filter filter, BlockReplac private JsonRpcResult GetNewPendingTransactionsResult(TxEventArgs txEventArgs, out string subscriptionId, TransactionsOption? option = null) { - NewPendingTransactionsSubscription newPendingTransactionsSubscription = new(_jsonRpcDuplexClient, _txPool, _logManager, option); + NewPendingTransactionsSubscription newPendingTransactionsSubscription = new(_jsonRpcDuplexClient, _txPool, _specProvider, _logManager, option); JsonRpcResult jsonRpcResult = new(); ManualResetEvent manualResetEvent = new(false); @@ -816,8 +819,8 @@ public void NewPendingTransactionsSubscription_on_NewPending_with_includeTransac jsonRpcResult.Response.Should().NotBeNull(); string serialized = _jsonSerializer.Serialize(jsonRpcResult.Response); - var expectedResult = string.Concat("{\"jsonrpc\":\"2.0\",\"method\":\"eth_subscription\",\"params\":{\"subscription\":\"", subscriptionId, "\",\"result\":{\"nonce\":\"0x0\",\"blockHash\":null,\"blockNumber\":null,\"transactionIndex\":null,\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\"}}}"); - expectedResult.Should().Be(serialized); + + JToken.Parse(serialized).Should().BeEquivalentTo($$$$"""{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"{{{{subscriptionId}}}}","result":{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","type":"0x0","hash":null,"v":"0x0","r":"0x0","s":"0x0","from":null}}}"""); } [TestCase(2)] @@ -844,6 +847,7 @@ public async Task NewPendingTransactionSubscription_multiple_fast_messages(int m using NewPendingTransactionsSubscription subscription = new( jsonRpcDuplexClient: client, txPool: txPool, + specProvider: _specProvider, logManager: LimboLogs.Instance); for (int i = 0; i < messages; i++) @@ -882,6 +886,7 @@ public async Task MultipleSubscriptions_concurrent_fast_messages(int messages) // ReSharper disable once AccessToDisposedClosure jsonRpcDuplexClient: client, txPool: txPool, + specProvider: _specProvider, logManager: LimboLogs.Instance); for (int i = 0; i < messages; i++) diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs index ea070b4502f..1a63fa846cd 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs @@ -15,7 +15,6 @@ using Nethermind.Core.Test.Builders; using Nethermind.Crypto; using Nethermind.Facade; -using Nethermind.JsonRpc.Modules; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.Logging; using Nethermind.Facade.Eth; @@ -174,10 +173,7 @@ protected override async Task Build( return this; } - public Task TestEthRpc(string method, params string[] parameters) => + public Task TestEthRpc(string method, params object?[]? parameters) => RpcTest.TestSerializedRequest(EthRpcModule, method, parameters); - - public Task TestSerializedRequest(T module, string method, params string[] parameters) where T : class, IRpcModule => - RpcTest.TestSerializedRequest(module, method, parameters); } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs index 6b264462616..5847c662c46 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs @@ -21,10 +21,9 @@ using Nethermind.Consensus.Tracing; using Nethermind.Consensus.Validators; using Nethermind.Core.Crypto; -using Nethermind.Db; using Nethermind.Evm; using Nethermind.Evm.TransactionProcessing; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Serialization.Json; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; @@ -666,7 +665,7 @@ public async Task Trace_call_without_blockParameter_provided_test() .WithGasLimit(93548).TestObject; await blockchain.AddBlock(transaction2); - TransactionForRpc transactionRpc = new(transaction2); + TransactionForRpc transactionRpc = TransactionForRpc.FromTransaction(transaction2); string[] traceTypes = { "trace" }; @@ -681,8 +680,8 @@ public async Task Trace_call_simple_tx_test() { Context context = new(); await context.Build(); - string transaction = "{\"from\":\"0xaaaaaaaa8583de65cc752fe3fad5098643244d22\",\"to\":\"0xd6a8d04cb9846759416457e2c593c99390092df6\"}"; - string traceTypes = "[\"trace\"]"; + object transaction = new { from = "0xaaaaaaaa8583de65cc752fe3fad5098643244d22", to = "0xd6a8d04cb9846759416457e2c593c99390092df6" }; + string[] traceTypes = { "trace" }; string blockParameter = "latest"; string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0xaaaaaaaa8583de65cc752fe3fad5098643244d22\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xd6a8d04cb9846759416457e2c593c99390092df6\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}"; @@ -693,17 +692,20 @@ public async Task Trace_call_simple_tx_test() Assert.That(serialized, Is.EqualTo(expectedResult), serialized.Replace("\"", "\\\"")); } - [TestCase("{\"from\":\"0x7f554713be84160fdf0178cc8df86f5aabd33397\",\"to\":\"0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f\",\"value\":\"0x0\",\"gasPrice\":\"0x119e04a40a\"}", "[\"trace\"]", "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x7f554713be84160fdf0178cc8df86f5aabd33397\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}")] - [TestCase("{\"from\":\"0xc71acc7863f3bc7347b24c3b835643bd89d4d161\",\"to\":\"0xa760e26aa76747020171fcf8bda108dfde8eb930\",\"value\":\"0x0\",\"gasPrice\":\"0x2108eea5bc\"}", "[\"trace\"]", "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0xc71acc7863f3bc7347b24c3b835643bd89d4d161\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xa760e26aa76747020171fcf8bda108dfde8eb930\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}")] - public async Task Trace_call_without_blockParameter_test(string transaction, string traceTypes, string expectedResult) + private static readonly IEnumerable<(object, string[], string)> Trace_call_without_blockParameter_test_cases = [ + (new { from = "0x7f554713be84160fdf0178cc8df86f5aabd33397", to = "0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f", value = "0x0", gasPrice = "0x119e04a40a" }, ["trace"], "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x7f554713be84160fdf0178cc8df86f5aabd33397\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}"), + (new { from = "0xc71acc7863f3bc7347b24c3b835643bd89d4d161", to = "0xa760e26aa76747020171fcf8bda108dfde8eb930", value = "0x0", gasPrice = "0x2108eea5bc" }, ["trace"], "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0xc71acc7863f3bc7347b24c3b835643bd89d4d161\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xa760e26aa76747020171fcf8bda108dfde8eb930\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}") + ]; + [TestCaseSource(nameof(Trace_call_without_blockParameter_test_cases))] + public async Task Trace_call_without_blockParameter_test((object transaction, string[] traceTypes, string expectedResult) testCase) { Context context = new(); await context.Build(); string serialized = await RpcTest.TestSerializedRequest( context.TraceRpcModule, - "trace_call", transaction, traceTypes); + "trace_call", testCase.transaction, testCase.traceTypes); - Assert.That(serialized, Is.EqualTo(expectedResult), serialized.Replace("\"", "\\\"")); + Assert.That(serialized, Is.EqualTo(testCase.expectedResult), serialized.Replace("\"", "\\\"")); } [Test] @@ -717,14 +719,14 @@ public async Task Trace_callMany_internal_transactions_test() Transaction transaction1 = Build.A.Transaction.WithNonce(currentNonceAddressA++).WithTo(TestItem.AddressC) .SignedAndResolved(TestItem.PrivateKeyA).TestObject; - TransactionForRpc txForRpc1 = new(transaction1); + TransactionForRpc txForRpc1 = TransactionForRpc.FromTransaction(transaction1); string[] traceTypes1 = { "Trace" }; Transaction transaction2 = Build.A.Transaction.WithNonce(currentNonceAddressA++).WithTo(TestItem.AddressD) .SignedAndResolved(TestItem.PrivateKeyA).TestObject; await blockchain.AddBlock(transaction1, transaction2); - TransactionForRpc txForRpc2 = new(transaction2); + TransactionForRpc txForRpc2 = TransactionForRpc.FromTransaction(transaction2); string[] traceTypes2 = { "Trace" }; BlockParameter numberOrTag = new(16); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TransactionForRpcConverterTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TransactionForRpcConverterTests.cs deleted file mode 100644 index 6b6add97578..00000000000 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TransactionForRpcConverterTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using FluentAssertions; -using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; -using Nethermind.JsonRpc.Test.Data; -using Nethermind.Serialization.Json; -using NUnit.Framework; - -namespace Nethermind.JsonRpc.Test.Modules -{ - [Parallelizable(ParallelScope.Self)] - [TestFixture] - public class TransactionForRpcConverterTests : SerializationTestBase - { - [Test] - public void R_and_s_are_quantity_and_not_data() - { - byte[] r = new byte[32]; - byte[] s = new byte[32]; - r[1] = 1; - s[2] = 2; - - Transaction tx = new(); - tx.Signature = new Signature(r, s, 27); - - TransactionForRpc txForRpc = new(tx); - - EthereumJsonSerializer serializer = new(); - string serialized = serializer.Serialize(txForRpc); - - serialized.Should().Contain("0x20000000000000000000000000000000000000000000000000000000000"); - serialized.Should().Contain("0x1000000000000000000000000000000000000000000000000000000000000"); - Console.WriteLine(serialized); - } - } -} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs new file mode 100644 index 00000000000..be91f5763a0 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +// using System.Collections.Generic; + +using System.Collections.Generic; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.JsonRpc.Modules.TxPool; +using Nethermind.TxPool; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.Test.Modules; + +public class TxPoolRpcModuleTests +{ + [Test] + public void Pool_content_produces_transactions_with_ChainId() + { + const ulong SomeChainId = 123ul; + var txA = Build.A.Transaction + .WithType(TxType.Legacy) + .WithChainId(null) + .TestObject; + var txB = Build.A.Transaction + .WithType(TxType.AccessList) + .WithAccessList(AccessList.Empty) + .WithChainId(null) + .TestObject; + + var txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo( + pending: new() + { + { + new AddressAsKey(TestItem.AddressA), new Dictionary + { + { 1, txA } + } + } + }, + queued: new() + { + { + new AddressAsKey(TestItem.AddressB), new Dictionary + { + { 2, txB } + } + } + } + )); + + var specProvider = Substitute.For(); + specProvider.ChainId.Returns(SomeChainId); + + var txPoolRpcModule = new TxPoolRpcModule(txPoolInfoProvider, specProvider); + + var txpoolContent = txPoolRpcModule.txpool_content().Data; + + var rpcTxA = txpoolContent.Pending[new AddressAsKey(TestItem.AddressA)][1] as LegacyTransactionForRpc; + var rpcTxB = txpoolContent.Queued[new AddressAsKey(TestItem.AddressB)][2] as AccessListTransactionForRpc; + + rpcTxA!.ChainId.Should().BeNull(); + rpcTxB!.ChainId.Should().Be(SomeChainId); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/RpcTest.cs b/src/Nethermind/Nethermind.JsonRpc.Test/RpcTest.cs index 4bc26918014..5a235eb5bb2 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/RpcTest.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/RpcTest.cs @@ -1,9 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using System.IO; -using System.Linq; using System.Text.Json; using System.Threading.Tasks; @@ -14,40 +12,38 @@ using Nethermind.Logging; using Nethermind.Serialization.Json; -using NUnit.Framework; - namespace Nethermind.JsonRpc.Test; public static class RpcTest { - public static async Task TestRequest(T module, string method, params string[] parameters) where T : class, IRpcModule + public static async Task TestRequest(T module, string method, params object?[]? parameters) where T : class, IRpcModule { IJsonRpcService service = BuildRpcService(module); - JsonRpcRequest request = GetJsonRequest(method, parameters); + JsonRpcRequest request = BuildJsonRequest(method, parameters); return await service.SendRequestAsync(request, new JsonRpcContext(RpcEndpoint.Http)); } - public static async Task TestSerializedRequest(T module, string method, params string[] parameters) where T : class, IRpcModule + public static async Task TestSerializedRequest(T module, string method, params object?[]? parameters) where T : class, IRpcModule { IJsonRpcService service = BuildRpcService(module); - JsonRpcRequest request = GetJsonRequest(method, parameters); + JsonRpcRequest request = BuildJsonRequest(method, parameters); - JsonRpcContext context = module is IContextAwareRpcModule { Context: not null } contextAwareModule ? - contextAwareModule.Context : - new JsonRpcContext(RpcEndpoint.Http); - using JsonRpcResponse response = await service.SendRequestAsync(request, context); + JsonRpcContext context = module is IContextAwareRpcModule { Context: not null } contextAwareModule + ? contextAwareModule.Context + : new JsonRpcContext(RpcEndpoint.Http); + using JsonRpcResponse response = await service.SendRequestAsync(request, context).ConfigureAwait(false); EthereumJsonSerializer serializer = new(); Stream stream = new MemoryStream(); - long size = await serializer.SerializeAsync(stream, response); + long size = await serializer.SerializeAsync(stream, response).ConfigureAwait(false); // for coverage (and to prove that it does not throw Stream indentedStream = new MemoryStream(); - await serializer.SerializeAsync(indentedStream, response, true); + await serializer.SerializeAsync(indentedStream, response, true).ConfigureAwait(false); stream.Seek(0, SeekOrigin.Begin); - string serialized = await new StreamReader(stream).ReadToEndAsync(); + string serialized = await new StreamReader(stream).ReadToEndAsync().ConfigureAwait(false); size.Should().Be(serialized.Length); @@ -63,18 +59,21 @@ private static IJsonRpcService BuildRpcService(T module) where T : class, IRp return service; } - public static JsonRpcRequest GetJsonRequest(string method, params string[]? parameters) + public static JsonRpcRequest BuildJsonRequest(string method, params object?[]? parameters) { - var doc = JsonDocument.Parse(JsonSerializer.Serialize(parameters?.ToArray())); - var request = new JsonRpcRequest() + // TODO: Eventually we would like to support injecting a custom serializer + var serializer = new EthereumJsonSerializer(); + parameters ??= []; + + var jsonParameters = serializer.Deserialize(serializer.Serialize(parameters)); + + return new JsonRpcRequest { JsonRpc = "2.0", Method = method, - Params = doc.RootElement, + Params = jsonParameters, Id = 67 }; - - return request; } private class TestSingletonFactory(T module) : SingletonFactory(module) diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Test/TraceStoreRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Test/TraceStoreRpcModuleTests.cs index 8c82d299e41..05ec44a9e1a 100644 --- a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Test/TraceStoreRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Test/TraceStoreRpcModuleTests.cs @@ -12,7 +12,7 @@ using Nethermind.Core.Test.Builders; using Nethermind.Db; using Nethermind.Evm.Tracing.ParityStyle; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; using Nethermind.JsonRpc.Modules.Trace; using Nethermind.Logging; @@ -29,7 +29,10 @@ public void trace_call_returns_from_inner_module() { TestContext test = new(); - test.Module.trace_call(new TransactionForRpc(Build.A.Transaction.TestObject), new[] { ParityTraceTypes.Trace.ToString() }, BlockParameter.Latest) + test.Module.trace_call( + call: TransactionForRpc.FromTransaction(Build.A.Transaction.TestObject), + traceTypes: [ParityTraceTypes.Trace.ToString()], + blockParameter: BlockParameter.Latest) .Should().BeEquivalentTo(ResultWrapper.Success(new ParityTxTraceFromReplay(test.NonDbTraces[0]))); } @@ -38,7 +41,9 @@ public void trace_callMany_returns_from_inner_module() { TestContext test = new(); - TransactionForRpcWithTraceTypes[] calls = { new() { TraceTypes = new[] { ParityTraceTypes.Trace.ToString() }, Transaction = new TransactionForRpc(Build.A.Transaction.TestObject) } }; + TransactionForRpcWithTraceTypes[] calls = [ + new() { TraceTypes = [ParityTraceTypes.Trace.ToString()], Transaction = TransactionForRpc.FromTransaction(Build.A.Transaction.TestObject) } + ]; test.Module.trace_callMany(calls, BlockParameter.Latest) .Should().BeEquivalentTo(ResultWrapper>.Success(test.NonDbTraces.Select(t => new ParityTxTraceFromReplay(t)))); } diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs index d9267e28112..12b38afe90a 100644 --- a/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs @@ -7,7 +7,7 @@ using Nethermind.Core.Crypto; using Nethermind.Db; using Nethermind.Evm.Tracing.ParityStyle; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; using Nethermind.JsonRpc.Modules; using Nethermind.JsonRpc.Modules.Trace; diff --git a/src/Nethermind/Nethermind.JsonRpc/Data/AccessListForRpc.cs b/src/Nethermind/Nethermind.JsonRpc/Data/AccessListForRpc.cs deleted file mode 100644 index 7c21797de61..00000000000 --- a/src/Nethermind/Nethermind.JsonRpc/Data/AccessListForRpc.cs +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; -using Nethermind.Facade.Eth; -using Nethermind.Int256; - -namespace Nethermind.JsonRpc.Data -{ - public readonly struct AccessListForRpc - { - public AccessListForRpc(IEnumerable accessList, in UInt256 gasUsed) - { - AccessList = accessList; - GasUsed = gasUsed; - } - - public IEnumerable AccessList { get; } - - public UInt256 GasUsed { get; } - } -} diff --git a/src/Nethermind/Nethermind.JsonRpc/Data/AccessListResultForRpc.cs b/src/Nethermind/Nethermind.JsonRpc/Data/AccessListResultForRpc.cs new file mode 100644 index 00000000000..a86aa7317b5 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc/Data/AccessListResultForRpc.cs @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Data; + +public readonly struct AccessListResultForRpc +{ + public AccessListForRpc AccessList { get; init; } + + public UInt256 GasUsed { get; init; } + + public AccessListResultForRpc(AccessListForRpc accessList, in UInt256 gasUsed) + { + AccessList = accessList; + GasUsed = gasUsed; + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpcWithTraceTypes.cs b/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpcWithTraceTypes.cs index 5f5c98acaf1..9ba096aaa6b 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpcWithTraceTypes.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpcWithTraceTypes.cs @@ -4,24 +4,17 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; -namespace Nethermind.JsonRpc.Data -{ - using Nethermind.JsonRpc.Modules.Trace; - - [JsonConverter(typeof(TransactionForRpcWithTraceTypesConverter))] - public class TransactionForRpcWithTraceTypes - { - public TransactionForRpc Transaction { get; set; } - public string[] TraceTypes { get; set; } - } -} +namespace Nethermind.JsonRpc.Data; -namespace Nethermind.JsonRpc.Modules.Trace +[JsonConverter(typeof(TransactionForRpcWithTraceTypesConverter))] +public class TransactionForRpcWithTraceTypes { - using Nethermind.JsonRpc.Data; - public class TransactionForRpcWithTraceTypesConverter : JsonConverter + public TransactionForRpc Transaction { get; set; } + public string[] TraceTypes { get; set; } + + private class TransactionForRpcWithTraceTypesConverter : JsonConverter { public override TransactionForRpcWithTraceTypes? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { @@ -31,6 +24,7 @@ public class TransactionForRpcWithTraceTypesConverter : JsonConverter(ref reader, options); @@ -43,8 +37,6 @@ public class TransactionForRpcWithTraceTypesConverter : JsonConverter throw new NotSupportedException(); } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs index 9c0b2919d32..aa0318197c6 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs @@ -18,7 +18,7 @@ using System.Collections.Generic; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.Core.Specs; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; namespace Nethermind.JsonRpc.Modules.DebugModule; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs index dc0a8bd9014..f8c1129cddc 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs @@ -6,7 +6,7 @@ using Nethermind.Blockchain.Find; using Nethermind.Core.Crypto; using Nethermind.Evm.Tracing.GethStyle; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.Synchronization.Reporting; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs index 6da17831925..146df3f42a2 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs @@ -1,17 +1,13 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using Nethermind.Blockchain.Find; using Nethermind.Core; -using Nethermind.Core.Eip2930; using Nethermind.Core.Extensions; using Nethermind.Evm; using Nethermind.Facade; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; using Nethermind.JsonRpc.Data; using Nethermind.Specs.Forks; @@ -27,7 +23,12 @@ private abstract class TxExecutor(IBlockchainBridge blockchainBridge, I { private bool NoBaseFee { get; set; } - protected override Transaction Prepare(TransactionForRpc call) => call.ToTransaction(_blockchainBridge.GetChainId()); + protected override Transaction Prepare(TransactionForRpc call) + { + var tx = call.ToTransaction(); + tx.ChainId = _blockchainBridge.GetChainId(); + return tx; + } protected override ResultWrapper Execute(BlockHeader header, Transaction tx, CancellationToken token) { @@ -43,15 +44,11 @@ protected override ResultWrapper Execute(BlockHeader header, Transactio return ExecuteTx(clonedHeader, tx, token); } - private static bool ShouldSetBaseFee(TransactionForRpc t) => - // x?.IsZero == false <=> x > 0 - t.GasPrice?.IsZero == false || t.MaxFeePerGas?.IsZero == false || t.MaxPriorityFeePerGas?.IsZero == false; - public override ResultWrapper Execute( TransactionForRpc transactionCall, BlockParameter? blockParameter) { - NoBaseFee = !ShouldSetBaseFee(transactionCall); + NoBaseFee = !transactionCall.ShouldSetBaseFee(); transactionCall.EnsureDefaults(_rpcConfig.GasCap); return base.Execute(transactionCall, blockParameter); } @@ -59,9 +56,6 @@ public override ResultWrapper Execute( public ResultWrapper ExecuteTx(TransactionForRpc transactionCall, BlockParameter? blockParameter) => Execute(transactionCall, blockParameter); protected abstract ResultWrapper ExecuteTx(BlockHeader header, Transaction tx, CancellationToken token); - - protected ResultWrapper GetInputError(CallOutput result) => - ResultWrapper.Fail(result.Error, ErrorCodes.InvalidInput); } private class CallTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig) @@ -87,38 +81,32 @@ private class EstimateGasTxExecutor(IBlockchainBridge blockchainBridge, IBlockFi { CallOutput result = _blockchainBridge.EstimateGas(header, tx, _errorMargin, token); - if (result.Error is null) + return result switch { - return ResultWrapper.Success((UInt256)result.GasSpent); - } - - return result.InputError - ? GetInputError(result) - : ResultWrapper.Fail(result.Error, ErrorCodes.ExecutionError); + { Error: null } => ResultWrapper.Success((UInt256)result.GasSpent), + { InputError: true } => ResultWrapper.Fail(result.Error, ErrorCodes.InvalidInput), + _ => ResultWrapper.Fail(result.Error, ErrorCodes.ExecutionError) + }; } } private class CreateAccessListTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig, bool optimize) - : TxExecutor(blockchainBridge, blockFinder, rpcConfig) + : TxExecutor(blockchainBridge, blockFinder, rpcConfig) { - protected override ResultWrapper ExecuteTx(BlockHeader header, Transaction tx, CancellationToken token) + protected override ResultWrapper ExecuteTx(BlockHeader header, Transaction tx, CancellationToken token) { CallOutput result = _blockchainBridge.CreateAccessList(header, tx, token, optimize); - if (result.Error is null) - { - return ResultWrapper.Success(new(GetResultAccessList(tx, result), GetResultGas(tx, result))); - } - - return result.InputError - ? GetInputError(result) - : ResultWrapper.Fail(result.Error, ErrorCodes.ExecutionError, new AccessListForRpc(GetResultAccessList(tx, result), GetResultGas(tx, result))); - } + var rpcAccessListResult = new AccessListResultForRpc( + accessList: AccessListForRpc.FromAccessList(result.AccessList ?? tx.AccessList), + gasUsed: GetResultGas(tx, result)); - private static IEnumerable GetResultAccessList(Transaction tx, CallOutput result) - { - AccessList? accessList = result.AccessList ?? tx.AccessList; - return accessList is null ? Enumerable.Empty() : AccessListItemForRpc.FromAccessList(accessList); + return result switch + { + { Error: null } => ResultWrapper.Success(rpcAccessListResult), + { InputError: true } => ResultWrapper.Fail(result.Error, ErrorCodes.InvalidInput), + _ => ResultWrapper.Fail(result.Error, ErrorCodes.ExecutionError), + }; } private static UInt256 GetResultGas(Transaction transaction, CallOutput result) diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs index 2a56b3ce014..97246ca48de 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs @@ -20,6 +20,7 @@ using Nethermind.Evm; using Nethermind.Facade; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Filters; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Int256; @@ -280,8 +281,12 @@ public ResultWrapper eth_sign(Address addressData, byte[] message) public virtual Task> eth_sendTransaction(TransactionForRpc rpcTx) { - Transaction tx = rpcTx.ToTransactionWithDefaults(_blockchainBridge.GetChainId()); - TxHandlingOptions options = rpcTx.Nonce is null ? TxHandlingOptions.ManagedNonce : TxHandlingOptions.None; + Transaction tx = rpcTx.ToTransaction(); + tx.ChainId = _blockchainBridge.GetChainId(); + + UInt256? nonce = rpcTx is LegacyTransactionForRpc legacy ? legacy.Nonce : null; + + TxHandlingOptions options = nonce is null ? TxHandlingOptions.ManagedNonce : TxHandlingOptions.None; return SendTx(tx, options); } @@ -334,7 +339,7 @@ public ResultWrapper> eth_simulateV1(Simulate new EstimateGasTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig) .ExecuteTx(transactionCall, blockParameter); - public ResultWrapper eth_createAccessList(TransactionForRpc transactionCall, BlockParameter? blockParameter = null, bool optimize = true) => + public ResultWrapper eth_createAccessList(TransactionForRpc transactionCall, BlockParameter? blockParameter = null, bool optimize = true) => new CreateAccessListTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig, optimize) .ExecuteTx(transactionCall, blockParameter); @@ -377,7 +382,7 @@ public ResultWrapper eth_getBlockByNumber(BlockParameter blockParam } RecoverTxSenderIfNeeded(transaction); - TransactionForRpc transactionModel = new(receipt?.BlockHash, receipt?.BlockNumber, receipt?.Index, transaction, baseFee); + TransactionForRpc transactionModel = TransactionForRpc.FromTransaction(transaction, receipt?.BlockHash, receipt?.BlockNumber, receipt?.Index, baseFee, specProvider.ChainId); if (_logger.IsTrace) _logger.Trace($"eth_getTransactionByHash request {transactionHash}, result: {transactionModel.Hash}"); return ResultWrapper.Success(transactionModel); } @@ -404,7 +409,7 @@ public ResultWrapper eth_pendingTransactions() { Transaction transaction = transactions[i]; RecoverTxSenderIfNeeded(transaction); - transactionsModels[i] = new TransactionForRpc(transaction); + transactionsModels[i] = TransactionForRpc.FromTransaction(transaction, chainId: specProvider.ChainId); transactionsModels[i].BlockHash = Keccak.Zero; } @@ -430,7 +435,7 @@ public ResultWrapper eth_getTransactionByBlockHashAndIndex(Ha Transaction transaction = block.Transactions[(int)positionIndex]; RecoverTxSenderIfNeeded(transaction); - TransactionForRpc transactionModel = new(block.Hash, block.Number, (int)positionIndex, transaction, block.BaseFeePerGas); + TransactionForRpc transactionModel = TransactionForRpc.FromTransaction(transaction, block.Hash, block.Number, (int)positionIndex, block.BaseFeePerGas, specProvider.ChainId); return ResultWrapper.Success(transactionModel); } @@ -453,7 +458,7 @@ public ResultWrapper eth_getTransactionByBlockNumberAndIndex( Transaction transaction = block.Transactions[(int)positionIndex]; RecoverTxSenderIfNeeded(transaction); - TransactionForRpc transactionModel = new(block.Hash, block.Number, (int)positionIndex, transaction, block.BaseFeePerGas); + TransactionForRpc transactionModel = TransactionForRpc.FromTransaction(transaction, block.Hash, block.Number, (int)positionIndex, block.BaseFeePerGas, specProvider.ChainId); if (_logger.IsDebug) _logger.Debug( diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs index 2915d9abcf6..3ded7a3a957 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs @@ -7,10 +7,9 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Filters; using Nethermind.Facade.Proxy.Models.Simulate; -using Nethermind.Facade.Proxy.Models; -using Nethermind.Facade.Proxy; using Nethermind.Int256; using Nethermind.JsonRpc.Data; using Nethermind.State.Proofs; @@ -171,7 +170,7 @@ ResultWrapper> eth_simulateV1([JsonRpcParamet EdgeCaseHint = "If your transaction has code executed, then you can generate transaction access list with eth_createAccessList. If you send it with your transaction then it will lower your gas cost on Ethereum", IsSharable = false, ExampleResponse = "{\"accessList\":[{\"address\":\"0xfffffffffffffffffffffffffffffffffffffffe\",\"storageKeys\":[\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"0x0000000000000000000000000000000000000000000000000000000000000002\"]},{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0xf71b\"}")] - ResultWrapper eth_createAccessList( + ResultWrapper eth_createAccessList( [JsonRpcParameter(Description = "Transaction's details", ExampleValue = "[\"{\"type\":\"0x1\"]")] TransactionForRpc transactionCall, [JsonRpcParameter(Description = "(optional)")] diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs index c0bf2db8e26..d87ae39bd1f 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs @@ -3,18 +3,15 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using Nethermind.Blockchain.Find; using Nethermind.Config; using Nethermind.Core; -using Nethermind.Core.Collections; using Nethermind.Facade; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Facade.Simulate; -using Nethermind.JsonRpc.Data; namespace Nethermind.JsonRpc.Modules.Eth; @@ -44,14 +41,15 @@ protected override SimulatePayload Prepare(Simulat StateOverrides = blockStateCall.StateOverrides, Calls = blockStateCall.Calls?.Select(callTransactionModel => { - UpdateTxType(callTransactionModel); + callTransactionModel = UpdateTxType(callTransactionModel); + LegacyTransactionForRpc asLegacy = callTransactionModel as LegacyTransactionForRpc; + bool hadGasLimitInRequest = asLegacy?.Gas is not null; + bool hadNonceInRequest = asLegacy?.Nonce is not null; + asLegacy!.EnsureDefaults(_gasCapBudget); + _gasCapBudget -= asLegacy.Gas!.Value; - bool hadGasLimitInRequest = callTransactionModel.Gas.HasValue; - bool hadNonceInRequest = callTransactionModel.Nonce.HasValue; - callTransactionModel.EnsureDefaults(_gasCapBudget); - _gasCapBudget -= callTransactionModel.Gas!.Value; - - Transaction tx = callTransactionModel.ToTransaction(_blockchainBridge.GetChainId()); + Transaction tx = callTransactionModel.ToTransaction(); + tx.ChainId = _blockchainBridge.GetChainId(); TransactionWithSourceDetails? result = new() { @@ -69,17 +67,28 @@ protected override SimulatePayload Prepare(Simulat return result; } - private static void UpdateTxType(TransactionForRpc callTransactionModel) + private static TransactionForRpc UpdateTxType(TransactionForRpc rpcTransaction) { - if (callTransactionModel.Type == TxType.Legacy) + // TODO: This is a bit messy since we're changing the transaction type + if (rpcTransaction is LegacyTransactionForRpc legacy) { - callTransactionModel.Type = TxType.EIP1559; + rpcTransaction = new EIP1559TransactionForRpc + { + Nonce = legacy.Nonce, + To = legacy.To, + From = legacy.From, + Gas = legacy.Gas, + Value = legacy.Value, + Input = legacy.Input, + GasPrice = legacy.GasPrice, + ChainId = legacy.ChainId, + V = legacy.V, + R = legacy.R, + S = legacy.S, + }; } - if (callTransactionModel.BlobVersionedHashes is not null) - { - callTransactionModel.Type = TxType.Blob; - } + return rpcTransaction; } public override ResultWrapper> Execute( @@ -188,7 +197,7 @@ .. call.BlockStateCalls.Select(b => (long)(b.BlockOverrides?.Number ?? ulong.Min { BlockOverrides = new BlockOverride { Number = (ulong)blockNumber }, StateOverrides = null, - Calls = Array.Empty() + Calls = [] }); } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/IPersonalRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/IPersonalRpcModule.cs index 3ca1db260a6..81805f149bd 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/IPersonalRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/IPersonalRpcModule.cs @@ -3,8 +3,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; namespace Nethermind.JsonRpc.Modules.Personal { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/PersonalRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/PersonalRpcModule.cs index 1116810731d..db941f73ed6 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/PersonalRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/PersonalRpcModule.cs @@ -7,8 +7,7 @@ using Nethermind.Core.Attributes; using Nethermind.Core.Crypto; using Nethermind.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.KeyStore; using Nethermind.Wallet; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/IProofRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/IProofRpcModule.cs index 8e2123e5533..353102d9037 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/IProofRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/IProofRpcModule.cs @@ -3,8 +3,7 @@ using Nethermind.Blockchain.Find; using Nethermind.Core.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; namespace Nethermind.JsonRpc.Modules.Proof { @@ -21,7 +20,7 @@ public interface IProofRpcModule : IRpcModule Description = "This function returns the same result as `eth_getTransactionReceipt` and also a tx proof, receipt proof and serialized block headers.", IsSharable = false, ExampleResponse = "\"transaction\":{\"hash\":\"0xb62594c08de66c683fbffe44792a1ccc0f9b80e43071048ed03c18a71fd3c19a\",\"nonce\":\"0x630\",\"blockHash\":\"0x42d72739c2b2659916d7b42a49661fdec317e780af1395c2c15aa89b4c42e220\",\"blockNumber\":\"0x88f194\",\"transactionIndex\":\"0x24\",\"from\":\"0x78ca86e8133ef9368b4537879cf2f38fddbb636b\",\"to\":\"0x1dfd95eb75a7486945d366a0bc0b937f0aaa526f\",\"value\":\"0x0\",\"gasPrice\":\"0x3b9aca00\",\"gas\":\"0xc9e2\",\"data\":\"0xa9059cbb000000000000000000000000e3ac1cc1453e70f80ff58f3bb56b0532238ae24a00000000000000000000000000000000000000000000003635c9adc5dea00000\",\"input\":\"0xa9059cbb000000000000000000000000e3ac1cc1453e70f80ff58f3bb56b0532238ae24a00000000000000000000000000000000000000000000003635c9adc5dea00000\",\"type\":\"0x0\",\"v\":\"0x2b\",\"s\":\"0x33a9425e84bf310d372a9f531b237baebccfdd2b426e817cc9553355a9165342\",\"r\":\"0xe14a066de4787a4c0192f5a2285fd835a85baa3a4f63b1e8a2d8d7f6e04425ca\"},\"txProof\":[\"0xf891a0311d3b27b7612bf40c2c5d623c62c2afe30a47f486700074e4c4d7cf603c90c8a0cd64d350a95e9286a580a75ae11fe58801992f9ac65ace8a0b853d16f87b09b0a0ae9d609ff06d19bb911d7ad05cfdd6c80a9f1fddccbdb76a78594536122345ce8080808080a09773b23452983c0ed65aebb64522af322967c62be34414e16b32b7e4bdaecdb68080808080808080\",\"0xf8b1a0715f91aae7675a1c8469685d18bc94241d275c82a3b52df6c4fab064fcba3017a0e77ac7615c08eaafccc876956f3dad1892f08c1f1128e2cdf9064664381a540fa06f2d934e5f7995657144ad66b8b5cdce6b6c141422f95d44eb91ca6765d4f819a0b265c005bad056db029945b3d68a631b624a77703733fa9b2042c0f211f8ef4ea0bb97f719cc5f6082fe5bab8588dc564a843a6b40c5494982ded868f19eef07b6808080808080808080808080\",\"0xf8af20b8acf8aa820630843b9aca0082c9e2941dfd95eb75a7486945d366a0bc0b937f0aaa526f80b844a9059cbb000000000000000000000000e3ac1cc1453e70f80ff58f3bb56b0532238ae24a00000000000000000000000000000000000000000000003635c9adc5dea000002ba0e14a066de4787a4c0192f5a2285fd835a85baa3a4f63b1e8a2d8d7f6e04425caa033a9425e84bf310d372a9f531b237baebccfdd2b426e817cc9553355a9165342\"]")] - ResultWrapper proof_getTransactionByHash([JsonRpcParameter(ExampleValue = "\"[\"0xb62594c08de66c683fbffe44792a1ccc0f9b80e43071048ed03c18a71fd3c19a\", \"false\"]")] Hash256 txHash, bool includeHeader); + ResultWrapper proof_getTransactionByHash([JsonRpcParameter(ExampleValue = "\"[\"0xb62594c08de66c683fbffe44792a1ccc0f9b80e43071048ed03c18a71fd3c19a\", \"false\"]")] Hash256 txHash, bool includeHeader); [JsonRpcMethod(IsImplemented = true, Description = "This function should return the same result as `eth_call` and also proofs of all used accounts and their storages and serialized block headers.", diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofRpcModule.cs index e79f6fdd106..5818849ea3a 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofRpcModule.cs @@ -14,7 +14,7 @@ using Nethermind.Evm; using Nethermind.Evm.Tracing; using Nethermind.Evm.Tracing.Proofs; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; using Nethermind.Logging; using Nethermind.Serialization.Rlp; @@ -102,18 +102,18 @@ public ResultWrapper proof_call(TransactionForRpc tx, Block return ResultWrapper.Success(callResultWithProof); } - public ResultWrapper proof_getTransactionByHash(Hash256 txHash, bool includeHeader) + public ResultWrapper proof_getTransactionByHash(Hash256 txHash, bool includeHeader) { Hash256 blockHash = _receiptFinder.FindBlockHash(txHash); if (blockHash is null) { - return ResultWrapper.Fail($"{txHash} receipt (transaction) could not be found", ErrorCodes.ResourceNotFound); + return ResultWrapper.Fail($"{txHash} receipt (transaction) could not be found", ErrorCodes.ResourceNotFound); } SearchResult searchResult = _blockFinder.SearchForBlock(new BlockParameter(blockHash)); if (searchResult.IsError) { - return ResultWrapper.Fail(searchResult); + return ResultWrapper.Fail(searchResult); } Block block = searchResult.Object; @@ -121,15 +121,15 @@ public ResultWrapper proof_getTransactionByHash(Hash256 tx Transaction[] txs = block.Transactions; Transaction transaction = txs[receipt.Index]; - TransactionWithProof txWithProof = new(); - txWithProof.Transaction = new TransactionForRpc(block.Hash, block.Number, receipt.Index, transaction, block.BaseFeePerGas); + TransactionForRpcWithProof txWithProof = new(); + txWithProof.Transaction = TransactionForRpc.FromTransaction(transaction, block.Hash, block.Number, receipt.Index, block.BaseFeePerGas, _specProvider.ChainId); txWithProof.TxProof = BuildTxProofs(txs, _specProvider.GetSpec(block.Header), receipt.Index); if (includeHeader) { txWithProof.BlockHeader = _headerDecoder.Encode(block.Header).Bytes; } - return ResultWrapper.Success(txWithProof); + return ResultWrapper.Success(txWithProof); } public ResultWrapper proof_getTransactionReceipt(Hash256 txHash, bool includeHeader) diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionForRpcWithProof.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionForRpcWithProof.cs new file mode 100644 index 00000000000..3d3745fafd7 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionForRpcWithProof.cs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Facade.Eth.RpcTransaction; + +namespace Nethermind.JsonRpc.Modules.Proof; + +public class TransactionForRpcWithProof +{ + public TransactionForRpc Transaction { get; set; } + + public byte[][] TxProof { get; set; } + + public byte[] BlockHeader { get; set; } +} diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionWithProof.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionWithProof.cs deleted file mode 100644 index 3069c957849..00000000000 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionWithProof.cs +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; - -namespace Nethermind.JsonRpc.Modules.Proof -{ - public class TransactionWithProof - { - public TransactionForRpc Transaction { get; set; } - - public byte[][] TxProof { get; set; } - - public byte[] BlockHeader { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/NewPendingTransactionsSubscription.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/NewPendingTransactionsSubscription.cs index 7ec4b75d77d..ebf4ec7267a 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/NewPendingTransactionsSubscription.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/NewPendingTransactionsSubscription.cs @@ -2,9 +2,8 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Threading.Tasks; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Core.Specs; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.Logging; using Nethermind.TxPool; @@ -14,16 +13,19 @@ namespace Nethermind.JsonRpc.Modules.Subscribe public class NewPendingTransactionsSubscription : Subscription { private readonly ITxPool _txPool; + private readonly ISpecProvider _specProvider; private readonly bool _includeTransactions; public NewPendingTransactionsSubscription( IJsonRpcDuplexClient jsonRpcDuplexClient, ITxPool? txPool, + ISpecProvider? specProvider, ILogManager? logManager, TransactionsOption? options = null) : base(jsonRpcDuplexClient) { _txPool = txPool ?? throw new ArgumentNullException(nameof(txPool)); + _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _includeTransactions = options?.IncludeTransactions ?? false; @@ -35,7 +37,9 @@ private void OnNewPending(object? sender, TxEventArgs e) { ScheduleAction(async () => { - using JsonRpcResult result = CreateSubscriptionMessage(_includeTransactions ? new TransactionForRpc(e.Transaction) : e.Transaction.Hash); + using JsonRpcResult result = CreateSubscriptionMessage(_includeTransactions + ? TransactionForRpc.FromTransaction(e.Transaction, chainId: _specProvider.ChainId) + : e.Transaction.Hash!); await JsonRpcDuplexClient.SendJsonRpcResult(result); if (_logger.IsTrace) _logger.Trace($"NewPendingTransactions subscription {Id} printed hash of NewPendingTransaction."); }); diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactory.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactory.cs index 29779ac837e..00abc7a7e00 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactory.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactory.cs @@ -61,7 +61,7 @@ public SubscriptionFactory(ILogManager? logManager, new LogsSubscription(jsonRpcDuplexClient, receiptCanonicalityMonitor, filterStore, blockTree, logManager, filter)), [SubscriptionType.NewPendingTransactions] = CreateSubscriptionType((jsonRpcDuplexClient, args) => - new NewPendingTransactionsSubscription(jsonRpcDuplexClient, txPool, logManager, args)), + new NewPendingTransactionsSubscription(jsonRpcDuplexClient, txPool, specProvider, logManager, args)), [SubscriptionType.DroppedPendingTransactions] = CreateSubscriptionType(jsonRpcDuplexClient => new DroppedPendingTransactionsSubscription(jsonRpcDuplexClient, txPool, logManager)), diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs index dae0f773487..80318f35c2b 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Nethermind.Blockchain.Find; using Nethermind.Core.Crypto; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; namespace Nethermind.JsonRpc.Modules.Trace diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs index f7747d6436f..622ba612700 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs @@ -11,17 +11,14 @@ using Nethermind.Consensus.Tracing; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Specs; using Nethermind.Evm.Tracing; using Nethermind.Evm.Tracing.ParityStyle; using Nethermind.Facade; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; using Nethermind.JsonRpc.Data; -using Nethermind.Logging; using Nethermind.Serialization.Rlp; using Nethermind.State; -using Nethermind.Trie; namespace Nethermind.JsonRpc.Modules.Trace { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs index 2040cc403e8..a4726b89477 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs @@ -4,18 +4,17 @@ using System.Collections.Generic; using System.Linq; using Nethermind.Core; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.TxPool; namespace Nethermind.JsonRpc.Modules.TxPool { public class TxPoolContent { - public TxPoolContent(TxPoolInfo info) + public TxPoolContent(TxPoolInfo info, ulong chainId) { - Pending = info.Pending.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => new TransactionForRpc(null, null, null, v.Value))); - Queued = info.Queued.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => new TransactionForRpc(null, null, null, v.Value))); + Pending = info.Pending.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => TransactionForRpc.FromTransaction(v.Value, chainId: chainId))); + Queued = info.Queued.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => TransactionForRpc.FromTransaction(v.Value, chainId: chainId))); } public Dictionary> Pending { get; set; } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs index da0a5893561..02c90681997 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using Nethermind.Logging; +using Nethermind.Core.Specs; using Nethermind.TxPool; namespace Nethermind.JsonRpc.Modules.TxPool @@ -10,10 +10,12 @@ namespace Nethermind.JsonRpc.Modules.TxPool public class TxPoolRpcModule : ITxPoolRpcModule { private readonly ITxPoolInfoProvider _txPoolInfoProvider; + private readonly ISpecProvider _specProvider; - public TxPoolRpcModule(ITxPoolInfoProvider txPoolInfoProvider, ILogManager logManager) + public TxPoolRpcModule(ITxPoolInfoProvider txPoolInfoProvider, ISpecProvider specProvider) { _txPoolInfoProvider = txPoolInfoProvider ?? throw new ArgumentNullException(nameof(txPoolInfoProvider)); + _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); } public ResultWrapper txpool_status() @@ -27,7 +29,8 @@ public ResultWrapper txpool_status() public ResultWrapper txpool_content() { var poolInfo = _txPoolInfoProvider.GetInfo(); - return ResultWrapper.Success(new TxPoolContent(poolInfo)); + var chainId = _specProvider.ChainId; + return ResultWrapper.Success(new TxPoolContent(poolInfo, chainId)); } public ResultWrapper txpool_inspect() diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs index 6f3bf5874ef..fc4e6d663ca 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs @@ -244,8 +244,7 @@ public async Task NewPayloadV3_should_decline_null_blobversionedhashes() string executionPayloadString = serializer.Serialize(executionPayload); - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - executionPayloadString, null!); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), executionPayloadString, null!); JsonRpcErrorResponse? response = (await jsonRpcService.SendRequestAsync(request, context)) as JsonRpcErrorResponse; Assert.That(response?.Error, Is.Not.Null); Assert.That(response!.Error!.Code, Is.EqualTo(ErrorCodes.InvalidParams)); @@ -260,8 +259,7 @@ public async Task NewPayloadV3_invalidblockhash() string requestStr = """ {"parentHash":"0xd6194b42ad579c195e9aaaf04692619f4de9c5fbdd6b58baaabe93384e834d25","feeRecipient":"0x0000000000000000000000000000000000000000","stateRoot":"0xfe1fa6bb862e4a5efd9ee8967b356d4f7b6205a437eeac8b0e625db3cb662018","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prevRandao":"0xfaebfae9aef88ac8eba03decf329c8155991e07cb1a9dc6ac69e420550a45037","blockNumber":"0x1","gasLimit":"0x2fefd8","gasUsed":"0x0","timestamp":"0x1235","extraData":"0x4e65746865726d696e64","baseFeePerGas":"0x342770c0","blockHash":"0x0718890af079939b4aae6ac0ecaa94c633ad73e69e787f526f0d558043e8e2f1","transactions":[],"withdrawals":[{"index":"0x1","validatorIndex":"0x0","address":"0x0000000000000000000000000000000000000000","amount":"0x64"},{"index":"0x2","validatorIndex":"0x1","address":"0x0100000000000000000000000000000000000000","amount":"0x64"},{"index":"0x3","validatorIndex":"0x2","address":"0x0200000000000000000000000000000000000000","amount":"0x64"},{"index":"0x4","validatorIndex":"0x3","address":"0x0300000000000000000000000000000000000000","amount":"0x64"},{"index":"0x5","validatorIndex":"0x4","address":"0x0400000000000000000000000000000000000000","amount":"0x64"},{"index":"0x6","validatorIndex":"0x5","address":"0x0500000000000000000000000000000000000000","amount":"0x64"},{"index":"0x7","validatorIndex":"0x6","address":"0x0600000000000000000000000000000000000000","amount":"0x64"},{"index":"0x8","validatorIndex":"0x7","address":"0x0700000000000000000000000000000000000000","amount":"0x64"},{"index":"0x9","validatorIndex":"0x8","address":"0x0800000000000000000000000000000000000000","amount":"0x64"},{"index":"0xa","validatorIndex":"0x9","address":"0x0900000000000000000000000000000000000000","amount":"0x64"}],"excessBlobGas":"0x0"} """; - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - requestStr, "[]", "0x169630f535b4a41330164c6e5c92b1224c0c407f582d407d0ac3d206cd32fd52"); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), requestStr, "[]", "0x169630f535b4a41330164c6e5c92b1224c0c407f582d407d0ac3d206cd32fd52"); var rpcResponse = await jsonRpcService.SendRequestAsync(request, context); JsonRpcErrorResponse? response = (rpcResponse) as JsonRpcErrorResponse; @@ -296,8 +294,7 @@ public async Task NewPayloadV3_should_decline_empty_fields() { JsonObject executionPayloadAsJObject = serializer.Deserialize(executionPayloadString); - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - serializer.Serialize(executionPayloadAsJObject), blobsString, parentBeaconBlockRootString); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), serializer.Serialize(executionPayloadAsJObject), blobsString, parentBeaconBlockRootString); JsonRpcResponse response = await jsonRpcService.SendRequestAsync(request, context); Assert.That(response is JsonRpcSuccessResponse); } @@ -310,8 +307,7 @@ public async Task NewPayloadV3_should_decline_empty_fields() JsonObject executionPayloadAsJObject = serializer.Deserialize(executionPayloadString); executionPayloadAsJObject[prop] = null; - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - serializer.Serialize(executionPayloadAsJObject), blobsString); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), serializer.Serialize(executionPayloadAsJObject), blobsString); JsonRpcErrorResponse? response = (await jsonRpcService.SendRequestAsync(request, context)) as JsonRpcErrorResponse; Assert.That(response?.Error, Is.Not.Null); Assert.That(response!.Error!.Code, Is.EqualTo(ErrorCodes.InvalidParams)); @@ -322,8 +318,7 @@ public async Task NewPayloadV3_should_decline_empty_fields() JsonObject executionPayloadAsJObject = serializer.Deserialize(executionPayloadString); executionPayloadAsJObject.Remove(prop); - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - serializer.Serialize(executionPayloadAsJObject), blobsString); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), serializer.Serialize(executionPayloadAsJObject), blobsString); JsonRpcErrorResponse? response = (await jsonRpcService.SendRequestAsync(request, context)) as JsonRpcErrorResponse; Assert.That(response?.Error, Is.Not.Null); Assert.That(response!.Error!.Code, Is.EqualTo(ErrorCodes.InvalidParams)); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs index aac51366839..903ff9dad59 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs @@ -14,7 +14,7 @@ namespace Nethermind.Merge.Plugin { - public sealed class MergeHeaderValidator : HeaderValidator + public class MergeHeaderValidator : HeaderValidator { // https://eips.ethereum.org/EIPS/eip-3675#constants private const int MaxExtraDataBytes = 32; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs index 9d387b5d200..81b83425ace 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs @@ -6,6 +6,10 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Autofac; +using Autofac.Core; +using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Nethermind.Api; using Nethermind.Api.Extensions; using Nethermind.Blockchain; @@ -30,6 +34,8 @@ using Nethermind.Merge.Plugin.Handlers; using Nethermind.Merge.Plugin.InvalidChainTracker; using Nethermind.Merge.Plugin.Synchronization; +using Nethermind.Synchronization; +using Nethermind.Synchronization.Blocks; using Nethermind.Synchronization.ParallelSync; using Nethermind.TxPool; @@ -436,43 +442,24 @@ public Task InitSynchronization() _api.Pivot = _beaconPivot; - MergeBlockDownloaderFactory blockDownloaderFactory = new MergeBlockDownloaderFactory( - _poSSwitcher, - _beaconPivot, - _api.SpecProvider, - _api.BlockValidator!, - _api.SealValidator!, - _syncConfig, - _api.BetterPeerStrategy!, - new FullStateFinder(_api.BlockTree, _api.StateReader), - _api.LogManager); + ContainerBuilder builder = new ContainerBuilder(); - MergeSynchronizer synchronizer = new MergeSynchronizer( - _api.DbProvider, - _api.NodeStorageFactory.WrapKeyValueStore(_api.DbProvider.StateDb), - _api.SpecProvider!, - _api.BlockTree!, - _api.ReceiptStorage!, - _api.SyncPeerPool, - _api.NodeStatsManager!, - _syncConfig, - blockDownloaderFactory, - _beaconPivot, - _poSSwitcher, - _mergeConfig, - _invalidChainTracker, - _api.ProcessExit!, - _api.BetterPeerStrategy, - _api.ChainSpec, - _beaconSync, - _api.StateReader, - _api.LogManager - ); - _api.Synchronizer = synchronizer; + _api.ConfigureContainerBuilderFromApiWithNetwork(builder) + .AddSingleton(_beaconSync) + .AddSingleton(_beaconPivot) + .AddSingleton(_mergeConfig) + .AddSingleton(_invalidChainTracker); + + builder.RegisterModule(new SynchronizerModule(_syncConfig)); + builder.RegisterModule(new MergeSynchronizerModule()); + + IContainer container = builder.Build(); + _api.ApiWithNetworkServiceContainer = container; + _api.DisposeStack.Append(container); PivotUpdator pivotUpdator = new( _api.BlockTree, - synchronizer.SyncModeSelector, + _api.SyncModeSelector, _api.SyncPeerPool, _syncConfig, _blockCacheService, diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloader.cs b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloader.cs index f0768e39168..c31a749ff3f 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloader.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloader.cs @@ -218,9 +218,9 @@ bool HasMoreToSync(out BlockHeader[]? headers, out int headersToRequest) } // can move this to block tree now? - if (!_blockValidator.ValidateSuggestedBlock(currentBlock, out _)) + if (!_blockValidator.ValidateSuggestedBlock(currentBlock, out string? errorMessage)) { - string message = InvalidBlockHelper.GetMessage(currentBlock, "invalid block sent by peer") + + string message = InvalidBlockHelper.GetMessage(currentBlock, $"invalid block sent by peer. {errorMessage}") + $" PeerInfo {bestPeer}"; if (_logger.IsWarn) _logger.Warn(message); throw new EthSyncException(message); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloaderFactory.cs b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloaderFactory.cs deleted file mode 100644 index 8c3b99555bf..00000000000 --- a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloaderFactory.cs +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using Nethermind.Blockchain; -using Nethermind.Blockchain.Receipts; -using Nethermind.Blockchain.Synchronization; -using Nethermind.Consensus; -using Nethermind.Consensus.Validators; -using Nethermind.Core.Specs; -using Nethermind.Logging; -using Nethermind.Synchronization; -using Nethermind.Synchronization.Blocks; -using Nethermind.Synchronization.ParallelSync; -using Nethermind.Synchronization.Peers; -using Nethermind.Synchronization.Reporting; - - -namespace Nethermind.Merge.Plugin.Synchronization -{ - public class MergeBlockDownloaderFactory : IBlockDownloaderFactory - { - private readonly IPoSSwitcher _poSSwitcher; - private readonly IBeaconPivot _beaconPivot; - private readonly ISpecProvider _specProvider; - private readonly IBlockValidator _blockValidator; - private readonly ISealValidator _sealValidator; - private readonly IBetterPeerStrategy _betterPeerStrategy; - private readonly ILogManager _logManager; - private readonly IFullStateFinder _fullStateFinder; - private readonly ISyncConfig _syncConfig; - - public MergeBlockDownloaderFactory( - IPoSSwitcher poSSwitcher, - IBeaconPivot beaconPivot, - ISpecProvider specProvider, - IBlockValidator blockValidator, - ISealValidator sealValidator, - ISyncConfig syncConfig, - IBetterPeerStrategy betterPeerStrategy, - IFullStateFinder fullStateFinder, - ILogManager logManager) - { - _poSSwitcher = poSSwitcher ?? throw new ArgumentNullException(nameof(poSSwitcher)); - _beaconPivot = beaconPivot ?? throw new ArgumentNullException(nameof(beaconPivot)); - _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); - _blockValidator = blockValidator ?? throw new ArgumentNullException(nameof(blockValidator)); - _sealValidator = sealValidator ?? throw new ArgumentNullException(nameof(sealValidator)); - _betterPeerStrategy = betterPeerStrategy ?? throw new ArgumentNullException(nameof(betterPeerStrategy)); - _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); - _fullStateFinder = fullStateFinder ?? throw new ArgumentNullException(nameof(fullStateFinder)); ; - _syncConfig = syncConfig ?? throw new ArgumentNullException(nameof(syncConfig)); ; - } - - public BlockDownloader Create(ISyncFeed syncFeed, IBlockTree blockTree, IReceiptStorage receiptStorage, - ISyncPeerPool syncPeerPool, ISyncReport syncReport) - { - ChainLevelHelper chainLevelHelper = new ChainLevelHelper(blockTree, _beaconPivot, _syncConfig, _logManager); - return new MergeBlockDownloader( - _poSSwitcher, - _beaconPivot, - syncFeed, - syncPeerPool, - blockTree, - _blockValidator, - _sealValidator, - syncReport, - receiptStorage, - _specProvider, - _betterPeerStrategy, - chainLevelHelper, - _fullStateFinder, - _logManager); - } - - public IPeerAllocationStrategyFactory CreateAllocationStrategyFactory() - { - return new MergeBlocksSyncPeerAllocationStrategyFactory(_poSSwitcher, _beaconPivot, _logManager); - } - } -} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeSynchronizer.cs b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeSynchronizer.cs index 38e65be39e7..ac3ad98313f 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeSynchronizer.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeSynchronizer.cs @@ -1,113 +1,58 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using Nethermind.Blockchain; -using Nethermind.Blockchain.Receipts; +using System; +using System.Threading; +using System.Threading.Tasks; +using Autofac; +using Autofac.Features.AttributeFilters; using Nethermind.Blockchain.Synchronization; -using Nethermind.Config; -using Nethermind.Consensus; -using Nethermind.Core.Specs; -using Nethermind.Db; +using Nethermind.Core; using Nethermind.Logging; -using Nethermind.Merge.Plugin.InvalidChainTracker; -using Nethermind.Specs.ChainSpecStyle; -using Nethermind.State; -using Nethermind.Stats; using Nethermind.Synchronization; using Nethermind.Synchronization.Blocks; using Nethermind.Synchronization.FastBlocks; using Nethermind.Synchronization.ParallelSync; -using Nethermind.Synchronization.Peers; -using Nethermind.Trie; -using Nethermind.Trie.Pruning; namespace Nethermind.Merge.Plugin.Synchronization; -public class MergeSynchronizer : Synchronizer +public class MergeSynchronizer( + [KeyFilter(nameof(BeaconHeadersSyncFeed))] SyncFeedComponent beaconHeaderComponent, + ISyncConfig syncConfig, + Synchronizer baseSynchronizer, + ILogManager logManager) + : ISynchronizer { - private readonly IPoSSwitcher _poSSwitcher; - private readonly IMergeConfig _mergeConfig; - private readonly IInvalidChainTracker _invalidChainTracker; - private BeaconHeadersSyncFeed _beaconHeadersFeed = null!; - private readonly IBeaconSyncStrategy _beaconSync; + private readonly CancellationTokenSource? _syncCancellation = new(); + private readonly ILogger _logger = logManager.GetClassLogger(); - public override ISyncModeSelector SyncModeSelector => _syncModeSelector ??= new MultiSyncModeSelector( - SyncProgressResolver, - _syncPeerPool, - _syncConfig, - _beaconSync, - _betterPeerStrategy!, - _logManager); - - public MergeSynchronizer( - IDbProvider dbProvider, - INodeStorage nodeStorage, - ISpecProvider specProvider, - IBlockTree blockTree, - IReceiptStorage receiptStorage, - ISyncPeerPool peerPool, - INodeStatsManager nodeStatsManager, - ISyncConfig syncConfig, - IBlockDownloaderFactory blockDownloaderFactory, - IPivot pivot, - IPoSSwitcher poSSwitcher, - IMergeConfig mergeConfig, - IInvalidChainTracker invalidChainTracker, - IProcessExitSource exitSource, - IBetterPeerStrategy betterPeerStrategy, - ChainSpec chainSpec, - IBeaconSyncStrategy beaconSync, - IStateReader stateReader, - ILogManager logManager) - : base( - dbProvider, - nodeStorage, - specProvider, - blockTree, - receiptStorage, - peerPool, - nodeStatsManager, - syncConfig, - blockDownloaderFactory, - pivot, - exitSource, - betterPeerStrategy, - chainSpec, - stateReader, - logManager) + public event EventHandler? SyncEvent { - _invalidChainTracker = invalidChainTracker; - _poSSwitcher = poSSwitcher; - _mergeConfig = mergeConfig; - _beaconSync = beaconSync; + add => baseSynchronizer.SyncEvent += value; + remove => baseSynchronizer.SyncEvent -= value; } - public override void Start() + public void Start() { - if (!_syncConfig.SynchronizationEnabled) + if (!syncConfig.SynchronizationEnabled) { return; } - base.Start(); + baseSynchronizer.Start(); StartBeaconHeadersComponents(); WireMultiSyncModeSelector(); } - private void StartBeaconHeadersComponents() + public Task StopAsync() { - FastBlocksPeerAllocationStrategyFactory fastFactory = new(); - _beaconHeadersFeed = - new(_poSSwitcher, _blockTree, _syncPeerPool, _syncConfig, _syncReport, _pivot, _mergeConfig, _invalidChainTracker, _logManager); - BeaconHeadersSyncDownloader beaconHeadersDownloader = new(_logManager); - - SyncDispatcher dispatcher = CreateDispatcher( - _beaconHeadersFeed!, - beaconHeadersDownloader, - fastFactory - ); + _syncCancellation?.Cancel(); + return baseSynchronizer.StopAsync(); + } - dispatcher.Start(_syncCancellation!.Token).ContinueWith(t => + private void StartBeaconHeadersComponents() + { + beaconHeaderComponent.Dispatcher.Start(_syncCancellation!.Token).ContinueWith(t => { if (t.IsFaulted) { @@ -122,6 +67,36 @@ private void StartBeaconHeadersComponents() private void WireMultiSyncModeSelector() { - WireFeedWithModeSelector(_beaconHeadersFeed); + baseSynchronizer.WireFeedWithModeSelector(beaconHeaderComponent.Feed); + } + + public void Dispose() + { + baseSynchronizer.Dispose(); + } +} + +public class MergeSynchronizerModule : Module +{ + protected override void Load(ContainerBuilder builder) + { + builder + .RegisterType() + .As() + .As>() + .InstancePerLifetimeScope(); + + builder + .AddSingleton() + .AddSingleton() + .AddScoped, MergeBlocksSyncPeerAllocationStrategyFactory>() + + .RegisterNamedComponentInItsOwnLifetime>(nameof(BeaconHeadersSyncFeed), ConfigureBeaconHeader); + } + + private void ConfigureBeaconHeader(ContainerBuilder scopeConfig) + { + scopeConfig.AddScoped, BeaconHeadersSyncFeed>() + .AddScoped, BeaconHeadersSyncDownloader>(); } } diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs index c037fe6b16e..714a3999dcc 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs @@ -5,14 +5,13 @@ using DotNetty.Buffers; using Nethermind.Core; using Nethermind.Core.Buffers; -using Nethermind.Core.ConsensusRequests; using Nethermind.Serialization.Rlp; namespace Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages { public class BlockBodiesMessageSerializer : IZeroInnerMessageSerializer { - private readonly BlockBodyDecoder _blockBodyDecoder = new(); + private readonly BlockBodyDecoder _blockBodyDecoder = BlockBodyDecoder.Instance; public void Serialize(IByteBuffer byteBuffer, BlockBodiesMessage message) { @@ -53,94 +52,5 @@ public BlockBodiesMessage Deserialize(IByteBuffer byteBuffer) return new() { Bodies = new(bodies, memoryOwner) }; } - - private class BlockBodyDecoder : IRlpValueDecoder - { - private readonly TxDecoder _txDecoder = TxDecoder.Instance; - private readonly HeaderDecoder _headerDecoder = new(); - private readonly WithdrawalDecoder _withdrawalDecoderDecoder = new(); - private readonly ConsensusRequestDecoder _requestsDecoder = ConsensusRequestDecoder.Instance; - - public int GetLength(BlockBody item, RlpBehaviors rlpBehaviors) - { - return Rlp.LengthOfSequence(GetBodyLength(item)); - } - - public int GetBodyLength(BlockBody b) => - Rlp.LengthOfSequence(GetTxLength(b.Transactions)) + - Rlp.LengthOfSequence(GetUnclesLength(b.Uncles)) - + (b.Withdrawals is not null ? Rlp.LengthOfSequence(GetWithdrawalsLength(b.Withdrawals)) : 0) - + (b.Requests is not null ? Rlp.LengthOfSequence(GetRequestsLength(b.Requests)) : 0); - - private int GetTxLength(Transaction[] transactions) => transactions.Sum(t => _txDecoder.GetLength(t, RlpBehaviors.None)); - - private int GetUnclesLength(BlockHeader[] headers) => headers.Sum(t => _headerDecoder.GetLength(t, RlpBehaviors.None)); - - private int GetWithdrawalsLength(Withdrawal[] withdrawals) => withdrawals.Sum(t => _withdrawalDecoderDecoder.GetLength(t, RlpBehaviors.None)); - - private int GetRequestsLength(ConsensusRequest[] requests) => requests.Sum(t => _requestsDecoder.GetLength(t, RlpBehaviors.None)); - - public BlockBody? Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - { - int sequenceLength = ctx.ReadSequenceLength(); - int startingPosition = ctx.Position; - if (sequenceLength == 0) - { - return null; - } - - // quite significant allocations (>0.5%) here based on a sample 3M blocks sync - // (just on these delegates) - Transaction[] transactions = ctx.DecodeArray(_txDecoder); - BlockHeader[] uncles = ctx.DecodeArray(_headerDecoder); - Withdrawal[]? withdrawals = null; - ConsensusRequest[]? requests = null; - if (ctx.PeekNumberOfItemsRemaining(startingPosition + sequenceLength, 1) > 0) - { - withdrawals = ctx.DecodeArray(_withdrawalDecoderDecoder); - } - - if (ctx.PeekNumberOfItemsRemaining(startingPosition + sequenceLength, 1) > 0) - { - requests = ctx.DecodeArray(_requestsDecoder); - } - - return new BlockBody(transactions, uncles, withdrawals, requests); - } - - public void Serialize(RlpStream stream, BlockBody body) - { - stream.StartSequence(GetBodyLength(body)); - stream.StartSequence(GetTxLength(body.Transactions)); - foreach (Transaction? txn in body.Transactions) - { - stream.Encode(txn); - } - - stream.StartSequence(GetUnclesLength(body.Uncles)); - foreach (BlockHeader? uncle in body.Uncles) - { - stream.Encode(uncle); - } - - if (body.Withdrawals is not null) - { - stream.StartSequence(GetWithdrawalsLength(body.Withdrawals)); - foreach (Withdrawal? withdrawal in body.Withdrawals) - { - stream.Encode(withdrawal); - } - } - - if (body.Requests is not null) - { - stream.StartSequence(GetRequestsLength(body.Requests)); - foreach (ConsensusRequest? request in body.Requests) - { - stream.Encode(request); - } - } - } - } } } diff --git a/src/Nethermind/Nethermind.Optimism.Test/Rpc/OptimismTransactionForRpcTests.cs b/src/Nethermind/Nethermind.Optimism.Test/Rpc/OptimismTransactionForRpcTests.cs new file mode 100644 index 00000000000..464d0451c5c --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/Rpc/OptimismTransactionForRpcTests.cs @@ -0,0 +1,109 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json; +using NUnit.Framework; +using Nethermind.Serialization.Json; +using Nethermind.Optimism.Rpc; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Core; +using FluentAssertions; +using Nethermind.Core.Test.Builders; +using Nethermind.Core.Crypto; +using System; +using System.Collections.Generic; + +namespace Nethermind.Optimism.Test.Rpc; + +public class OptimismTransactionForRpcTests +{ + private readonly IJsonSerializer serializer = new EthereumJsonSerializer(); + + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction.WithType(TxType.DepositTx); + public static readonly Transaction[] Transactions = [ + Build.TestObject, + Build + .With(s => s.IsOPSystemTransaction = true) + .With(s => s.Mint = 1234) + .TestObject, + Build + .WithGasLimit(0x1234) + .WithValue(0x1) + .WithData([0x61, 0x62, 0x63, 0x64, 0x65, 0x66]) + .WithSourceHash(Hash256.Zero) + .WithSenderAddress(Address.FromNumber(1)) + .WithHash(new Hash256("0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9")) + .TestObject + ]; + + [SetUp] + public void SetUp() + { + TransactionForRpc.RegisterTransactionType(); + } + + [TestCaseSource(nameof(Transactions))] + public void Always_satisfies_schema(Transaction transaction) + { + TransactionForRpc rpcTransaction = TransactionForRpc.FromTransaction(transaction); + string serialized = serializer.Serialize(rpcTransaction); + using var jsonDocument = JsonDocument.Parse(serialized); + JsonElement json = jsonDocument.RootElement; + ValidateSchema(json); + } + + private static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x7[eE]$"); + json.GetProperty("sourceHash").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{64}$"); + json.GetProperty("from").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + var hasMint = json.TryGetProperty("mint", out var mint); + if (hasMint) + { + mint.GetString()?.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var hasIsSystemTx = json.TryGetProperty("isSystemTx", out var isSystemTx); + if (hasIsSystemTx) + { + isSystemTx.GetBoolean(); + } + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } + + private static readonly IEnumerable<(string, string)> MalformedJsonTransactions = [ + (nameof(OptimismTransactionForRpc.Gas), """{"type":"0x7e","nonce":null,"gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.Value), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.Input), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.From), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.SourceHash), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + ]; + + [TestCaseSource(nameof(MalformedJsonTransactions))] + public void Rejects_malformed_transaction_missing_field((string missingField, string json) testCase) + { + var rpcTx = serializer.Deserialize(testCase.json); + rpcTx.Should().NotBeNull(); + + var toTransaction = rpcTx.ToTransaction; + toTransaction.Should().Throw().WithParameterName(testCase.missingField); + } + + private static readonly IEnumerable<(string, string)> ValidJsonTransactions = [ + (nameof(OptimismTransactionForRpc.Mint), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.IsSystemTx), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + ]; + + [TestCaseSource(nameof(ValidJsonTransactions))] + public void Accepts_valid_transaction_missing_field((string missingField, string json) testCase) + { + var rpcTx = serializer.Deserialize(testCase.json); + rpcTx.Should().NotBeNull(); + + var toTransaction = rpcTx.ToTransaction; + toTransaction.Should().NotThrow(); + } +} diff --git a/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs b/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs index c481446873e..f7ce6c9b833 100644 --- a/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs +++ b/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs @@ -11,10 +11,12 @@ using Nethermind.Consensus.Producers; using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; +using Nethermind.Core; using Nethermind.Evm; using Nethermind.Evm.TransactionProcessing; using Nethermind.Init.Steps; using Nethermind.Merge.Plugin.InvalidChainTracker; +using Nethermind.Optimism.Rpc; using Nethermind.TxPool; namespace Nethermind.Optimism; @@ -25,6 +27,8 @@ public class InitializeBlockchainOptimism(OptimismNethermindApi api) : Initializ protected override async Task InitBlockchain() { + api.RegisterTxType(new OptimismTxDecoder(), Always.Valid); + api.SpecHelper = new(api.ChainSpec.Optimism); api.L1CostHelper = new(api.SpecHelper, api.ChainSpec.Optimism.L1BlockAddress); @@ -52,8 +56,14 @@ protected override ITransactionProcessor CreateTransactionProcessor(CodeInfoRepo protected override IHeaderValidator CreateHeaderValidator() { if (api.InvalidChainTracker is null) throw new StepDependencyException(nameof(api.InvalidChainTracker)); + if (api.BlockTree is null) throw new StepDependencyException(nameof(api.BlockTree)); + if (api.SealValidator is null) throw new StepDependencyException(nameof(api.SealValidator)); + if (api.SpecProvider is null) throw new StepDependencyException(nameof(api.SpecProvider)); + if (api.SpecHelper is null) throw new StepDependencyException(nameof(api.SpecHelper)); + if (api.LogManager is null) throw new StepDependencyException(nameof(api.LogManager)); OptimismHeaderValidator opHeaderValidator = new( + api.PoSSwitcher, api.BlockTree, api.SealValidator, api.SpecProvider, diff --git a/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs b/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs index 32db335b572..0851b0a29fd 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs @@ -1,21 +1,43 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Nethermind.Blockchain; using Nethermind.Consensus; +using Nethermind.Consensus.Messages; using Nethermind.Consensus.Validators; using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Int256; using Nethermind.Logging; +using Nethermind.Merge.Plugin; namespace Nethermind.Optimism; -public class OptimismHeaderValidator( +public class PreBedrockHeaderValidator( IBlockTree? blockTree, ISealValidator? sealValidator, ISpecProvider? specProvider, - ILogManager? logManager) - : HeaderValidator(blockTree, sealValidator, specProvider, logManager) + ILogManager? logManager) : HeaderValidator(blockTree, sealValidator, specProvider, logManager) +{ + public override bool Validate(BlockHeader header, BlockHeader? parent, bool isUncle, [NotNullWhen(false)] out string? error) + { + error = null; + return ValidateParent(header, parent, ref error); + } +} + +public class OptimismHeaderValidator( + IPoSSwitcher poSSwitcher, + IBlockTree blockTree, + ISealValidator sealValidator, + ISpecProvider specProvider, + ILogManager logManager) + : MergeHeaderValidator( + poSSwitcher, + new PreBedrockHeaderValidator(blockTree, sealValidator, specProvider, logManager), + blockTree, specProvider, sealValidator, logManager) { protected override bool ValidateGasLimitRange(BlockHeader header, BlockHeader parent, IReleaseSpec spec, ref string? error) => true; } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index 82d7d0665c4..33615392a99 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -2,7 +2,9 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Linq; using System.Threading.Tasks; +using Autofac; using Nethermind.Api; using Nethermind.Api.Extensions; using Nethermind.Consensus; @@ -23,12 +25,12 @@ using Nethermind.Consensus.Validators; using Nethermind.Core; using Nethermind.Merge.Plugin.Synchronization; -using Nethermind.Synchronization.ParallelSync; using Nethermind.HealthChecks; using Nethermind.Serialization.Json; using Nethermind.Specs.ChainSpecStyle; using Nethermind.Serialization.Rlp; using Nethermind.Optimism.Rpc; +using Nethermind.Synchronization; namespace Nethermind.Optimism; @@ -80,7 +82,7 @@ public void InitTxTypesAndRlpDecoders(INethermindApi api) { if (ShouldRunSteps(api)) { - api.RegisterTxType(TxType.DepositTx, new OptimismTxDecoder(), Always.Valid); + api.RegisterTxType(new OptimismTxDecoder(), Always.Valid); Rlp.RegisterDecoders(typeof(OptimismReceiptMessageDecoder).Assembly, true); } } @@ -99,7 +101,9 @@ public Task Init(INethermindApi api) ArgumentNullException.ThrowIfNull(_api.BlockTree); ArgumentNullException.ThrowIfNull(_api.EthereumEcdsa); - _api.PoSSwitcher = AlwaysPoS.Instance; + ArgumentNullException.ThrowIfNull(_api.SpecProvider); + + _api.PoSSwitcher = new OptimismPoSSwitcher(_api.SpecProvider, _api.ChainSpec.Optimism.BedrockBlockNumber); _blockCacheService = new BlockCacheService(); _api.EthereumEcdsa = new OptimismEthereumEcdsa(_api.EthereumEcdsa); @@ -133,6 +137,7 @@ public Task InitSynchronization() ArgumentNullException.ThrowIfNull(_api.SyncPeerPool); ArgumentNullException.ThrowIfNull(_api.NodeStatsManager); ArgumentNullException.ThrowIfNull(_api.BlockchainProcessor); + ArgumentNullException.ThrowIfNull(_api.BetterPeerStrategy); ArgumentNullException.ThrowIfNull(_blockCacheService); ArgumentNullException.ThrowIfNull(_invalidChainTracker); @@ -144,45 +149,28 @@ public Task InitSynchronization() _beaconPivot = new BeaconPivot(_syncConfig, _api.DbProvider.MetadataDb, _api.BlockTree, _api.PoSSwitcher, _api.LogManager); _beaconSync = new BeaconSync(_beaconPivot, _api.BlockTree, _syncConfig, _blockCacheService, _api.PoSSwitcher, _api.LogManager); - _api.BetterPeerStrategy = new MergeBetterPeerStrategy(null!, _api.PoSSwitcher, _beaconPivot, _api.LogManager); + _api.BetterPeerStrategy = new MergeBetterPeerStrategy(_api.BetterPeerStrategy, _api.PoSSwitcher, _beaconPivot, _api.LogManager); _api.Pivot = _beaconPivot; - MergeBlockDownloaderFactory blockDownloaderFactory = new MergeBlockDownloaderFactory( - _api.PoSSwitcher, - _beaconPivot, - _api.SpecProvider, - _api.BlockValidator!, - _api.SealValidator!, - _syncConfig, - _api.BetterPeerStrategy!, - new FullStateFinder(_api.BlockTree, _api.StateReader!), - _api.LogManager); + ContainerBuilder builder = new ContainerBuilder(); + ((INethermindApi)_api).ConfigureContainerBuilderFromApiWithNetwork(builder) + .AddSingleton(_beaconSync) + .AddSingleton(_beaconPivot) + .AddSingleton(_api.PoSSwitcher) + .AddSingleton(_mergeConfig) + .AddSingleton(_invalidChainTracker); - _api.Synchronizer = new MergeSynchronizer( - _api.DbProvider, - _api.NodeStorageFactory.WrapKeyValueStore(_api.DbProvider.StateDb), - _api.SpecProvider!, - _api.BlockTree!, - _api.ReceiptStorage!, - _api.SyncPeerPool, - _api.NodeStatsManager!, - _syncConfig, - blockDownloaderFactory, - _beaconPivot, - _api.PoSSwitcher, - _mergeConfig, - _invalidChainTracker, - _api.ProcessExit!, - _api.BetterPeerStrategy, - _api.ChainSpec, - _beaconSync, - _api.StateReader!, - _api.LogManager - ); + builder.RegisterModule(new SynchronizerModule(_syncConfig)); + builder.RegisterModule(new MergeSynchronizerModule()); + + IContainer container = builder.Build(); + + _api.ApiWithNetworkServiceContainer = container; + _api.DisposeStack.Append(container); _ = new PivotUpdator( _api.BlockTree, - _api.Synchronizer.SyncModeSelector, + _api.SyncModeSelector, _api.SyncPeerPool, _syncConfig, _blockCacheService, diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPoSSwitcher.cs b/src/Nethermind/Nethermind.Optimism/OptimismPoSSwitcher.cs new file mode 100644 index 00000000000..8dd42874806 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism/OptimismPoSSwitcher.cs @@ -0,0 +1,35 @@ +using System; +using Nethermind.Consensus; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Int256; +using Nethermind.Optimism; + +public class OptimismPoSSwitcher(ISpecProvider specProvider, long bedrockBlockNumber) : IPoSSwitcher +{ + public UInt256? TerminalTotalDifficulty => specProvider.TerminalTotalDifficulty; + + public UInt256? FinalTotalDifficulty => TerminalTotalDifficulty; + + public bool TransitionFinished => true; + + public Hash256? ConfiguredTerminalBlockHash => null; + + public long? ConfiguredTerminalBlockNumber => null; + + public event EventHandler TerminalBlockReached { add { } remove { } } + + public void ForkchoiceUpdated(BlockHeader newHeadHash, Hash256 finalizedHash) { } + + public (bool IsTerminal, bool IsPostMerge) GetBlockConsensusInfo(BlockHeader header) + { + return (header.Number == bedrockBlockNumber - 1, header.IsPostMerge = header.Number >= bedrockBlockNumber); + } + + public bool HasEverReachedTerminalBlock() => true; + + public bool IsPostMerge(BlockHeader header) => GetBlockConsensusInfo(header).IsPostMerge; + + public bool TryUpdateTerminalBlock(BlockHeader header) => false; +} diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs index 8a3d5586391..e5933bb58f6 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs @@ -12,6 +12,7 @@ using Nethermind.Evm; using Nethermind.Facade; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; using Nethermind.JsonRpc; using Nethermind.JsonRpc.Client; @@ -108,7 +109,8 @@ public OptimismEthRpcModule( public override async Task> eth_sendTransaction(TransactionForRpc rpcTx) { - Transaction tx = rpcTx.ToTransactionWithDefaults(_blockchainBridge.GetChainId()); + Transaction tx = rpcTx.ToTransaction(); + tx.ChainId = _blockchainBridge.GetChainId(); tx.SenderAddress ??= _ecdsa.RecoverAddress(tx); if (tx.SenderAddress is null) @@ -116,11 +118,6 @@ public override async Task> eth_sendTransaction(Transacti return ResultWrapper.Fail("Failed to recover sender"); } - if (rpcTx.Nonce is null) - { - tx.Nonce = _accountStateProvider.GetNonce(tx.SenderAddress); - } - await _sealer.Seal(tx, TxHandlingOptions.None); return await eth_sendRawTransaction(Rlp.Encode(tx, RlpBehaviors.SkipTypedWrapping).Bytes); @@ -165,20 +162,19 @@ public override async Task> eth_sendRawTransaction(byte[] public new ResultWrapper eth_getTransactionByHash(Hash256 transactionHash) { - (TxReceipt? receipt, Transaction? transaction, UInt256? baseFee) = _blockchainBridge.GetTransaction(transactionHash, checkTxnPool: true); + (TxReceipt? receipt, Transaction? transaction, _) = _blockchainBridge.GetTransaction(transactionHash, checkTxnPool: true); if (transaction is null) { return ResultWrapper.Success(null); } RecoverTxSenderIfNeeded(transaction); - OptimismTransactionForRpc transactionModel = new(receipt?.BlockHash, receipt as OptimismTxReceipt, transaction, baseFee); + OptimismTransactionForRpc transactionModel = new OptimismTransactionForRpc(transaction, blockHash: receipt?.BlockHash, receipt: receipt as OptimismTxReceipt); if (_logger.IsTrace) _logger.Trace($"eth_getTransactionByHash request {transactionHash}, result: {transactionModel.Hash}"); return ResultWrapper.Success(transactionModel); } - public new ResultWrapper eth_getTransactionByBlockHashAndIndex(Hash256 blockHash, - UInt256 positionIndex) + public new ResultWrapper eth_getTransactionByBlockHashAndIndex(Hash256 blockHash, UInt256 positionIndex) { SearchResult searchResult = _blockFinder.SearchForBlock(new BlockParameter(blockHash)); if (searchResult.IsError || searchResult.Object is null) @@ -196,7 +192,10 @@ public override async Task> eth_sendRawTransaction(byte[] Transaction transaction = block.Transactions[(int)positionIndex]; RecoverTxSenderIfNeeded(transaction); - OptimismTransactionForRpc transactionModel = new(block.Hash, receipts.FirstOrDefault(r => r.TxHash == transaction.Hash), transaction, block.BaseFeePerGas); + OptimismTransactionForRpc transactionModel = new OptimismTransactionForRpc( + transaction, + blockHash: block.Hash, + receipt: receipts.FirstOrDefault(r => r.TxHash == transaction.Hash)); return ResultWrapper.Success(transactionModel); } @@ -222,7 +221,10 @@ public override async Task> eth_sendRawTransaction(byte[] Transaction transaction = block.Transactions[(int)positionIndex]; RecoverTxSenderIfNeeded(transaction); - OptimismTransactionForRpc transactionModel = new(block.Hash, receipts.FirstOrDefault(r => r.TxHash == transaction.Hash), transaction, block.BaseFeePerGas); + OptimismTransactionForRpc transactionModel = new OptimismTransactionForRpc( + transaction, + blockHash: block.Hash, + receipt: receipts.FirstOrDefault(r => r.TxHash == transaction.Hash)); if (_logger.IsDebug) _logger.Debug( @@ -252,7 +254,12 @@ public override async Task> eth_sendRawTransaction(byte[] if (returnFullTransactionObjects) { _blockchainBridge.RecoverTxSenders(block); - result.Transactions = result.Transactions.Select((hash, index) => new OptimismTransactionForRpc(block.Hash, receipts.FirstOrDefault(r => r.TxHash?.Equals(hash) ?? false), block.Transactions[index], block.BaseFeePerGas)); + result.Transactions = result.Transactions.Select((hash, index) => new OptimismTransactionForRpc( + transaction: block.Transactions[index], + blockHash: block.Hash, + blockNumber: block.Number, + txIndex: index, + receipt: receipts.FirstOrDefault(r => r.TxHash?.Equals(hash) ?? false))); } return ResultWrapper.Success(result); diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismTransactionForRpc.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismTransactionForRpc.cs index cb28429f3f6..fba5f35ba18 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismTransactionForRpc.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismTransactionForRpc.cs @@ -3,27 +3,91 @@ using Nethermind.Core.Crypto; using Nethermind.Core; -using Nethermind.Facade.Eth; using Nethermind.Int256; using System.Text.Json.Serialization; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Facade.Eth; +using System; namespace Nethermind.Optimism.Rpc; -public class OptimismTransactionForRpc : TransactionForRpc +/// +/// Defined in: +/// - https://github.com/ethereum-optimism/op-geth/blob/8af19cf20261c0b62f98cc27da3a268f542822ee/core/types/deposit_tx.go#L29-L46 +/// - https://specs.optimism.io/protocol/deposits.html#the-deposited-transaction-type +/// +public class OptimismTransactionForRpc : TransactionForRpc, IFromTransaction { - public OptimismTransactionForRpc(Hash256? blockHash, OptimismTxReceipt? receipt, Transaction transaction, UInt256? baseFee = null) - : base(blockHash, receipt?.BlockNumber, receipt?.Index, transaction, baseFee) - { - if (transaction.Type == TxType.DepositTx) - { - SourceHash = transaction.SourceHash; - Mint = transaction.Mint; - IsSystemTx = transaction.IsOPSystemTransaction ? true : null; - Nonce = receipt?.DepositNonce; - DepositReceiptVersion = receipt?.DepositReceiptVersion; - } - } + public static TxType TxType => TxType.DepositTx; + + public override TxType? Type => TxType; + + public Hash256? SourceHash { get; set; } + + public Address? From { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Address? To { get; set; } + + public UInt256? Mint { get; set; } + + public UInt256? Value { get; set; } + + public ulong? Gas { get; set; } + + public bool? IsSystemTx { get; set; } + + public byte[]? Input { get; set; } + + public UInt256? Nonce { get; set; } + #region Nethermind specific fields [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public UInt256? DepositReceiptVersion { get; set; } + #endregion + + [JsonConstructor] + public OptimismTransactionForRpc() { } + + public OptimismTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, OptimismTxReceipt? receipt = null) + : base(transaction, txIndex, blockHash, blockNumber) + { + SourceHash = transaction.SourceHash ?? Hash256.Zero; + From = transaction.SenderAddress ?? Address.SystemUser; + To = transaction.To; + Mint = transaction.Mint; + Value = transaction.Value; + // TODO: Unsafe cast + Gas = (ulong)transaction.GasLimit; + IsSystemTx = transaction.IsOPSystemTransaction; + Input = transaction.Data?.ToArray() ?? []; + Nonce = receipt?.DepositNonce ?? 0; + + DepositReceiptVersion = receipt?.DepositReceiptVersion; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.SourceHash = SourceHash ?? throw new ArgumentNullException(nameof(SourceHash)); + tx.SenderAddress = From ?? throw new ArgumentNullException(nameof(From)); + tx.To = To; + tx.Mint = Mint ?? 0; + tx.Value = Value ?? throw new ArgumentNullException(nameof(Value)); + // TODO: Unsafe cast + tx.GasLimit = (long)(Gas ?? throw new ArgumentNullException(nameof(Gas))); + tx.IsOPSystemTransaction = IsSystemTx ?? false; + tx.Data = Input ?? throw new ArgumentNullException(nameof(Input)); + + return tx; + } + + // NOTE: No defaulting mechanism for Optimism transactions + public override void EnsureDefaults(long? gasCap) { } + + public override bool ShouldSetBaseFee() => false; + + public static OptimismTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, receipt: extraData.Receipt as OptimismTxReceipt); } diff --git a/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs b/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs index bec343e4e35..f1583b6339b 100644 --- a/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs +++ b/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs @@ -4,8 +4,7 @@ using System.Threading.Tasks; using Nethermind.Core.Extensions; using Nethermind.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Overseer.Test.Framework; using NUnit.Framework; @@ -72,23 +71,24 @@ public async Task Clique_vote() [Test] public async Task Clique_transaction_broadcast() { - TransactionForRpc tx = new TransactionForRpc(); - tx.Value = 2.Ether(); - tx.GasPrice = 20.GWei(); - tx.Gas = 21000; - tx.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; - - tx.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; + 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") diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs index 9d22da0ee84..6661b28c2fc 100644 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs +++ b/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs @@ -2,37 +2,31 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Overseer.Test.JsonRpc; -namespace Nethermind.Overseer.Test.Framework +namespace Nethermind.Overseer.Test.Framework; + +public class CliqueContext(CliqueState state) : TestContextBase(state) { - public class CliqueContext : TestContextBase + public CliqueContext Propose(Address address, bool vote) { - public CliqueContext(CliqueState state) : base(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", new object[] { address, 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", new object[] { address })); - } + 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", new object[] { tx })); - } + 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.Runner.Test/Ethereum/ContextWithMocks.cs b/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs index ef3c4c9a68a..9b0a4f49d77 100644 --- a/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs +++ b/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.IO.Abstractions; +using Autofac; using Nethermind.Api; using Nethermind.Blockchain; using Nethermind.Blockchain.Filters; @@ -46,6 +47,7 @@ using Nethermind.Trie; using NSubstitute; using Nethermind.Blockchain.Blocks; +using Nethermind.Core; using Nethermind.Facade.Find; namespace Nethermind.Runner.Test.Ethereum @@ -76,7 +78,6 @@ public static NethermindApi ContextWithMocks() StaticNodesManager = Substitute.For(), BloomStorage = Substitute.For(), Sealer = Substitute.For(), - Synchronizer = Substitute.For(), BlockchainProcessor = Substitute.For(), BlockProducer = Substitute.For(), DiscoveryApp = Substitute.For(), @@ -103,7 +104,6 @@ public static NethermindApi ContextWithMocks() EngineSignerStore = Substitute.For(), NodeStatsManager = Substitute.For(), RpcModuleProvider = Substitute.For(), - SyncModeSelector = Substitute.For(), SyncPeerPool = Substitute.For(), PeerDifficultyRefreshPool = Substitute.For(), WebSocketsManager = Substitute.For(), @@ -117,10 +117,15 @@ public static NethermindApi ContextWithMocks() TxValidator = new TxValidator(MainnetSpecProvider.Instance.ChainId), UnclesValidator = Substitute.For(), BlockProductionPolicy = Substitute.For(), - SyncProgressResolver = Substitute.For(), BetterPeerStrategy = Substitute.For(), ReceiptMonitor = Substitute.For(), - BadBlocksStore = Substitute.For() + BadBlocksStore = Substitute.For(), + + ApiWithNetworkServiceContainer = new ContainerBuilder() + .AddSingleton(Substitute.For()) + .AddSingleton(Substitute.For()) + .AddSingleton(Substitute.For()) + .Build(), }; api.WorldStateManager = new ReadOnlyWorldStateManager(api.DbProvider, Substitute.For(), LimboLogs.Instance); diff --git a/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs b/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs index 2d097c4a769..9d17ac28787 100644 --- a/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs +++ b/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs @@ -11,12 +11,10 @@ using Nethermind.Config; using Nethermind.Consensus; using Nethermind.Core; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Logging; using Nethermind.Serialization.Json; using Nethermind.Specs.ChainSpecStyle; -using ILogger = Nethermind.Logging.InterfaceLogger; namespace Nethermind.Runner.Ethereum.Api { @@ -83,7 +81,6 @@ private ChainSpec LoadChainSpec(IJsonSerializer ethereumJsonSerializer) IChainSpecLoader loader = new ChainSpecLoader(ethereumJsonSerializer); ChainSpec chainSpec = loader.LoadEmbeddedOrFromFile(chainSpecFile, _logger); - TransactionForRpc.DefaultChainId = chainSpec.ChainId; return chainSpec; } diff --git a/src/Nethermind/Nethermind.Runner/configs/base-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/base-mainnet.cfg index 9c32c0f7d8c..14d442ace08 100644 --- a/src/Nethermind/Nethermind.Runner/configs/base-mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/base-mainnet.cfg @@ -15,8 +15,8 @@ "AncientBodiesBarrier": 105235063, "AncientReceiptsBarrier": 105235063, "FastSyncCatchUpHeightDelta": "10000000000", - "PivotNumber": 20680000, - "PivotHash": "0xf6a3f8571f1641e83c531e264dd1a805f8a1ecfe974a5d6cdf211fe16d35fad4", + "PivotNumber": 21130000, + "PivotHash": "0x3e1cdb3a013b113ba7c413feea14028dac20f6a367e4eb6c5b93ba2406b62755", "PivotTotalDifficulty": "0", "MaxAttemptsToUpdatePivot": 0 }, diff --git a/src/Nethermind/Nethermind.Runner/configs/base-sepolia.cfg b/src/Nethermind/Nethermind.Runner/configs/base-sepolia.cfg index e369d49a0bd..ba97e4c2ea1 100644 --- a/src/Nethermind/Nethermind.Runner/configs/base-sepolia.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/base-sepolia.cfg @@ -13,8 +13,8 @@ "FastSync": true, "SnapSync": true, "FastSyncCatchUpHeightDelta": "10000000000", - "PivotNumber": 16190000, - "PivotHash": "0x8a04494563509fbd72ca62cc947b265d3bfa8803a0dc4d1450672853efbc6817", + "PivotNumber": 16640000, + "PivotHash": "0x826380d1472683f54980776743190b268c3d7da94ff775f61997fc7dbc5919c1", "PivotTotalDifficulty": "0", "MaxAttemptsToUpdatePivot": 0 }, diff --git a/src/Nethermind/Nethermind.Runner/configs/chiado.cfg b/src/Nethermind/Nethermind.Runner/configs/chiado.cfg index 2caeddda453..41587f3efbe 100644 --- a/src/Nethermind/Nethermind.Runner/configs/chiado.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/chiado.cfg @@ -16,8 +16,8 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 12130000, - "PivotHash": "0x906cdf95d8d77c3bca1cca7c6f832cf15b3b39da3bb9c2c9308c79e4f5bde2cf", + "PivotNumber": 12310000, + "PivotHash": "0xc358086245b073027dbe0640c3647b446a5431d2fbcaeeaa51a204d62f5bfb47", "PivotTotalDifficulty": "231708131825107706987652208063906496124457284", "FastSyncCatchUpHeightDelta": "10000000000", "UseGethLimitsInFastBlocks": false diff --git a/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg b/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg index 85a3d9a092b..ee1ede26e10 100644 --- a/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg @@ -11,9 +11,9 @@ }, "Sync": { "FastSync": true, - "PivotNumber": 32180000, - "PivotHash": "0xc962101318876bc4728b6c491a0a29117dfce9c2d27800008bd3d74d11fd1aa5", - "PivotTotalDifficulty": "10950286567515799754251394867154301044276410309", + "PivotNumber": 32340000, + "PivotHash": "0xc0a0cb30d591c3263fa0d317e3839e45f62d99cb096757e54fe9af65f5bd80ae", + "PivotTotalDifficulty": "11004731746223149908405534804343383958109034440", "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, diff --git a/src/Nethermind/Nethermind.Runner/configs/exosama.cfg b/src/Nethermind/Nethermind.Runner/configs/exosama.cfg index d8ef1140f00..b1d56d75702 100644 --- a/src/Nethermind/Nethermind.Runner/configs/exosama.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/exosama.cfg @@ -11,9 +11,9 @@ }, "Sync": { "FastSync": true, - "PivotNumber": 12340000, - "PivotHash": "0xab44a093519fd72562c9a93fe5cdaaa8d5f171b90edeca811003324eff6964a3", - "PivotTotalDifficulty": "4199084407804380639138042655708019729009213285", + "PivotNumber": 12520000, + "PivotHash": "0x662472aa84289ecb5fd53369f4f0c0f088199b3df4223599257bcf7d92ddf543", + "PivotTotalDifficulty": "4260335233850149562561450085045738007070933285", "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, diff --git a/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg b/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg index f7bcfab9c47..a333f843e1a 100644 --- a/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg @@ -13,8 +13,8 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 36340000, - "PivotHash": "0x9fefe19997f398430c979a36b70d734997db87461b24084f303444430a7d99ac", + "PivotNumber": 36520000, + "PivotHash": "0x380d77ea4f36e3ef2f9d3536772b43ceaa23c64ff4013492f23e90be2402ef68", "PivotTotalDifficulty": "8626000110427538733349499292577475819600160930", "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg index d60775812e4..aceecb125f7 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg @@ -11,9 +11,9 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 13710000, - "PivotHash": "0x10c7117af99c830c485c9dee83fea59ef8bc9fe74fe93ce393ef1ae06e8ddb50", - "PivotTotalDifficulty": "27138394" + "PivotNumber": 13890000, + "PivotHash": "0x1ed87295c8a93a1cb14f5871363452edd1c34561964b6cfd1c59184a1187c4cd", + "PivotTotalDifficulty": "27453101" }, "Metrics": { "NodeName": "JOC-Mainnet" diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg index 6fe47a3e5d7..ffa597dea7c 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg @@ -11,9 +11,9 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 7310000, - "PivotHash": "0xe277d991a85ff41c7f1c45940a871a00ded49407b446ce6ab337b4820f6c903a", - "PivotTotalDifficulty": "13535348" + "PivotNumber": 7490000, + "PivotHash": "0x2b4e6a42a6b559fee61f21110fcd88f7f9c0c30e9d7704d27638f95237e7a85e", + "PivotTotalDifficulty": "13782418" }, "Metrics": { "NodeName": "JOC-Testnet" diff --git a/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg index 74b1b6f68d2..4ff53d17602 100644 --- a/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg @@ -9,8 +9,8 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 20901000, - "PivotHash": "0xfe2488ddbad3900b2e647598014b1eb5d3389e8ad7111cb612573a8a3bc1e834", + "PivotNumber": 20976000, + "PivotHash": "0x7841f9f6b15a11c4d7e3a291ced640f2a3d7ff55de9ab51883e3f5396bd240db", "PivotTotalDifficulty": "58750003716598352816469", "FastSyncCatchUpHeightDelta": "10000000000" }, diff --git a/src/Nethermind/Nethermind.Runner/configs/op-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/op-mainnet.cfg index a507ab52511..a3ab1dd1b22 100644 --- a/src/Nethermind/Nethermind.Runner/configs/op-mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/op-mainnet.cfg @@ -16,8 +16,8 @@ "AncientBodiesBarrier": 105235063, "AncientReceiptsBarrier": 105235063, "FastSyncCatchUpHeightDelta": "10000000000", - "PivotNumber": 126270000, - "PivotHash": "0xd3404c9404e47cbbcd688c1620119495cad1dab37631b556cbbb6d4ce0cea54f", + "PivotNumber": 126730000, + "PivotHash": "0x09e633ab9904950f6830196b9141b625aa70df43a3b619927aa1017060e39978", "PivotTotalDifficulty": "0", "MaxAttemptsToUpdatePivot": 0 }, @@ -39,12 +39,6 @@ "Merge": { "Enabled": true }, - "Snapshot": { - "Enabled": true, - "DownloadUrl": "http://optimism-snapshot.nethermind.io/op-mainnet-genesis-v2.zip", - "SnapshotFileName": "op-mainnet-genesis-v2.zip", - "Checksum": "0x9b08eeb974c46f257c8bca812b119849590d92975ac86e4215d0d5647c26b147" - }, "Optimism": { "SequencerUrl": "https://mainnet-sequencer.optimism.io" } diff --git a/src/Nethermind/Nethermind.Runner/configs/op-sepolia.cfg b/src/Nethermind/Nethermind.Runner/configs/op-sepolia.cfg index 9b5b7ed0aca..374881b2d34 100644 --- a/src/Nethermind/Nethermind.Runner/configs/op-sepolia.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/op-sepolia.cfg @@ -13,8 +13,8 @@ "FastSync": true, "SnapSync": true, "FastSyncCatchUpHeightDelta": "10000000000", - "PivotNumber": 18170000, - "PivotHash": "0xd006b425a97e05178570f69ec445e513ed076e07bdc7f929c0ccbef0ac6981ac", + "PivotNumber": 18630000, + "PivotHash": "0x8a77b55ae1ef0792f070c048cee04d830e5463f14b7f9a3e685890e97e8d0ed1", "PivotTotalDifficulty": "0", "MaxAttemptsToUpdatePivot": 0 }, diff --git a/src/Nethermind/Nethermind.Runner/configs/sepolia.cfg b/src/Nethermind/Nethermind.Runner/configs/sepolia.cfg index 99442bdcb2f..12a3940aea1 100644 --- a/src/Nethermind/Nethermind.Runner/configs/sepolia.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/sepolia.cfg @@ -17,8 +17,8 @@ "FastSync": true, "SnapSync": true, "UseGethLimitsInFastBlocks": true, - "PivotNumber": 6821000, - "PivotHash": "0x7c5a0544e9eb44f09d977808fef54a3c7bdcd3d88105117f70cc890bcb32f723", + "PivotNumber": 6885000, + "PivotHash": "0x4444447abec31fe6d9db967ede00e778b80a895933d7b6c8f321fb9d533d54db", "PivotTotalDifficulty": "17000018015853232", "FastSyncCatchUpHeightDelta": "10000000000" }, diff --git a/src/Nethermind/Nethermind.Runner/configs/volta.cfg b/src/Nethermind/Nethermind.Runner/configs/volta.cfg index cd6b665a3f1..8d0b6c83b33 100644 --- a/src/Nethermind/Nethermind.Runner/configs/volta.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/volta.cfg @@ -15,9 +15,9 @@ }, "Sync": { "FastSync": true, - "PivotNumber": 29530000, - "PivotHash": "0xd4c57f73fbea59f81e88d3325afe1c8f09e095e477b901c92fad7ea6beb9c595", - "PivotTotalDifficulty": "10048538295175312826073452157460115283920659540", + "PivotNumber": 29640000, + "PivotHash": "0x0d82a28c3ff2df03f03fc27e5dd70ca1d2f10dd0f9e283f7ebb9823253f3b053", + "PivotTotalDifficulty": "10085969355536616057054423364277609787180535520", "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs new file mode 100644 index 00000000000..6e38f63dc38 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using System.Runtime.CompilerServices; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; + +namespace Nethermind.Serialization.Rlp; + +public class BlockBodyDecoder : IRlpValueDecoder +{ + private readonly TxDecoder _txDecoder = TxDecoder.Instance; + private readonly HeaderDecoder _headerDecoder = new(); + private readonly WithdrawalDecoder _withdrawalDecoderDecoder = new(); + private readonly ConsensusRequestDecoder _requestsDecoder = ConsensusRequestDecoder.Instance; + + private static BlockBodyDecoder? _instance = null; + public static BlockBodyDecoder Instance => _instance ??= new BlockBodyDecoder(); + + // Cant set to private because of `Rlp.RegisterDecoder`. + public BlockBodyDecoder() + { + } + + public int GetLength(BlockBody item, RlpBehaviors rlpBehaviors) + { + return Rlp.LengthOfSequence(GetBodyLength(item)); + } + + public int GetBodyLength(BlockBody b) + { + (int txs, int uncles, int? withdrawals, int? requests) = GetBodyComponentLength(b); + return Rlp.LengthOfSequence(txs) + + Rlp.LengthOfSequence(uncles) + + (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0) + + (requests is not null ? Rlp.LengthOfSequence(requests.Value) : 0); + } + + public (int Txs, int Uncles, int? Withdrawals, int? Requests) GetBodyComponentLength(BlockBody b) => + ( + GetTxLength(b.Transactions), + GetUnclesLength(b.Uncles), + (b.Withdrawals is not null ? GetWithdrawalsLength(b.Withdrawals) : null), + (b.Requests is not null ? GetRequestsLength(b.Requests) : null) + ); + + private int GetTxLength(Transaction[] transactions) => transactions.Sum(t => _txDecoder.GetLength(t, RlpBehaviors.None)); + + private int GetUnclesLength(BlockHeader[] headers) => headers.Sum(t => _headerDecoder.GetLength(t, RlpBehaviors.None)); + + private int GetWithdrawalsLength(Withdrawal[] withdrawals) => withdrawals.Sum(t => _withdrawalDecoderDecoder.GetLength(t, RlpBehaviors.None)); + + private int GetRequestsLength(ConsensusRequest[] requests) => requests.Sum(t => _requestsDecoder.GetLength(t, RlpBehaviors.None)); + + public BlockBody? Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int sequenceLength = ctx.ReadSequenceLength(); + int startingPosition = ctx.Position; + if (sequenceLength == 0) + { + return null; + } + + return DecodeUnwrapped(ref ctx, startingPosition + sequenceLength); + } + + public BlockBody? DecodeUnwrapped(ref Rlp.ValueDecoderContext ctx, int lastPosition) + { + + // quite significant allocations (>0.5%) here based on a sample 3M blocks sync + // (just on these delegates) + Transaction[] transactions = ctx.DecodeArray(_txDecoder); + BlockHeader[] uncles = ctx.DecodeArray(_headerDecoder); + Withdrawal[]? withdrawals = null; + ConsensusRequest[]? requests = null; + if (ctx.PeekNumberOfItemsRemaining(lastPosition, 1) > 0) + { + withdrawals = ctx.DecodeArray(_withdrawalDecoderDecoder); + } + + if (ctx.PeekNumberOfItemsRemaining(lastPosition, 1) > 0) + { + requests = ctx.DecodeArray(_requestsDecoder); + } + + return new BlockBody(transactions, uncles, withdrawals, requests); + } + + public void Serialize(RlpStream stream, BlockBody body) + { + stream.StartSequence(GetBodyLength(body)); + stream.StartSequence(GetTxLength(body.Transactions)); + foreach (Transaction? txn in body.Transactions) + { + stream.Encode(txn); + } + + stream.StartSequence(GetUnclesLength(body.Uncles)); + foreach (BlockHeader? uncle in body.Uncles) + { + stream.Encode(uncle); + } + + if (body.Withdrawals is not null) + { + stream.StartSequence(GetWithdrawalsLength(body.Withdrawals)); + foreach (Withdrawal? withdrawal in body.Withdrawals) + { + stream.Encode(withdrawal); + } + } + + if (body.Requests is not null) + { + stream.StartSequence(GetRequestsLength(body.Requests)); + foreach (ConsensusRequest? request in body.Requests) + { + stream.Encode(request); + } + } + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 0530f141fd8..89d1cfd7a83 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -12,9 +12,7 @@ namespace Nethermind.Serialization.Rlp public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder { private readonly HeaderDecoder _headerDecoder = new(); - private readonly TxDecoder _txDecoder = TxDecoder.Instance; - private readonly WithdrawalDecoder _withdrawalDecoder = new(); - private readonly ConsensusRequestDecoder _consensusRequestsDecoder = ConsensusRequestDecoder.Instance; + private readonly BlockBodyDecoder _blockBodyDecoder = BlockBodyDecoder.Instance; public Block? Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { @@ -29,191 +27,27 @@ public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder return null; } - int sequenceLength = rlpStream.ReadSequenceLength(); - int blockCheck = rlpStream.Position + sequenceLength; - - BlockHeader header = Rlp.Decode(rlpStream); - - int transactionsSequenceLength = rlpStream.ReadSequenceLength(); - int transactionsCheck = rlpStream.Position + transactionsSequenceLength; - List transactions = new(); - while (rlpStream.Position < transactionsCheck) - { - transactions.Add(Rlp.Decode(rlpStream, rlpBehaviors)); - } - - rlpStream.Check(transactionsCheck); - - int unclesSequenceLength = rlpStream.ReadSequenceLength(); - int unclesCheck = rlpStream.Position + unclesSequenceLength; - List uncleHeaders = new(); - while (rlpStream.Position < unclesCheck) - { - uncleHeaders.Add(Rlp.Decode(rlpStream, rlpBehaviors)); - } - - rlpStream.Check(unclesCheck); - - List? withdrawals = DecodeWithdrawals(rlpStream, blockCheck); - List? requests = DecodeRequests(rlpStream, blockCheck); - - if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) - { - rlpStream.Check(blockCheck); - } - - return new(header, transactions, uncleHeaders, withdrawals, requests); - } - - - private List? DecodeWithdrawals(RlpStream rlpStream, int blockCheck) - { - List? withdrawals = null; - if (rlpStream.Position != blockCheck) - { - bool lengthWasRead = true; - try - { - rlpStream.PeekNextRlpLength(); - } - catch - { - lengthWasRead = false; - } - - if (lengthWasRead) - { - int withdrawalsLength = rlpStream.ReadSequenceLength(); - int withdrawalsCheck = rlpStream.Position + withdrawalsLength; - withdrawals = new(); - - while (rlpStream.Position < withdrawalsCheck) - { - withdrawals.Add(Rlp.Decode(rlpStream)); - } - - rlpStream.Check(withdrawalsCheck); - } - } - - return withdrawals; + Span contentSpan = rlpStream.PeekNextItem(); + Rlp.ValueDecoderContext ctx = new Rlp.ValueDecoderContext(contentSpan); + Block? decoded = Decode(ref ctx, rlpBehaviors); + rlpStream.Position += contentSpan.Length; + return decoded; } - private List? DecodeRequests(RlpStream rlpStream, int blockCheck) - { - List? requests = null; - if (rlpStream.Position != blockCheck) - { - bool lengthWasRead = true; - try - { - rlpStream.PeekNextRlpLength(); - } - catch - { - lengthWasRead = false; - } - - if (lengthWasRead) - { - int requestsLength = rlpStream.ReadSequenceLength(); - int requestsCheck = rlpStream.Position + requestsLength; - requests = new(); - - while (rlpStream.Position < requestsCheck) - { - requests.Add(Rlp.Decode(rlpStream)); - } - - rlpStream.Check(requestsCheck); - } - } - - return requests; - } - - private (int Total, int Txs, int Uncles, int? Withdrawals, int? Requests) GetContentLength(Block item, RlpBehaviors rlpBehaviors) { - int contentLength = _headerDecoder.GetLength(item.Header, rlpBehaviors); - - int txLength = GetTxLength(item, rlpBehaviors); - contentLength += Rlp.LengthOfSequence(txLength); - - int unclesLength = GetUnclesLength(item, rlpBehaviors); - contentLength += Rlp.LengthOfSequence(unclesLength); - - int? withdrawalsLength = null; - if (item.Withdrawals is not null) - { - withdrawalsLength = GetWithdrawalsLength(item, rlpBehaviors); - - if (withdrawalsLength.HasValue) - contentLength += Rlp.LengthOfSequence(withdrawalsLength.Value); - } - - int? consensusRequestsLength = null; - if (item.Requests is not null) - { - consensusRequestsLength = GetConsensusRequestsLength(item, rlpBehaviors); - - if (consensusRequestsLength.HasValue) - contentLength += Rlp.LengthOfSequence(consensusRequestsLength.Value); - } - - return (contentLength, txLength, unclesLength, withdrawalsLength, consensusRequestsLength); - } - - private int GetUnclesLength(Block item, RlpBehaviors rlpBehaviors) - { - int unclesLength = 0; - for (int i = 0; i < item.Uncles.Length; i++) - { - unclesLength += _headerDecoder.GetLength(item.Uncles[i], rlpBehaviors); - } - - return unclesLength; - } - - private int GetTxLength(Block item, RlpBehaviors rlpBehaviors) - { - int txLength = 0; - for (int i = 0; i < item.Transactions.Length; i++) - { - txLength += _txDecoder.GetLength(item.Transactions[i], rlpBehaviors); - } - - return txLength; - } - - private int? GetWithdrawalsLength(Block item, RlpBehaviors rlpBehaviors) - { - if (item.Withdrawals is null) - return null; - - var withdrawalLength = 0; - - for (int i = 0, count = item.Withdrawals.Length; i < count; i++) - { - withdrawalLength += _withdrawalDecoder.GetLength(item.Withdrawals[i], rlpBehaviors); - } - - return withdrawalLength; - } - - private int? GetConsensusRequestsLength(Block item, RlpBehaviors rlpBehaviors) - { - if (item.Requests is null) - return null; + int headerLength = _headerDecoder.GetLength(item.Header, rlpBehaviors); - var consensusRequestsLength = 0; + (int txs, int uncles, int? withdrawals, int? requests) = _blockBodyDecoder.GetBodyComponentLength(item.Body); - for (int i = 0, count = item.Requests.Length; i < count; i++) - { - consensusRequestsLength += _consensusRequestsDecoder.GetLength(item.Requests[i], rlpBehaviors); - } + int contentLength = + headerLength + + Rlp.LengthOfSequence(txs) + + Rlp.LengthOfSequence(uncles) + + (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0) + + (requests is not null ? Rlp.LengthOfSequence(requests.Value) : 0); - return consensusRequestsLength; + return (contentLength, txs, uncles, withdrawals, requests); } public int GetLength(Block? item, RlpBehaviors rlpBehaviors) @@ -238,78 +72,8 @@ public int GetLength(Block? item, RlpBehaviors rlpBehaviors) int blockCheck = decoderContext.Position + sequenceLength; BlockHeader header = Rlp.Decode(ref decoderContext); - - int transactionsSequenceLength = decoderContext.ReadSequenceLength(); - int transactionsCheck = decoderContext.Position + transactionsSequenceLength; - List transactions = new(); - while (decoderContext.Position < transactionsCheck) - { - transactions.Add(Rlp.Decode(ref decoderContext, rlpBehaviors)); - } - - decoderContext.Check(transactionsCheck); - - int unclesSequenceLength = decoderContext.ReadSequenceLength(); - int unclesCheck = decoderContext.Position + unclesSequenceLength; - List uncleHeaders = new(); - while (decoderContext.Position < unclesCheck) - { - uncleHeaders.Add(Rlp.Decode(ref decoderContext, rlpBehaviors)); - } - - decoderContext.Check(unclesCheck); - - List? withdrawals = DecodeWithdrawals(ref decoderContext, blockCheck); - List? requests = DecodeRequests(ref decoderContext, blockCheck); - - if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) - { - decoderContext.Check(blockCheck); - } - - return new(header, transactions, uncleHeaders, withdrawals, requests); - } - - private List? DecodeWithdrawals(ref Rlp.ValueDecoderContext decoderContext, int blockCheck) - { - List? withdrawals = null; - - if (decoderContext.Position != blockCheck) - { - int withdrawalsLength = decoderContext.ReadSequenceLength(); - int withdrawalsCheck = decoderContext.Position + withdrawalsLength; - withdrawals = new(); - - while (decoderContext.Position < withdrawalsCheck) - { - withdrawals.Add(Rlp.Decode(ref decoderContext)); - } - - decoderContext.Check(withdrawalsCheck); - } - - return withdrawals; - } - - private List? DecodeRequests(ref Rlp.ValueDecoderContext decoderContext, int blockCheck) - { - List? requests = null; - - if (decoderContext.Position != blockCheck) - { - int requestsLength = decoderContext.ReadSequenceLength(); - int requestsCheck = decoderContext.Position + requestsLength; - requests = new(); - - while (decoderContext.Position < requestsCheck) - { - requests.Add(Rlp.Decode(ref decoderContext)); - } - - decoderContext.Check(requestsCheck); - } - - return requests; + BlockBody body = _blockBodyDecoder.DecodeUnwrapped(ref decoderContext, blockCheck); + return new(header, body); } public Rlp Encode(Block? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) diff --git a/src/Nethermind/Nethermind.State.Test/PatriciaTreeTests.cs b/src/Nethermind/Nethermind.State.Test/PatriciaTreeTests.cs index 9f1108a9f33..0dd057454aa 100644 --- a/src/Nethermind/Nethermind.State.Test/PatriciaTreeTests.cs +++ b/src/Nethermind/Nethermind.State.Test/PatriciaTreeTests.cs @@ -24,11 +24,11 @@ public void Create_commit_change_balance_get() Account account = new(1); StateTree stateTree = new(); stateTree.Set(TestItem.AddressA, account); - stateTree.Commit(0); + stateTree.Commit(); account = account.WithChangedBalance(2); stateTree.Set(TestItem.AddressA, account); - stateTree.Commit(0); + stateTree.Commit(); Account accountRestored = stateTree.Get(TestItem.AddressA); Assert.That(accountRestored.Balance, Is.EqualTo((UInt256)2)); @@ -41,11 +41,11 @@ public void Create_create_commit_change_balance_get() StateTree stateTree = new(); stateTree.Set(TestItem.AddressA, account); stateTree.Set(TestItem.AddressB, account); - stateTree.Commit(0); + stateTree.Commit(); account = account.WithChangedBalance(2); stateTree.Set(TestItem.AddressA, account); - stateTree.Commit(0); + stateTree.Commit(); Account accountRestored = stateTree.Get(TestItem.AddressA); Assert.That(accountRestored.Balance, Is.EqualTo((UInt256)2)); @@ -58,7 +58,7 @@ public void Create_commit_reset_change_balance_get() Account account = new(1); StateTree stateTree = new(new TrieStore(db, LimboLogs.Instance), LimboLogs.Instance); stateTree.Set(TestItem.AddressA, account); - stateTree.Commit(0); + stateTree.Commit(); Hash256 rootHash = stateTree.RootHash; stateTree.RootHash = null; @@ -67,7 +67,7 @@ public void Create_commit_reset_change_balance_get() stateTree.Get(TestItem.AddressA); account = account.WithChangedBalance(2); stateTree.Set(TestItem.AddressA, account); - stateTree.Commit(0); + stateTree.Commit(); Assert.That(db.Keys.Count, Is.EqualTo(2)); } @@ -84,7 +84,7 @@ public void Commit_with_skip_root_should_skip_root(bool skipRoot, bool hasRoot) stateTree.Set(TestItem.AddressA, account); stateTree.UpdateRootHash(); Hash256 stateRoot = stateTree.RootHash; - stateTree.Commit(0, skipRoot); + stateTree.Commit(skipRoot); if (hasRoot) { diff --git a/src/Nethermind/Nethermind.State.Test/Proofs/AccountProofCollectorTests.cs b/src/Nethermind/Nethermind.State.Test/Proofs/AccountProofCollectorTests.cs index 2e82c29714a..503368f463a 100644 --- a/src/Nethermind/Nethermind.State.Test/Proofs/AccountProofCollectorTests.cs +++ b/src/Nethermind/Nethermind.State.Test/Proofs/AccountProofCollectorTests.cs @@ -53,7 +53,7 @@ public void Non_existing_account_is_valid_on_non_empty_tree_with_branch_without_ Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressC, new UInt256[] { 1, 2, 3 }); tree.Accept(accountProofCollector, tree.RootHash); @@ -75,7 +75,7 @@ public void Non_existing_account_is_valid_even_when_leaf_is_the_last_part_of_the Account account1 = Build.An.Account.WithBalance(1).TestObject; tree.Set(TestItem.AddressA, account1); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressC, new UInt256[] { 1, 2, 3 }); tree.Accept(accountProofCollector, tree.RootHash); @@ -106,7 +106,7 @@ public void Non_existing_account_is_valid_even_when_extension_on_the_way_is_not_ Account account = Build.An.Account.WithBalance(1).TestObject; tree.Set(c.AsSpan(), Rlp.Encode(account.WithChangedBalance(3))); tree.Set(d.AsSpan(), Rlp.Encode(account.WithChangedBalance(4))); - tree.Commit(0); + tree.Commit(); // now wer are looking for a trying to trick the code to think that the extension of c and d is a good match // if everything is ok the proof length of 1 is enough since the extension from the root is not matched @@ -128,7 +128,7 @@ public void Addresses_are_correct() Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA); tree.Accept(accountProofCollector, tree.RootHash); @@ -150,7 +150,7 @@ public void Balance_is_correct() Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA); tree.Accept(accountProofCollector, tree.RootHash); @@ -173,7 +173,7 @@ public void Code_hash_is_correct() Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA); tree.Accept(accountProofCollector, tree.RootHash); @@ -196,7 +196,7 @@ public void Nonce_is_correct() Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA); tree.Accept(accountProofCollector, tree.RootHash); @@ -219,7 +219,7 @@ public void Storage_root_is_correct() Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA); tree.Accept(accountProofCollector, tree.RootHash); @@ -242,7 +242,7 @@ public void Proof_path_is_filled() Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA); tree.Accept(accountProofCollector, tree.RootHash); @@ -260,7 +260,7 @@ public void Storage_proofs_length_is_as_expected() Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA, new[] { Bytes.FromHexString("0x0000000000000000000000000000000000000000000000000000000000000000"), Bytes.FromHexString("0x0000000000000000000000000000000000000000000000000000000000000001") }); tree.Accept(accountProofCollector, tree.RootHash); @@ -277,14 +277,14 @@ public void Storage_proofs_have_values_set() StorageTree storageTree = new(trieStore.GetTrieStore(TestItem.AddressA.ToAccountPath), Keccak.EmptyTreeHash, LimboLogs.Instance); storageTree.Set(UInt256.Zero, Bytes.FromHexString("0xab12000000000000000000000000000000000000000000000000000000000000000000000000000000")); storageTree.Set(UInt256.One, Bytes.FromHexString("0xab34000000000000000000000000000000000000000000000000000000000000000000000000000000")); - storageTree.Commit(0); + storageTree.Commit(); byte[] code = new byte[] { 1, 2, 3 }; Account account1 = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA, new[] { Bytes.FromHexString("0x0000000000000000000000000000000000000000000000000000000000000000"), Bytes.FromHexString("0x0000000000000000000000000000000000000000000000000000000000000001") }); tree.Accept(accountProofCollector, tree.RootHash); @@ -302,14 +302,14 @@ public void Storage_proofs_have_keys_set() StorageTree storageTree = new(trieStore.GetTrieStore(TestItem.AddressA.ToAccountPath), Keccak.EmptyTreeHash, LimboLogs.Instance); storageTree.Set(UInt256.Zero, Bytes.FromHexString("0xab12000000000000000000000000000000000000000000000000000000000000000000000000000000")); storageTree.Set(UInt256.One, Bytes.FromHexString("0xab34000000000000000000000000000000000000000000000000000000000000000000000000000000")); - storageTree.Commit(0); + storageTree.Commit(); byte[] code = new byte[] { 1, 2, 3 }; Account account1 = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); AccountProofCollector accountProofCollector = new(TestItem.AddressA, new[] { Bytes.FromHexString("0x0000000000000000000000000000000000000000000000000000000000000000"), Bytes.FromHexString("0x0000000000000000000000000000000000000000000000000000000000000001") }); tree.Accept(accountProofCollector, tree.RootHash); @@ -332,14 +332,14 @@ public void Storage_proofs_have_values_set_complex_setup() storageTree.Set(Keccak.Compute(a).Bytes, Rlp.Encode(Bytes.FromHexString("0xab12000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(b).Bytes, Rlp.Encode(Bytes.FromHexString("0xab34000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(c).Bytes, Rlp.Encode(Bytes.FromHexString("0xab56000000000000000000000000000000000000000000000000000000000000000000000000000000"))); - storageTree.Commit(0); + storageTree.Commit(); byte[] code = new byte[] { 1, 2, 3 }; Account account1 = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); TreeDumper dumper = new(); tree.Accept(dumper, tree.RootHash); @@ -371,14 +371,14 @@ public void Storage_proofs_have_values_set_complex_2_setup() storageTree.Set(Keccak.Compute(c).Bytes, Rlp.Encode(Bytes.FromHexString("0xab56000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(d).Bytes, Rlp.Encode(Bytes.FromHexString("0xab78000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(e).Bytes, Rlp.Encode(Bytes.FromHexString("0xab9a000000000000000000000000000000000000000000000000000000000000000000000000000000"))); - storageTree.Commit(0); + storageTree.Commit(); byte[] code = new byte[] { 1, 2, 3 }; Account account1 = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); TreeDumper dumper = new(); tree.Accept(dumper, tree.RootHash); @@ -412,14 +412,14 @@ public void Storage_proofs_have_values_set_complex_3_setup() storageTree.Set(Keccak.Compute(c).Bytes, Rlp.Encode(Bytes.FromHexString("0xab56000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(d).Bytes, Rlp.Encode(Bytes.FromHexString("0xab78000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(e).Bytes, Rlp.Encode(Bytes.FromHexString("0xab9a000000000000000000000000000000000000000000000000000000000000000000000000000000"))); - storageTree.Commit(0); + storageTree.Commit(); byte[] code = new byte[] { 1, 2, 3 }; Account account1 = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); TreeDumper dumper = new(); tree.Accept(dumper, tree.RootHash); @@ -451,14 +451,14 @@ public void Storage_proofs_when_values_are_missing_setup() storageTree.Set(Keccak.Compute(a).Bytes, Rlp.Encode(Bytes.FromHexString("0xab12000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(c).Bytes, Rlp.Encode(Bytes.FromHexString("0xab56000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(e).Bytes, Rlp.Encode(Bytes.FromHexString("0xab9a000000000000000000000000000000000000000000000000000000000000000000000000000000"))); - storageTree.Commit(0); + storageTree.Commit(); byte[] code = new byte[] { 1, 2, 3 }; Account account1 = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); TreeDumper dumper = new(); tree.Accept(dumper, tree.RootHash); @@ -486,7 +486,7 @@ public void Shows_empty_values_when_account_is_missing() byte[] code = new byte[] { 1, 2, 3 }; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); TreeDumper dumper = new(); tree.Accept(dumper, tree.RootHash); @@ -519,14 +519,14 @@ public void Storage_proofs_have_values_set_selective_setup() storageTree.Set(Keccak.Compute(c).Bytes, Rlp.Encode(Bytes.FromHexString("0xab56000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(d).Bytes, Rlp.Encode(Bytes.FromHexString("0xab78000000000000000000000000000000000000000000000000000000000000000000000000000000"))); storageTree.Set(Keccak.Compute(e).Bytes, Rlp.Encode(Bytes.FromHexString("0xab9a000000000000000000000000000000000000000000000000000000000000000000000000000000"))); - storageTree.Commit(0); + storageTree.Commit(); byte[] code = new byte[] { 1, 2, 3 }; Account account1 = Build.An.Account.WithBalance(1).WithStorageRoot(storageTree.RootHash).TestObject; Account account2 = Build.An.Account.WithBalance(2).TestObject; tree.Set(TestItem.AddressA, account1); tree.Set(TestItem.AddressB, account2); - tree.Commit(0); + tree.Commit(); TreeDumper dumper = new(); tree.Accept(dumper, tree.RootHash); @@ -629,14 +629,14 @@ public void _Test_storage_failed_case(string historicallyFailingCase) TestContext.Out.WriteLine($"Set {Keccak.Compute(rawKey).Bytes.ToHexString()}"); storageTree.Set(addressWithStorage.StorageCells[j].Index, new byte[] { 1 }); storageTree.UpdateRootHash(); - storageTree.Commit(0); + storageTree.Commit(); } Account account = Build.An.Account.WithBalance((UInt256)accountIndex).WithStorageRoot(storageTree.RootHash).TestObject; tree.Set(addressWithStorage.Address, account); tree.UpdateRootHash(); - tree.Commit(0); + tree.Commit(); TreeDumper treeDumper = new(); tree.Accept(treeDumper, tree.RootHash); @@ -705,14 +705,14 @@ public void Chaotic_test() } storageTree.UpdateRootHash(); - storageTree.Commit(0); + storageTree.Commit(); account = account.WithChangedStorageRoot(storageTree.RootHash); tree.Set(addressesWithStorage[i].Address, account); } tree.UpdateRootHash(); - tree.Commit(0); + tree.Commit(); for (int i = 0; i < accountsCount; i++) { diff --git a/src/Nethermind/Nethermind.State.Test/SnapSync/RecreateStateFromAccountRangesTests.cs b/src/Nethermind/Nethermind.State.Test/SnapSync/RecreateStateFromAccountRangesTests.cs index ff3f2558ef5..0b0ce1d8b2b 100644 --- a/src/Nethermind/Nethermind.State.Test/SnapSync/RecreateStateFromAccountRangesTests.cs +++ b/src/Nethermind/Nethermind.State.Test/SnapSync/RecreateStateFromAccountRangesTests.cs @@ -93,7 +93,7 @@ public void Test01() tree.Set(TestItem.Tree.AccountsWithPaths[4].Path, TestItem.Tree.AccountsWithPaths[4].Account); tree.Set(TestItem.Tree.AccountsWithPaths[5].Path, TestItem.Tree.AccountsWithPaths[5].Account); - tree.Commit(0); + tree.Commit(); Assert.That(tree.RootHash, Is.EqualTo(_inputTree.RootHash)); Assert.That(db.Keys.Count, Is.EqualTo(6)); // we don't persist proof nodes (boundary nodes) diff --git a/src/Nethermind/Nethermind.State.Test/StateTreeTests.cs b/src/Nethermind/Nethermind.State.Test/StateTreeTests.cs index 70e0244ef4d..c333a35b5fa 100644 --- a/src/Nethermind/Nethermind.State.Test/StateTreeTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StateTreeTests.cs @@ -36,7 +36,7 @@ public void No_reads_when_setting_on_empty() tree.Set(TestItem.AddressA, _account0); tree.Set(TestItem.AddressB, _account0); tree.Set(TestItem.AddressC, _account0); - tree.Commit(0); + tree.Commit(); Assert.That(db.ReadsCount, Is.EqualTo(0), "reads"); } @@ -48,7 +48,7 @@ public void Minimal_writes_when_setting_on_empty() tree.Set(TestItem.AddressA, _account0); tree.Set(TestItem.AddressB, _account0); tree.Set(TestItem.AddressC, _account0); - tree.Commit(0); + tree.Commit(); Assert.That(db.WritesCount, Is.EqualTo(5), "writes"); // branch, branch, two leaves (one is stored as RLP) } @@ -60,7 +60,7 @@ public void Minimal_writes_when_setting_on_empty_scenario_2() tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb00000000"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), _account0); - tree.Commit(0); + tree.Commit(); Assert.That(db.WritesCount, Is.EqualTo(7), "writes"); // extension, branch, leaf, extension, branch, 2x same leaf Assert.That(Trie.Metrics.TreeNodeHashCalculations, Is.EqualTo(7), "hashes"); Assert.That(Trie.Metrics.TreeNodeRlpEncodings, Is.EqualTo(7), "encodings"); @@ -75,7 +75,7 @@ public void Minimal_writes_when_setting_on_empty_scenario_3() tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), null); - tree.Commit(0); + tree.Commit(); Assert.That(db.WritesCount, Is.EqualTo(4), "writes"); // extension, branch, 2x leaf Assert.That(Trie.Metrics.TreeNodeHashCalculations, Is.EqualTo(4), "hashes"); Assert.That(Trie.Metrics.TreeNodeRlpEncodings, Is.EqualTo(4), "encodings"); @@ -91,7 +91,7 @@ public void Minimal_writes_when_setting_on_empty_scenario_4() tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), _account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), null); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), null); - tree.Commit(0); + tree.Commit(); Assert.That(db.WritesCount, Is.EqualTo(1), "writes"); // extension, branch, 2x leaf Assert.That(Trie.Metrics.TreeNodeHashCalculations, Is.EqualTo(1), "hashes"); Assert.That(Trie.Metrics.TreeNodeRlpEncodings, Is.EqualTo(1), "encodings"); @@ -108,7 +108,7 @@ public void Minimal_writes_when_setting_on_empty_scenario_5() tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), null); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), null); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb00000000"), null); - tree.Commit(0); + tree.Commit(); Assert.That(db.WritesCount, Is.EqualTo(0), "writes"); // extension, branch, 2x leaf Assert.That(Trie.Metrics.TreeNodeHashCalculations, Is.EqualTo(0), "hashes"); Assert.That(Trie.Metrics.TreeNodeRlpEncodings, Is.EqualTo(0), "encodings"); @@ -127,7 +127,7 @@ public void Scenario_traverse_extension_read_full_match() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0xf99f1d3234bad8d63d818db36ff63eefc8916263e654db8b800d3bd03f6339a5")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0xf99f1d3234bad8d63d818db36ff63eefc8916263e654db8b800d3bd03f6339a5")); } @@ -143,7 +143,7 @@ public void Scenario_traverse_extension_read_missing() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0xf99f1d3234bad8d63d818db36ff63eefc8916263e654db8b800d3bd03f6339a5")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0xf99f1d3234bad8d63d818db36ff63eefc8916263e654db8b800d3bd03f6339a5")); } @@ -158,7 +158,7 @@ public void Scenario_traverse_extension_new_branching() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x543c960143a2a06b685d6b92f0c37000273e616bc23888521e7edf15ad06da46")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x543c960143a2a06b685d6b92f0c37000273e616bc23888521e7edf15ad06da46")); } @@ -174,7 +174,7 @@ public void Scenario_traverse_extension_delete_missing() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0xf99f1d3234bad8d63d818db36ff63eefc8916263e654db8b800d3bd03f6339a5")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0xf99f1d3234bad8d63d818db36ff63eefc8916263e654db8b800d3bd03f6339a5")); } @@ -191,7 +191,7 @@ public void Scenario_traverse_extension_create_new_extension() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x0918112fc898173562441709a2c1cbedb80d1aaecaeadf2f3e9492eeaa568c67")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x0918112fc898173562441709a2c1cbedb80d1aaecaeadf2f3e9492eeaa568c67")); } @@ -205,7 +205,7 @@ public void Scenario_traverse_leaf_update_new_value() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0xaa5c248d4b4b8c27a654296a8e0cc51131eb9011d9166fa0fca56a966489e169")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0xaa5c248d4b4b8c27a654296a8e0cc51131eb9011d9166fa0fca56a966489e169")); } @@ -219,7 +219,7 @@ public void Scenario_traverse_leaf_update_no_change() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x491fbb33aaff22c0a7ff68d5c81ec114dddf89d022ccdee838a0e9d6cd45cab4")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x491fbb33aaff22c0a7ff68d5c81ec114dddf89d022ccdee838a0e9d6cd45cab4")); } @@ -233,7 +233,7 @@ public void Scenario_traverse_leaf_read_matching_leaf() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")); } @@ -247,7 +247,7 @@ public void Scenario_traverse_leaf_delete_missing() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x491fbb33aaff22c0a7ff68d5c81ec114dddf89d022ccdee838a0e9d6cd45cab4")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x491fbb33aaff22c0a7ff68d5c81ec114dddf89d022ccdee838a0e9d6cd45cab4")); } @@ -261,7 +261,7 @@ public void Scenario_traverse_leaf_update_with_extension() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x215a4bab4cf2d5ebbaa59c82ae94c9707fcf4cc0ca1fe7e18f918e46db428ef9")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x215a4bab4cf2d5ebbaa59c82ae94c9707fcf4cc0ca1fe7e18f918e46db428ef9")); } @@ -276,7 +276,7 @@ public void Scenario_traverse_leaf_delete_matching_leaf() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x491fbb33aaff22c0a7ff68d5c81ec114dddf89d022ccdee838a0e9d6cd45cab4")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x491fbb33aaff22c0a7ff68d5c81ec114dddf89d022ccdee838a0e9d6cd45cab4")); } @@ -291,7 +291,7 @@ public void Scenario_traverse_leaf_read_missing() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x491fbb33aaff22c0a7ff68d5c81ec114dddf89d022ccdee838a0e9d6cd45cab4")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x491fbb33aaff22c0a7ff68d5c81ec114dddf89d022ccdee838a0e9d6cd45cab4")); } @@ -306,7 +306,7 @@ public void Scenario_traverse_branch_update_missing() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0xc063af0bd3dd88320bc852ff8452049c42fbc06d1a69661567bd427572824cbf")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0xc063af0bd3dd88320bc852ff8452049c42fbc06d1a69661567bd427572824cbf")); } @@ -322,7 +322,7 @@ public void Scenario_traverse_branch_read_missing() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x94a193704e99c219d9a21428eb37d6d2d71b3d2cea80c77ff0e201c0df70a283")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x94a193704e99c219d9a21428eb37d6d2d71b3d2cea80c77ff0e201c0df70a283")); } @@ -337,7 +337,7 @@ public void Scenario_traverse_branch_delete_missing() tree.UpdateRootHash(); Hash256 rootHash = tree.RootHash; Assert.That(rootHash.ToString(true), Is.EqualTo("0x94a193704e99c219d9a21428eb37d6d2d71b3d2cea80c77ff0e201c0df70a283")); - tree.Commit(0); + tree.Commit(); Assert.That(rootHash.ToString(true), Is.EqualTo("0x94a193704e99c219d9a21428eb37d6d2d71b3d2cea80c77ff0e201c0df70a283")); } @@ -349,7 +349,7 @@ public void Minimal_hashes_when_setting_on_empty() tree.Set(TestItem.AddressA, _account0); tree.Set(TestItem.AddressB, _account0); tree.Set(TestItem.AddressC, _account0); - tree.Commit(0); + tree.Commit(); Assert.That(Trie.Metrics.TreeNodeHashCalculations, Is.EqualTo(5), "hashes"); // branch, branch, three leaves } @@ -361,7 +361,7 @@ public void Minimal_encodings_when_setting_on_empty() tree.Set(TestItem.AddressA, _account0); tree.Set(TestItem.AddressB, _account0); tree.Set(TestItem.AddressC, _account0); - tree.Commit(0); + tree.Commit(); Assert.That(Trie.Metrics.TreeNodeRlpEncodings, Is.EqualTo(5), "encodings"); // branch, branch, three leaves } @@ -373,7 +373,7 @@ public void Zero_decodings_when_setting_on_empty() tree.Set(TestItem.AddressA, _account0); tree.Set(TestItem.AddressB, _account0); tree.Set(TestItem.AddressC, _account0); - tree.Commit(0); + tree.Commit(); Assert.That(Trie.Metrics.TreeNodeRlpDecodings, Is.EqualTo(0), "decodings"); } @@ -386,7 +386,7 @@ public void No_writes_on_continues_update() tree.Set(TestItem.AddressA, _account1); tree.Set(TestItem.AddressA, _account2); tree.Set(TestItem.AddressA, _account3); - tree.Commit(0); + tree.Commit(); Assert.That(db.WritesCount, Is.EqualTo(1), "writes"); // extension, branch, two leaves } @@ -397,11 +397,11 @@ public void No_writes_on_reverted_update() MemDb db = new(); StateTree tree = new(new TrieStore(db, LimboLogs.Instance).GetTrieStore(null), LimboLogs.Instance); tree.Set(TestItem.AddressA, _account0); - tree.Commit(0); + tree.Commit(); Assert.That(db.WritesCount, Is.EqualTo(1), "writes before"); // extension, branch, two leaves tree.Set(TestItem.AddressA, _account1); tree.Set(TestItem.AddressA, _account0); - tree.Commit(0); + tree.Commit(); Assert.That(db.WritesCount, Is.EqualTo(1), "writes after"); // extension, branch, two leaves } @@ -447,7 +447,7 @@ public void Can_ask_about_root_hash_without_when_emptied() tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb00000000"), null); tree.UpdateRootHash(); Assert.That(tree.RootHash, Is.EqualTo(PatriciaTree.EmptyTreeHash)); - tree.Commit(0); + tree.Commit(); Assert.That(tree.RootHash, Is.EqualTo(PatriciaTree.EmptyTreeHash)); } diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index af65595b304..27f814222f8 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -318,17 +319,36 @@ private void SaveToTree(HashSet toUpdateRoots, Change change) /// Commit persistent storage trees /// /// Current block number - public void CommitTrees(long blockNumber) + 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. + + using ArrayPoolList commitTask = new ArrayPoolList(_storages.Count); foreach (KeyValuePair storage in _storages) { if (!_toUpdateRoots.Contains(storage.Key)) { continue; } - storage.Value.Commit(blockNumber); + + if (blockCommitter.TryRequestConcurrencyQuota()) + { + commitTask.Add(Task.Factory.StartNew((ctx) => + { + StorageTree st = (StorageTree)ctx; + st.Commit(); + blockCommitter.ReturnConcurrencyQuota(); + }, storage.Value)); + } + else + { + storage.Value.Commit(); + } } + Task.WaitAll(commitTask.ToArray()); + _toUpdateRoots.Clear(); // only needed here as there is no control over cached storage size otherwise _storages.Clear(); diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index c4689da5d04..e8afceafeb8 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -850,14 +850,14 @@ public void Reset(bool resizeCollections = true) _needsStateRootUpdate = false; } - public void CommitTree(long blockNumber) + public void CommitTree() { if (_needsStateRootUpdate) { RecalculateStateRoot(); } - _tree.Commit(blockNumber); + _tree.Commit(); } public static void CommitBranch() diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index c18fb219381..8df066b9292 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -174,8 +174,11 @@ public void DecrementNonce(Address address, UInt256 delta) public void CommitTree(long blockNumber) { - _persistentStorageProvider.CommitTrees(blockNumber); - _stateProvider.CommitTree(blockNumber); + using (IBlockCommitter committer = _trieStore.BeginBlockCommit(blockNumber)) + { + _persistentStorageProvider.CommitTrees(committer); + _stateProvider.CommitTree(); + } _persistentStorageProvider.StateRoot = _stateProvider.StateRoot; } diff --git a/src/Nethermind/Nethermind.Synchronization.Test/DbTuner/SyncDbTunerTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/DbTuner/SyncDbTunerTests.cs index 8c7954d8b92..ffc2251face 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/DbTuner/SyncDbTunerTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/DbTuner/SyncDbTunerTests.cs @@ -53,15 +53,6 @@ public void Setup() _receiptDb); } - [TearDown] - public void TearDown() - { - _blockDb?.Dispose(); - _codeDb?.Dispose(); - _receiptDb?.Dispose(); - _stateDb?.Dispose(); - } - [Test] public void WhenSnapIsOn_TriggerStateDbTune() { diff --git a/src/Nethermind/Nethermind.Synchronization.Test/FastSync/StateSyncFeedHealingTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/FastSync/StateSyncFeedHealingTests.cs index 3c7a2ea1cb2..4c4c3f6a91f 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/FastSync/StateSyncFeedHealingTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/FastSync/StateSyncFeedHealingTests.cs @@ -71,7 +71,7 @@ public async Task HealBigSqueezedRandomTree() accounts[path] = account; } - dbContext.RemoteStateTree.Commit(0); + dbContext.RemoteStateTree.Commit(); int startingHashIndex = 0; int endHashIndex; @@ -115,7 +115,7 @@ public async Task HealBigSqueezedRandomTree() } } - dbContext.RemoteStateTree.Commit(blockNumber); + dbContext.RemoteStateTree.Commit(); } endHashIndex = startingHashIndex + 1000; diff --git a/src/Nethermind/Nethermind.Synchronization.Test/FastSync/StateSyncFeedTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/FastSync/StateSyncFeedTests.cs index 07505497bea..8450719c629 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/FastSync/StateSyncFeedTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/FastSync/StateSyncFeedTests.cs @@ -76,7 +76,7 @@ public async Task Big_test((string Name, Action Setu .WithChangedStorageRoot(SetStorage(dbContext.RemoteTrieStore, i, TestItem.Addresses[i]).RootHash)); dbContext.RemoteStateTree.UpdateRootHash(); - dbContext.RemoteStateTree.Commit(0); + dbContext.RemoteStateTree.Commit(); ctx.Feed.FallAsleep(); ctx.Pool.WakeUpAll(); @@ -94,7 +94,7 @@ public async Task Big_test((string Name, Action Setu .WithChangedStorageRoot(SetStorage(dbContext.RemoteTrieStore, (byte)(i % 7), TestItem.Addresses[i]).RootHash)); dbContext.RemoteStateTree.UpdateRootHash(); - dbContext.RemoteStateTree.Commit(0); + dbContext.RemoteStateTree.Commit(); ctx.Feed.FallAsleep(); @@ -193,7 +193,7 @@ public async Task When_saving_root_goes_asleep() { DbContext dbContext = new(_logger, _logManager); dbContext.RemoteStateTree.Set(TestItem.KeccakA, Build.An.Account.TestObject); - dbContext.RemoteStateTree.Commit(0); + dbContext.RemoteStateTree.Commit(); dbContext.CompareTrees("BEGIN"); @@ -234,7 +234,7 @@ public async Task Can_download_with_moving_target((string Name, Action(), - bestPeerStrategy, - new ChainSpec(), - stateReader, - LimboLogs.Instance); + ContainerBuilder builder = new ContainerBuilder() + .AddSingleton(nodeStorage) + .AddSingleton(MainnetSpecProvider.Instance) + .AddSingleton(_blockTree) + .AddSingleton(_receiptStorage) + .AddSingleton(_pool) + .AddSingleton(stats) + .AddSingleton(syncConfig) + .AddSingleton(Always.Valid) + .AddSingleton(Always.Valid) + .AddSingleton(pivot) + .AddSingleton(Substitute.For()) + .AddSingleton(bestPeerStrategy) + .AddSingleton(new ChainSpec()) + .AddSingleton(stateReader) + .AddSingleton(No.BeaconSync) + .AddSingleton(LimboLogs.Instance); + dbProvider.ConfigureServiceCollection(builder); + + builder.RegisterModule(new SynchronizerModule(syncConfig)); + + IContainer container = builder.Build(); + + _synchronizer = container.Resolve(); + _syncServer = new SyncServer( trieStore.TrieNodeRlpStore, _codeDb, @@ -94,7 +102,7 @@ public async Task Setup() Always.Valid, Always.Valid, _pool, - _synchronizer.SyncModeSelector, + container.Resolve(), quickConfig, Policy.FullGossip, MainnetSpecProvider.Instance, diff --git a/src/Nethermind/Nethermind.Synchronization.Test/ParallelSync/MultiSyncModeSelectorTests.Scenario.cs b/src/Nethermind/Nethermind.Synchronization.Test/ParallelSync/MultiSyncModeSelectorTests.Scenario.cs index f44d95d9c8c..87ab10fe7bd 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/ParallelSync/MultiSyncModeSelectorTests.Scenario.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/ParallelSync/MultiSyncModeSelectorTests.Scenario.cs @@ -145,7 +145,6 @@ public class ScenarioBuilder private readonly List _overwrites = new(); private readonly List _peers = new(); - private bool _needToWaitForHeaders; public ISyncPeerPool SyncPeerPool { get; set; } = null!; @@ -812,7 +811,7 @@ void Test() } TotalDifficultyBetterPeerStrategy bestPeerStrategy = new(LimboLogs.Instance); - MultiSyncModeSelector selector = new(SyncProgressResolver, SyncPeerPool, SyncConfig, BeaconSyncStrategy, bestPeerStrategy, LimboLogs.Instance, _needToWaitForHeaders); + MultiSyncModeSelector selector = new(SyncProgressResolver, SyncPeerPool, SyncConfig, BeaconSyncStrategy, bestPeerStrategy, LimboLogs.Instance); selector.Stop(); selector.Update(); selector.Current.Should().Be(syncMode); @@ -840,7 +839,7 @@ void Test() public ScenarioBuilder WhenConsensusRequiresToWaitForHeaders(bool needToWaitForHeaders) { - _needToWaitForHeaders = needToWaitForHeaders; + SyncConfig.NeedToWaitForHeader = needToWaitForHeaders; return this; } diff --git a/src/Nethermind/Nethermind.Synchronization.Test/RangeQueryVisitorTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/RangeQueryVisitorTests.cs index 1c2bd7be51f..d0316f5223e 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/RangeQueryVisitorTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/RangeQueryVisitorTests.cs @@ -74,7 +74,7 @@ public void AccountRangeFetchWithSparserTree() tree.Set(new Hash256("0400000000000000000000000000000000000000000000000000000000000000"), TestItem.GenerateRandomAccount()); tree.Set(new Hash256("0500000000000000000000000000000000000000000000000000000000000000"), TestItem.GenerateRandomAccount()); tree.UpdateRootHash(); - tree.Commit(0); + tree.Commit(); var startHash = new Hash256("0150000000000000000000000000000000000000000000000000000000000000"); var limitHash = new Hash256("0350000000000000000000000000000000000000000000000000000000000000"); @@ -98,7 +98,7 @@ public void AccountRangeFetch_AfterTree() tree.Set(new Hash256("0400000000000000000000000000000000000000000000000000000000000000"), TestItem.GenerateRandomAccount()); tree.Set(new Hash256("0500000000000000000000000000000000000000000000000000000000000000"), TestItem.GenerateRandomAccount()); tree.UpdateRootHash(); - tree.Commit(0); + tree.Commit(); var startHash = new Hash256("0510000000000000000000000000000000000000000000000000000000000000"); var limitHash = new Hash256("0600000000000000000000000000000000000000000000000000000000000000"); @@ -127,7 +127,7 @@ public void RangeFetchPartialLimit() stateTree.Set(new Hash256("0x6000000000000000000000000000000000000000000000000000000000000000"), TestItem.GenerateRandomAccount()); stateTree.Set(new Hash256("0x7000000000000000000000000000000000000000000000000000000000000000"), TestItem.GenerateRandomAccount()); stateTree.Set(new Hash256("0x8000000000000000000000000000000000000000000000000000000000000000"), TestItem.GenerateRandomAccount()); - stateTree.Commit(0); + stateTree.Commit(); var startHash = new Hash256("0x3000000000000000000000000000000000000000000000000000000000000000"); var limitHash = new Hash256("0x4500000000000000000000000000000000000000000000000000000000000000"); @@ -163,7 +163,7 @@ public void RangeFetchPartialLimit_FarProof() { stateTree.Set(new Hash256(path), TestItem.GenerateRandomAccount(random)); } - stateTree.Commit(0); + stateTree.Commit(); var startHash = new Hash256("0x1140000000000000000000000000000000000000000000000000000000000000"); var limitHash = new Hash256("0x1235000000000000000000000000000000000000000000000000000000000000"); diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SnapSync/RecreateStateFromAccountRangesTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SnapSync/RecreateStateFromAccountRangesTests.cs index cdb1aad7a51..21924e7e150 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SnapSync/RecreateStateFromAccountRangesTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SnapSync/RecreateStateFromAccountRangesTests.cs @@ -99,7 +99,7 @@ public void Test01() tree.Set(TestItem.Tree.AccountsWithPaths[4].Path, TestItem.Tree.AccountsWithPaths[4].Account); tree.Set(TestItem.Tree.AccountsWithPaths[5].Path, TestItem.Tree.AccountsWithPaths[5].Account); - tree.Commit(0); + tree.Commit(); Assert.That(tree.RootHash, Is.EqualTo(_inputTree.RootHash)); Assert.That(db.Keys.Count, Is.EqualTo(6)); // we don't persist proof nodes (boundary nodes) @@ -370,7 +370,7 @@ public void CorrectlyDetermineMaxKeccakExist() tree.Set(ac1.Path, ac1.Account); tree.Set(ac2.Path, ac2.Account); tree.Set(ac3.Path, ac3.Account); - tree.Commit(0); + tree.Commit(); Hash256 rootHash = tree.RootHash; // "0x8c81279168edc449089449bc0f2136fc72c9645642845755633cf259cd97988b" diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SnapSync/SnapServerTest.cs b/src/Nethermind/Nethermind.Synchronization.Test/SnapSync/SnapServerTest.cs index 3907b0237c5..613de534190 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SnapSync/SnapServerTest.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SnapSync/SnapServerTest.cs @@ -342,7 +342,7 @@ public void TestWithHugeTree() { stateTree.Set(TestItem.GetRandomAddress(), TestItem.GenerateRandomAccount()); } - stateTree.Commit(0); + stateTree.Commit(); List accountWithStorage = new(); for (int i = 1000; i < 10000; i += 1000) @@ -353,12 +353,12 @@ public void TestWithHugeTree() { storageTree.Set(TestItem.GetRandomKeccak(), TestItem.GetRandomKeccak().Bytes.ToArray()); } - storageTree.Commit(1); + storageTree.Commit(); var account = TestItem.GenerateRandomAccount().WithChangedStorageRoot(storageTree.RootHash); stateTree.Set(address, account); accountWithStorage.Add(new PathWithAccount() { Path = Keccak.Compute(address.Bytes), Account = account }); } - stateTree.Commit(1); + stateTree.Commit(); SnapServer server = new(store.AsReadOnly(), codeDb, CreateConstantStateRootTracker(true), LimboLogs.Instance); diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs index 95cde2d1a4e..5d48d0a61dc 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs @@ -702,10 +702,13 @@ public void GetNodeData_returns_cached_trie_nodes() Hash256 nodeKey = TestItem.KeccakA; TrieNode node = new(NodeType.Leaf, nodeKey, TestItem.KeccakB.Bytes); IScopedTrieStore scopedTrieStore = trieStore.GetTrieStore(null); - using (ICommitter committer = scopedTrieStore.BeginCommit(TrieType.State, 1, node)) + using (IBlockCommitter _ = trieStore.BeginBlockCommit(1)) { - TreePath path = TreePath.Empty; - committer.CommitNode(ref path, new NodeCommitInfo(node)); + using (ICommitter committer = scopedTrieStore.BeginCommit(node)) + { + TreePath path = TreePath.Empty; + committer.CommitNode(ref path, new NodeCommitInfo(node)); + } } stateDb.KeyExists(nodeKey).Should().BeFalse(); diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs index 629432766c4..ada2c15efb3 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs @@ -5,6 +5,9 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Nethermind.Blockchain; using Nethermind.Blockchain.BeaconBlockRoot; using Nethermind.Blockchain.Blocks; @@ -42,6 +45,7 @@ using BlockTree = Nethermind.Blockchain.BlockTree; using Nethermind.Synchronization.SnapSync; using Nethermind.Config; +using Nethermind.Core.Specs; using Nethermind.Specs.ChainSpecStyle; using Nethermind.Trie; @@ -356,28 +360,32 @@ private SyncTestContext CreateSyncManager(int index) TotalDifficultyBetterPeerStrategy bestPeerStrategy = new(LimboLogs.Instance); Pivot pivot = new(syncConfig); - BlockDownloaderFactory blockDownloaderFactory = new( - MainnetSpecProvider.Instance, - blockValidator, - sealValidator, - new TotalDifficultyBetterPeerStrategy(LimboLogs.Instance), - logManager); - Synchronizer synchronizer = new( - dbProvider, - new NodeStorage(dbProvider.StateDb), - MainnetSpecProvider.Instance, - tree, - NullReceiptStorage.Instance, - syncPeerPool, - nodeStatsManager, - syncConfig, - blockDownloaderFactory, - pivot, - Substitute.For(), - bestPeerStrategy, - new ChainSpec(), - stateReader, - logManager); + + ContainerBuilder builder = new ContainerBuilder(); + builder + .AddSingleton(dbProvider) + .AddSingleton(new NodeStorage(dbProvider.StateDb)) + .AddSingleton(MainnetSpecProvider.Instance) + .AddSingleton(tree) + .AddSingleton(NullReceiptStorage.Instance) + .AddSingleton(syncPeerPool) + .AddSingleton(nodeStatsManager) + .AddSingleton(syncConfig) + .AddSingleton(blockValidator) + .AddSingleton(sealValidator) + .AddSingleton(pivot) + .AddSingleton(Substitute.For()) + .AddSingleton(bestPeerStrategy) + .AddSingleton(new ChainSpec()) + .AddSingleton(stateReader) + .AddSingleton(receiptStorage) + .AddSingleton(No.BeaconSync) + .AddSingleton(logManager); + dbProvider.ConfigureServiceCollection(builder); + builder.RegisterModule(new SynchronizerModule(syncConfig)); + IContainer container = builder.Build(); + + Synchronizer synchronizer = container.Resolve(); ISyncModeSelector selector = synchronizer.SyncModeSelector; SyncServer syncServer = new( diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerTests.cs index 872499cf73f..47c721d274c 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerTests.cs @@ -8,6 +8,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Nethermind.Blockchain; using Nethermind.Blockchain.Receipts; using Nethermind.Blockchain.Synchronization; @@ -17,6 +20,7 @@ using Nethermind.Core; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Core.Timers; using Nethermind.Db; @@ -333,74 +337,48 @@ ISyncConfig GetSyncConfig() => : totalDifficultyBetterPeerStrategy; StateReader reader = new StateReader(trieStore, codeDb, LimboLogs.Instance); - FullStateFinder fullStateFinder = new FullStateFinder(BlockTree, reader); INodeStorage nodeStorage = new NodeStorage(dbProvider.StateDb); SyncPeerPool = new SyncPeerPool(BlockTree, stats, bestPeerStrategy, _logManager, 25); Pivot pivot = new(syncConfig); IInvalidChainTracker invalidChainTracker = new NoopInvalidChainTracker(); + + ContainerBuilder builder = new ContainerBuilder(); + dbProvider.ConfigureServiceCollection(builder); + + builder + .AddSingleton(dbProvider) + .AddSingleton(nodeStorage) + .AddSingleton(MainnetSpecProvider.Instance) + .AddSingleton(BlockTree) + .AddSingleton(NullReceiptStorage.Instance) + .AddSingleton(SyncPeerPool) + .AddSingleton(stats) + .AddSingleton(syncConfig) + .AddSingleton(pivot) + .AddSingleton(poSSwitcher) + .AddSingleton(mergeConfig) + .AddSingleton(invalidChainTracker) + .AddSingleton(Substitute.For()) + .AddSingleton(bestPeerStrategy) + .AddSingleton(new ChainSpec()) + .AddSingleton(No.BeaconSync) + .AddSingleton(reader) + .AddSingleton(Always.Valid) + .AddSingleton(Always.Valid) + .AddSingleton(beaconPivot) + .AddSingleton(_logManager); + + builder.RegisterModule(new SynchronizerModule(syncConfig)); + if (IsMerge(synchronizerType)) { - IBlockDownloaderFactory blockDownloaderFactory = new MergeBlockDownloaderFactory( - poSSwitcher, - beaconPivot, - MainnetSpecProvider.Instance, - Always.Valid, - Always.Valid, - syncConfig, - bestPeerStrategy, - fullStateFinder, - _logManager - ); - Synchronizer = new MergeSynchronizer( - dbProvider, - nodeStorage, - MainnetSpecProvider.Instance, - BlockTree, - NullReceiptStorage.Instance, - SyncPeerPool, - stats, - syncConfig, - blockDownloaderFactory, - pivot, - poSSwitcher, - mergeConfig, - invalidChainTracker, - Substitute.For(), - bestPeerStrategy, - new ChainSpec(), - No.BeaconSync, - reader, - _logManager); - } - else - { - IBlockDownloaderFactory blockDownloaderFactory = new BlockDownloaderFactory( - MainnetSpecProvider.Instance, - Always.Valid, - Always.Valid, - new TotalDifficultyBetterPeerStrategy(_logManager), - _logManager); - - Synchronizer = new Synchronizer( - dbProvider, - nodeStorage, - MainnetSpecProvider.Instance, - BlockTree, - NullReceiptStorage.Instance, - SyncPeerPool, - stats, - syncConfig, - blockDownloaderFactory, - pivot, - Substitute.For(), - bestPeerStrategy, - new ChainSpec(), - reader, - _logManager); + builder.RegisterModule(new MergeSynchronizerModule()); } + IContainer container = builder.Build(); + Synchronizer = container.Resolve(); SyncServer = new SyncServer( trieStore.TrieNodeRlpStore, codeDb, @@ -409,7 +387,7 @@ ISyncConfig GetSyncConfig() => Always.Valid, Always.Valid, SyncPeerPool, - Synchronizer.SyncModeSelector, + container.Resolve(), syncConfig, Policy.FullGossip, MainnetSpecProvider.Instance, diff --git a/src/Nethermind/Nethermind.Synchronization.Test/TrieScenarios.cs b/src/Nethermind/Nethermind.Synchronization.Test/TrieScenarios.cs index 5d3c8cde41d..5f8d57a1820 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/TrieScenarios.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/TrieScenarios.cs @@ -69,7 +69,7 @@ private static (string Name, Action Action)[] InitSc ("empty", (tree, _, codeDb) => { codeDb[Keccak.Compute(Code0).Bytes] = Code0; - tree.Commit(0); + tree.Commit(); }), ("set_3_via_address", (tree, stateDb, codeDb) => { @@ -77,7 +77,7 @@ private static (string Name, Action Action)[] InitSc SetStorage(tree, stateDb, TestItem.AddressA, Account0); SetStorage(tree, stateDb, TestItem.AddressB, Account0); SetStorage(tree, stateDb, TestItem.AddressC, Account0); - tree.Commit(0); + tree.Commit(); }), ("storage_hash_and_code_hash_same", (tree, stateDb, codeDb) => { @@ -86,11 +86,11 @@ private static (string Name, Action Action)[] InitSc Hash256 account = new Hash256("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); StorageTree remoteStorageTree = new(stateDb.GetTrieStore(account), Keccak.EmptyTreeHash, LimboLogs.Instance); remoteStorageTree.Set((UInt256) 1, new byte[] {1}); - remoteStorageTree.Commit(0); + remoteStorageTree.Commit(); remoteStorageTree.UpdateRootHash(); codeDb[codeHash.Bytes] = code; tree.Set(account, AccountJustState0.WithChangedStorageRoot(remoteStorageTree.RootHash).WithChangedCodeHash(codeHash)); - tree.Commit(0); + tree.Commit(); }), ("storage_hash_and_code_hash_same_with_additional_account_of_same_storage_root", (tree, stateDb, codeDb) => { @@ -100,19 +100,19 @@ private static (string Name, Action Action)[] InitSc Hash256 account1 = new Hash256("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); StorageTree remoteStorageTree1 = new(stateDb.GetTrieStore(account1), Keccak.EmptyTreeHash, LimboLogs.Instance); remoteStorageTree1.Set((UInt256) 1, new byte[] {1}); - remoteStorageTree1.Commit(0); + remoteStorageTree1.Commit(); remoteStorageTree1.UpdateRootHash(); Hash256 account2 = new Hash256("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); StorageTree remoteStorageTree2 = new(stateDb.GetTrieStore(account2), Keccak.EmptyTreeHash, LimboLogs.Instance); remoteStorageTree2.Set((UInt256) 1, new byte[] {1}); - remoteStorageTree2.Commit(0); + remoteStorageTree2.Commit(); remoteStorageTree2.UpdateRootHash(); codeDb[codeHash.Bytes] = code; tree.Set(account1, AccountJustState0.WithChangedStorageRoot(remoteStorageTree1.RootHash)); tree.Set(account2, AccountJustState0.WithChangedStorageRoot(remoteStorageTree2.RootHash).WithChangedCodeHash(codeHash)); - tree.Commit(0); + tree.Commit(); }), ("storage_hash_and_code_hash_same_with_additional_account_of_same_code", (tree, stateDb, codeDb) => { @@ -123,20 +123,20 @@ private static (string Name, Action Action)[] InitSc new Hash256("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); StorageTree remoteStorageTree = new(stateDb.GetTrieStore(accountWithStorage), Keccak.EmptyTreeHash, LimboLogs.Instance); remoteStorageTree.Set((UInt256) 1, new byte[] {1}); - remoteStorageTree.Commit(0); + remoteStorageTree.Commit(); remoteStorageTree.UpdateRootHash(); codeDb[codeHash.Bytes] = code; tree.Set(new Hash256("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), AccountJustState0.WithChangedCodeHash(codeHash)); tree.Set(accountWithStorage, AccountJustState0.WithChangedStorageRoot(remoteStorageTree.RootHash).WithChangedCodeHash(codeHash)); - tree.Commit(0); + tree.Commit(); }), ("branch_with_same_accounts_at_different_addresses", (tree, _, codeDb) => { codeDb[Keccak.Compute(Code0).Bytes] = Code0; tree.Set(new Hash256("1baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), AccountJustState0); tree.Set(new Hash256("2baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), AccountJustState0); - tree.Commit(0); + tree.Commit(); }), ("set_3_delete_1", (tree, stateDb, codeDb) => { @@ -145,7 +145,7 @@ private static (string Name, Action Action)[] InitSc SetStorage(tree, stateDb, new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), Account0); SetStorage(tree, stateDb, new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), Account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), null); - tree.Commit(0); + tree.Commit(); }), ("set_3_delete_2", (tree, stateDb, codeDb) => { @@ -155,7 +155,7 @@ private static (string Name, Action Action)[] InitSc SetStorage(tree, stateDb, new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), Account0); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), null); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), null); - tree.Commit(0); + tree.Commit(); }), ("set_3_delete_all", (tree, _, _) => { @@ -166,7 +166,7 @@ private static (string Name, Action Action)[] InitSc tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb0"), null); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb1eeeeeb1"), null); tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb00000000"), null); - tree.Commit(0); + tree.Commit(); }), ("extension_read_full_match", (tree, stateDb, codeDb) => { @@ -177,7 +177,7 @@ private static (string Name, Action Action)[] InitSc Account _ = tree.Get(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb11111111"))!; tree.UpdateRootHash(); Hash256 __ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("extension_read_missing", (tree, stateDb, codeDb) => { @@ -188,7 +188,7 @@ private static (string Name, Action Action)[] InitSc Account _ = tree.Get(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddd"))!; tree.UpdateRootHash(); Hash256 __ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("extension_new_branch", (tree, stateDb, codeDb) => { @@ -200,7 +200,7 @@ private static (string Name, Action Action)[] InitSc SetStorage(tree, stateDb, new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddd"), Account2); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("just_state", (tree, _, _) => { @@ -209,7 +209,7 @@ private static (string Name, Action Action)[] InitSc tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddd"), AccountJustState2); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("extension_delete_missing", (tree, stateDb, codeDb) => { @@ -220,7 +220,7 @@ private static (string Name, Action Action)[] InitSc tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddd"), null); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("extension_create_new_extension", (tree, stateDb, codeDb) => { @@ -234,7 +234,7 @@ private static (string Name, Action Action)[] InitSc SetStorage(tree, stateDb, new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeaaaaaaaaaaaaaaaab11111111"), Account3); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("leaf_new_value", (tree, stateDb, codeDb) => { @@ -243,7 +243,7 @@ private static (string Name, Action Action)[] InitSc SetStorage(tree, stateDb, new Hash256("1111111111111111111111111111111111111111111111111111111111111111"), Account1); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("leaf_no_change", (tree, stateDb, codeDb) => { @@ -252,7 +252,7 @@ private static (string Name, Action Action)[] InitSc SetStorage(tree, stateDb, new Hash256("1111111111111111111111111111111111111111111111111111111111111111"), Account0); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("leaf_delete", (tree, _, _) => { @@ -260,7 +260,7 @@ private static (string Name, Action Action)[] InitSc tree.Set(new Hash256("1111111111111111111111111111111111111111111111111111111111111111"), null); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("leaf_delete_missing", (tree, stateDb, codeDb) => { @@ -269,7 +269,7 @@ private static (string Name, Action Action)[] InitSc tree.Set(new Hash256("1111111111111111111111111111111ddddddddddddddddddddddddddddddddd"), null); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("leaf_update_extension", (tree, stateDb, codeDb) => { @@ -279,7 +279,7 @@ private static (string Name, Action Action)[] InitSc SetStorage(tree, stateDb, new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb00000000000000000000000000000000"), Account1); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("leaf_read", (tree, stateDb, codeDb) => { @@ -288,7 +288,7 @@ private static (string Name, Action Action)[] InitSc Account _ = tree.Get(new Hash256("1111111111111111111111111111111111111111111111111111111111111111"))!; tree.UpdateRootHash(); Hash256 __ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("leaf_update_missing", (tree, stateDb, codeDb) => { @@ -297,7 +297,7 @@ private static (string Name, Action Action)[] InitSc Account _ = tree.Get(new Hash256("111111111111111111111111111111111111111111111111111111111ddddddd"))!; tree.UpdateRootHash(); Hash256 __ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("branch_update_missing", (tree, stateDb, codeDb) => { @@ -309,7 +309,7 @@ private static (string Name, Action Action)[] InitSc SetStorage(tree, stateDb, new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb22222"), Account2); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("branch_read_missing", (tree, stateDb, codeDb) => { @@ -320,7 +320,7 @@ private static (string Name, Action Action)[] InitSc Account _ = tree.Get(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb22222"))!; tree.UpdateRootHash(); Hash256 __ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }), ("branch_delete_missing", (tree, stateDb, codeDb) => { @@ -331,7 +331,7 @@ private static (string Name, Action Action)[] InitSc tree.Set(new Hash256("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeb22222"), null); tree.UpdateRootHash(); Hash256 _ = tree.RootHash; - tree.Commit(0); + tree.Commit(); }) }; } @@ -354,7 +354,7 @@ private static StorageTree SetStorage(ITrieStore trieStore, Hash256 account) remoteStorageTree.Set((UInt256)1007, new byte[] { 7 }); remoteStorageTree.Set((UInt256)1008, new byte[] { 8 }); - remoteStorageTree.Commit(0); + remoteStorageTree.Commit(); return remoteStorageTree; } diff --git a/src/Nethermind/Nethermind.Synchronization/Blocks/IBlockDownloaderFactory.cs b/src/Nethermind/Nethermind.Synchronization/Blocks/IBlockDownloaderFactory.cs deleted file mode 100644 index 64afb7b1061..00000000000 --- a/src/Nethermind/Nethermind.Synchronization/Blocks/IBlockDownloaderFactory.cs +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using Nethermind.Blockchain; -using Nethermind.Blockchain.Receipts; -using Nethermind.Consensus; -using Nethermind.Consensus.Validators; -using Nethermind.Core.Specs; -using Nethermind.Logging; -using Nethermind.Synchronization.ParallelSync; -using Nethermind.Synchronization.Peers; -using Nethermind.Synchronization.Reporting; - -namespace Nethermind.Synchronization.Blocks -{ - public class BlockDownloaderFactory : IBlockDownloaderFactory - { - private readonly ISpecProvider _specProvider; - private readonly IBlockValidator _blockValidator; - private readonly ISealValidator _sealValidator; - private readonly IBetterPeerStrategy _betterPeerStrategy; - private readonly ILogManager _logManager; - - public BlockDownloaderFactory( - ISpecProvider specProvider, - IBlockValidator blockValidator, - ISealValidator sealValidator, - IBetterPeerStrategy betterPeerStrategy, - ILogManager logManager) - { - _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); - _blockValidator = blockValidator ?? throw new ArgumentNullException(nameof(blockValidator)); - _sealValidator = sealValidator ?? throw new ArgumentNullException(nameof(sealValidator)); - _betterPeerStrategy = betterPeerStrategy ?? throw new ArgumentNullException(nameof(betterPeerStrategy)); - _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); - } - - public BlockDownloader Create(ISyncFeed syncFeed, IBlockTree blockTree, IReceiptStorage receiptStorage, ISyncPeerPool peerPool, ISyncReport syncReport) - { - return new( - syncFeed, - peerPool, - blockTree, - _blockValidator, - _sealValidator, - syncReport, - receiptStorage, - _specProvider, - _betterPeerStrategy, - _logManager); - } - - public IPeerAllocationStrategyFactory CreateAllocationStrategyFactory() - { - return new BlocksSyncPeerAllocationStrategyFactory(); - } - } - - public interface IBlockDownloaderFactory - { - BlockDownloader Create(ISyncFeed syncFeed, IBlockTree blockTree, IReceiptStorage receiptStorage, ISyncPeerPool syncPeerPool, ISyncReport syncReport); - IPeerAllocationStrategyFactory CreateAllocationStrategyFactory(); - } -} diff --git a/src/Nethermind/Nethermind.Synchronization/DbTuner/SyncDbOptimizer.cs b/src/Nethermind/Nethermind.Synchronization/DbTuner/SyncDbOptimizer.cs index e9f5c7c10d5..13a8ecbb4a6 100644 --- a/src/Nethermind/Nethermind.Synchronization/DbTuner/SyncDbOptimizer.cs +++ b/src/Nethermind/Nethermind.Synchronization/DbTuner/SyncDbOptimizer.cs @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Autofac.Features.AttributeFilters; +using Microsoft.Extensions.DependencyInjection; using Nethermind.Blockchain.Synchronization; using Nethermind.Db; using Nethermind.Synchronization.FastBlocks; @@ -24,26 +26,32 @@ public SyncDbTuner( ISyncFeed? snapSyncFeed, ISyncFeed? bodiesSyncFeed, ISyncFeed? receiptSyncFeed, - ITunableDb? stateDb, - ITunableDb? codeDb, - ITunableDb? blockDb, - ITunableDb? receiptDb + [KeyFilter(DbNames.State)] ITunableDb? stateDb, + [KeyFilter(DbNames.Code)] ITunableDb? codeDb, + [KeyFilter(DbNames.Blocks)] ITunableDb? blockDb, + [KeyFilter(DbNames.Receipts)] ITunableDb? receiptDb ) { + if (syncConfig.TuneDbMode == ITunableDb.TuneType.Default && syncConfig.BlocksDbTuneDbMode == ITunableDb.TuneType.Default) + { + // Do nothing. + return; + } + // Only these three make sense as they are write heavy // Headers is used everywhere, so slowing read might slow the whole sync. // Statesync is read heavy, Forward sync is just plain too slow to saturate IO. - if (snapSyncFeed is not null) + if (snapSyncFeed is not NoopSyncFeed) { snapSyncFeed.StateChanged += SnapStateChanged; } - if (bodiesSyncFeed is not null) + if (bodiesSyncFeed is not NoopSyncFeed) { bodiesSyncFeed.StateChanged += BodiesStateChanged; } - if (receiptSyncFeed is not null) + if (receiptSyncFeed is not NoopSyncFeed) { receiptSyncFeed.StateChanged += ReceiptsStateChanged; } diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs index 9b972b68d67..1152f416054 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs @@ -4,6 +4,8 @@ using System; using System.Threading; using System.Threading.Tasks; +using Autofac.Features.AttributeFilters; +using Microsoft.Extensions.DependencyInjection; using Nethermind.Blockchain; using Nethermind.Blockchain.Synchronization; using Nethermind.Consensus.Validators; @@ -50,8 +52,8 @@ public BodiesSyncFeed( ISyncPeerPool syncPeerPool, ISyncConfig syncConfig, ISyncReport syncReport, - IDbMeta blocksDb, - IDb metadataDb, + [KeyFilter(DbNames.Blocks)] IDbMeta blocksDb, + [KeyFilter(DbNames.Metadata)] IDb metadataDb, ILogManager logManager, long flushDbInterval = DefaultFlushDbInterval) : base(metadataDb, specProvider, logManager.GetClassLogger()) diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs index e621dc56795..cbfce46215c 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Autofac.Features.AttributeFilters; +using Microsoft.Extensions.DependencyInjection; using Nethermind.Blockchain; using Nethermind.Blockchain.Receipts; using Nethermind.Blockchain.Synchronization; @@ -57,7 +59,7 @@ public ReceiptsSyncFeed( ISyncPeerPool syncPeerPool, ISyncConfig syncConfig, ISyncReport syncReport, - IDb metadataDb, + [KeyFilter(DbNames.Metadata)] IDb metadataDb, ILogManager logManager) : base(metadataDb, specProvider, logManager?.GetClassLogger() ?? default) { diff --git a/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs b/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs index 0c4b7b35568..1abfe9643bb 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs @@ -8,6 +8,8 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Autofac.Features.AttributeFilters; +using Microsoft.Extensions.DependencyInjection; using Nethermind.Blockchain; using Nethermind.Core; using Nethermind.Core.Caching; @@ -74,6 +76,12 @@ public class TreeSync private long _blockNumber; private readonly SyncMode _syncMode; + public TreeSync([KeyFilter(DbNames.Code)] IDb codeDb, INodeStorage nodeStorage, IBlockTree blockTree, ILogManager logManager) + : this(SyncMode.StateNodes, codeDb, nodeStorage, blockTree, logManager) + { + + } + public TreeSync(SyncMode syncMode, IDb codeDb, INodeStorage nodeStorage, IBlockTree blockTree, ILogManager logManager) { _syncMode = syncMode; diff --git a/src/Nethermind/Nethermind.Synchronization/ISynchronizer.cs b/src/Nethermind/Nethermind.Synchronization/ISynchronizer.cs index 512b0a767a5..011659c5238 100644 --- a/src/Nethermind/Nethermind.Synchronization/ISynchronizer.cs +++ b/src/Nethermind/Nethermind.Synchronization/ISynchronizer.cs @@ -15,8 +15,5 @@ public interface ISynchronizer : IDisposable void Start(); Task StopAsync(); - - ISyncProgressResolver SyncProgressResolver { get; } - ISyncModeSelector SyncModeSelector { get; } } } diff --git a/src/Nethermind/Nethermind.Synchronization/MallocTrimmer.cs b/src/Nethermind/Nethermind.Synchronization/MallocTrimmer.cs index 63c6e85e36c..0010bc111db 100644 --- a/src/Nethermind/Nethermind.Synchronization/MallocTrimmer.cs +++ b/src/Nethermind/Nethermind.Synchronization/MallocTrimmer.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using Nethermind.Blockchain.Synchronization; using Nethermind.Core.Extensions; using Nethermind.Core.Memory; using Nethermind.Logging; @@ -16,6 +17,14 @@ public class MallocTrimmer private readonly MallocHelper _mallocHelper; private readonly ILogger _logger; + public MallocTrimmer( + ISyncModeSelector syncModeSelector, + ISyncConfig syncConfig, + ILogManager logManager + ) : this(syncModeSelector, TimeSpan.FromSeconds(syncConfig.MallocTrimIntervalSec), logManager) + { + } + public MallocTrimmer( ISyncModeSelector syncModeSelector, TimeSpan interval, diff --git a/src/Nethermind/Nethermind.Synchronization/ParallelSync/ISyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/ParallelSync/ISyncFeed.cs index 64ac3f4687e..1f87736fb08 100644 --- a/src/Nethermind/Nethermind.Synchronization/ParallelSync/ISyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/ParallelSync/ISyncFeed.cs @@ -30,6 +30,6 @@ public interface ISyncFeed /// Return true if not finished. May not run even if return true if MultiSyncModeSelector said no, probably /// because it's waiting for other sync or something. - bool IsFinished { get; } + bool IsFinished => false; } } diff --git a/src/Nethermind/Nethermind.Synchronization/ParallelSync/MultiSyncModeSelector.cs b/src/Nethermind/Nethermind.Synchronization/ParallelSync/MultiSyncModeSelector.cs index e9eff01f553..7cfc08deca8 100644 --- a/src/Nethermind/Nethermind.Synchronization/ParallelSync/MultiSyncModeSelector.cs +++ b/src/Nethermind/Nethermind.Synchronization/ParallelSync/MultiSyncModeSelector.cs @@ -8,8 +8,10 @@ using System.Threading; using System.Threading.Tasks; using Nethermind.Blockchain.Synchronization; +using Nethermind.Core; using Nethermind.Int256; using Nethermind.Logging; +using Nethermind.Specs.ChainSpecStyle; using Nethermind.State.Snap; using Nethermind.Synchronization.Peers; @@ -80,8 +82,7 @@ public MultiSyncModeSelector( ISyncConfig syncConfig, IBeaconSyncStrategy beaconSyncStrategy, IBetterPeerStrategy betterPeerStrategy, - ILogManager logManager, - bool needToWaitForHeaders = false) + ILogManager logManager) { _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _syncConfig = syncConfig ?? throw new ArgumentNullException(nameof(syncConfig)); @@ -89,7 +90,7 @@ public MultiSyncModeSelector( _syncPeerPool = syncPeerPool ?? throw new ArgumentNullException(nameof(syncPeerPool)); _betterPeerStrategy = betterPeerStrategy ?? throw new ArgumentNullException(nameof(betterPeerStrategy)); _syncProgressResolver = syncProgressResolver ?? throw new ArgumentNullException(nameof(syncProgressResolver)); - _needToWaitForHeaders = needToWaitForHeaders; + _needToWaitForHeaders = syncConfig.NeedToWaitForHeader; if (syncConfig.FastSyncCatchUpHeightDelta <= FastSyncLag) { diff --git a/src/Nethermind/Nethermind.Synchronization/ParallelSync/NoopSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/ParallelSync/NoopSyncFeed.cs new file mode 100644 index 00000000000..f04f755c736 --- /dev/null +++ b/src/Nethermind/Nethermind.Synchronization/ParallelSync/NoopSyncFeed.cs @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Threading; +using System.Threading.Tasks; +using Nethermind.Synchronization.Peers; + +namespace Nethermind.Synchronization.ParallelSync; + +public class NoopSyncFeed : ISyncFeed +{ + public SyncFeedState CurrentState { get; } + +#pragma warning disable CS0067 + public event EventHandler? StateChanged; +#pragma warning disable + + public Task PrepareRequest(CancellationToken token = default) + { + return Task.FromResult(default); + } + + public SyncResponseHandlingResult HandleResponse(T response, PeerInfo? peer = null) + { + return SyncResponseHandlingResult.NotAssigned; + } + + public bool IsMultiFeed { get; } + public AllocationContexts Contexts { get; } + public void Activate() + { + } + + public void Finish() + { + } + + public Task FeedTask => Task.CompletedTask; + public void SyncModeSelectorOnChanged(SyncMode current) + { + } + + public bool IsFinished { get; } = true; +} diff --git a/src/Nethermind/Nethermind.Synchronization/ParallelSync/SyncDispatcher.cs b/src/Nethermind/Nethermind.Synchronization/ParallelSync/SyncDispatcher.cs index 50c38f3384b..2bbdb3e50d4 100644 --- a/src/Nethermind/Nethermind.Synchronization/ParallelSync/SyncDispatcher.cs +++ b/src/Nethermind/Nethermind.Synchronization/ParallelSync/SyncDispatcher.cs @@ -4,6 +4,8 @@ using System; using System.Threading; using System.Threading.Tasks; +using Nethermind.Blockchain.Synchronization; +using Nethermind.Core; using Nethermind.Core.Exceptions; using Nethermind.Core.Extensions; using Nethermind.Logging; @@ -25,6 +27,18 @@ public class SyncDispatcher private readonly SemaphoreSlim _concurrentProcessingSemaphore; + public SyncDispatcher( + ISyncConfig syncConfig, + ISyncFeed? syncFeed, + ISyncDownloader? downloader, + ISyncPeerPool? syncPeerPool, + IPeerAllocationStrategyFactory? peerAllocationStrategy, + ILogManager? logManager) + : this(syncConfig.MaxProcessingThreads, syncFeed, downloader, syncPeerPool, peerAllocationStrategy, logManager) + { + + } + public SyncDispatcher( int maxNumberOfProcessingThread, ISyncFeed? syncFeed, diff --git a/src/Nethermind/Nethermind.Synchronization/ParallelSync/SyncProgressResolver.cs b/src/Nethermind/Nethermind.Synchronization/ParallelSync/SyncProgressResolver.cs index 73a325d3149..705611e0f79 100644 --- a/src/Nethermind/Nethermind.Synchronization/ParallelSync/SyncProgressResolver.cs +++ b/src/Nethermind/Nethermind.Synchronization/ParallelSync/SyncProgressResolver.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using Autofac.Features.AttributeFilters; using Nethermind.Blockchain; using Nethermind.Blockchain.Synchronization; using Nethermind.Core; @@ -23,19 +24,19 @@ public class SyncProgressResolver : ISyncProgressResolver // ReSharper disable once NotAccessedField.Local private readonly ILogger _logger; - private readonly ISyncFeed? _headersSyncFeed; - private readonly ISyncFeed? _bodiesSyncFeed; - private readonly ISyncFeed? _receiptsSyncFeed; - private readonly ISyncFeed? _snapSyncFeed; + private readonly ISyncFeed _headersSyncFeed; + private readonly ISyncFeed _bodiesSyncFeed; + private readonly ISyncFeed _receiptsSyncFeed; + private readonly ISyncFeed _snapSyncFeed; public SyncProgressResolver( IBlockTree blockTree, IFullStateFinder fullStateFinder, ISyncConfig syncConfig, - ISyncFeed? headersSyncFeed, - ISyncFeed? bodiesSyncFeed, - ISyncFeed? receiptsSyncFeed, - ISyncFeed? snapSyncFeed, + [KeyFilter(nameof(HeadersSyncFeed))] ISyncFeed headersSyncFeed, + ISyncFeed bodiesSyncFeed, + ISyncFeed receiptsSyncFeed, + ISyncFeed snapSyncFeed, ILogManager logManager) { _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); @@ -83,11 +84,11 @@ public long FindBestFullState() return _blockTree.FindHeader(blockHash)?.TotalDifficulty == 0 ? null : _blockTree.FindHeader(blockHash)?.TotalDifficulty; } - public bool IsFastBlocksHeadersFinished() => !IsFastBlocks() || !_syncConfig.DownloadHeadersInFastSync || _headersSyncFeed?.IsFinished == true; + public bool IsFastBlocksHeadersFinished() => !IsFastBlocks() || !_syncConfig.DownloadHeadersInFastSync || _headersSyncFeed.IsFinished; - public bool IsFastBlocksBodiesFinished() => !IsFastBlocks() || !_syncConfig.DownloadBodiesInFastSync || _bodiesSyncFeed?.IsFinished == true; + public bool IsFastBlocksBodiesFinished() => !IsFastBlocks() || !_syncConfig.DownloadBodiesInFastSync || _bodiesSyncFeed.IsFinished; - public bool IsFastBlocksReceiptsFinished() => !IsFastBlocks() || !_syncConfig.DownloadReceiptsInFastSync || _receiptsSyncFeed?.IsFinished == true; + public bool IsFastBlocksReceiptsFinished() => !IsFastBlocks() || !_syncConfig.DownloadReceiptsInFastSync || _receiptsSyncFeed.IsFinished; public bool IsSnapGetRangesFinished() => _snapSyncFeed?.IsFinished ?? true; diff --git a/src/Nethermind/Nethermind.Synchronization/SnapSync/ProgressTracker.cs b/src/Nethermind/Nethermind.Synchronization/SnapSync/ProgressTracker.cs index eda6709e66e..b6dba820c0c 100644 --- a/src/Nethermind/Nethermind.Synchronization/SnapSync/ProgressTracker.cs +++ b/src/Nethermind/Nethermind.Synchronization/SnapSync/ProgressTracker.cs @@ -8,7 +8,10 @@ using System.Linq; using System.Text; using System.Threading; +using Autofac.Features.AttributeFilters; +using Microsoft.Extensions.DependencyInjection; using Nethermind.Blockchain; +using Nethermind.Blockchain.Synchronization; using Nethermind.Core; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; @@ -58,6 +61,11 @@ public class ProgressTracker : IDisposable private readonly Pivot _pivot; + public ProgressTracker(IBlockTree blockTree, [KeyFilter(DbNames.State)] IDb db, ILogManager logManager, ISyncConfig syncConfig) + : this(blockTree, db, logManager, syncConfig.SnapSyncAccountRangePartitionCount) + { + } + public ProgressTracker(IBlockTree blockTree, IDb db, ILogManager logManager, int accountRangePartitionCount = 8) { _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); diff --git a/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProvider.cs b/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProvider.cs index afb19297673..51548bfa9f6 100644 --- a/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProvider.cs +++ b/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProvider.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading; +using Autofac.Features.AttributeFilters; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.ObjectPool; using Nethermind.Core; using Nethermind.Core.Caching; @@ -33,7 +35,7 @@ public class SnapProvider : ISnapProvider // This is actually close to 97% effective. private readonly ClockKeyCache _codeExistKeyCache = new(1024 * 16); - public SnapProvider(ProgressTracker progressTracker, IDb codeDb, INodeStorage nodeStorage, ILogManager logManager) + public SnapProvider(ProgressTracker progressTracker, [KeyFilter(DbNames.Code)] IDb codeDb, INodeStorage nodeStorage, ILogManager logManager) { _codeDb = codeDb ?? throw new ArgumentNullException(nameof(codeDb)); _progressTracker = progressTracker ?? throw new ArgumentNullException(nameof(progressTracker)); diff --git a/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProviderHelper.cs b/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProviderHelper.cs index ea9dfc1f70a..504d66fb884 100644 --- a/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProviderHelper.cs +++ b/src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProviderHelper.cs @@ -76,7 +76,7 @@ public static (AddRangeResult result, bool moreChildrenToRight, List +/// Container to make it simpler to get a bunch of components within a same named scoped. +/// +public class SyncFeedComponent(Lazy> feed, Lazy> dispatcher, Lazy> blockDownloader, ILifetimeScope lifetimeScope) : IDisposable, IAsyncDisposable +{ + public ISyncFeed Feed => feed.Value; + public SyncDispatcher Dispatcher => dispatcher.Value; + public BlockDownloader BlockDownloader => (BlockDownloader)blockDownloader.Value; + + public void Dispose() + { + lifetimeScope.Dispose(); + } + + public async ValueTask DisposeAsync() + { + await lifetimeScope.DisposeAsync(); + } +} diff --git a/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs b/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs index ff69883e6f6..826406ca9de 100644 --- a/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs +++ b/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs @@ -4,165 +4,74 @@ using System; using System.Threading; using System.Threading.Tasks; -using Nethermind.Blockchain; -using Nethermind.Blockchain.Receipts; +using Autofac; +using Autofac.Features.AttributeFilters; using Nethermind.Blockchain.Synchronization; using Nethermind.Config; using Nethermind.Core; using Nethermind.Core.Extensions; -using Nethermind.Core.Specs; -using Nethermind.Db; using Nethermind.Logging; -using Nethermind.Specs.ChainSpecStyle; -using Nethermind.State; using Nethermind.Stats; using Nethermind.Stats.Model; +using Nethermind.Synchronization; using Nethermind.Synchronization.Blocks; using Nethermind.Synchronization.DbTuner; -using Nethermind.Synchronization.FastBlocks - ; +using Nethermind.Synchronization.FastBlocks; using Nethermind.Synchronization.FastSync; using Nethermind.Synchronization.ParallelSync; -using Nethermind.Synchronization.Peers; using Nethermind.Synchronization.Reporting; using Nethermind.Synchronization.SnapSync; using Nethermind.Synchronization.StateSync; -using Nethermind.Trie; namespace Nethermind.Synchronization { - public class Synchronizer : ISynchronizer + public class Synchronizer( + ISyncModeSelector syncModeSelector, + ISyncReport syncReport, + ISyncConfig syncConfig, + ILogManager logManager, + INodeStatsManager nodeStatsManager, + [KeyFilter(nameof(FullSyncFeed))] SyncFeedComponent fullSyncComponent, + [KeyFilter(nameof(FastSyncFeed))] SyncFeedComponent fastSyncComponent, + SyncFeedComponent stateSyncComponent, + SyncFeedComponent snapSyncComponent, + [KeyFilter(nameof(HeadersSyncFeed))] SyncFeedComponent fastHeaderComponent, + SyncFeedComponent oldBodiesComponent, + SyncFeedComponent oldReceiptsComponent, +#pragma warning disable CS9113 // Parameter is unread. But it need to be instantiated to function + SyncDbTuner syncDbTuner, + MallocTrimmer mallocTrimmer, +#pragma warning restore CS9113 // Parameter is unread. + IProcessExitSource exitSource) + : ISynchronizer { private const int FeedsTerminationTimeout = 5_000; - private static MallocTrimmer? s_trimmer; - private static SyncDbTuner? s_dbTuner; + private readonly ILogger _logger = logManager.GetClassLogger(); - private readonly ISpecProvider _specProvider; - private readonly IReceiptStorage _receiptStorage; - private readonly IBlockDownloaderFactory _blockDownloaderFactory; - private readonly INodeStatsManager _nodeStatsManager; - - protected readonly ILogger _logger; - protected readonly IBlockTree _blockTree; - protected readonly ISyncConfig _syncConfig; - protected readonly ISyncPeerPool _syncPeerPool; - protected readonly ILogManager _logManager; - protected readonly ISyncReport _syncReport; - protected readonly IPivot _pivot; - - protected CancellationTokenSource? _syncCancellation = new(); + private CancellationTokenSource? _syncCancellation = new(); /* sync events are used mainly for managing sync peers reputation */ public event EventHandler? SyncEvent; - private readonly IDbProvider _dbProvider; - private FastSyncFeed? _fastSyncFeed; - private StateSyncFeed? _stateSyncFeed; - private FullSyncFeed? _fullSyncFeed; - private readonly IProcessExitSource _exitSource; - protected IBetterPeerStrategy _betterPeerStrategy; - private readonly ChainSpec _chainSpec; - - public ISnapProvider SnapProvider { get; } - - private HeadersSyncFeed? _headersSyncFeed; - private HeadersSyncFeed? HeadersSyncFeed => _headersSyncFeed ??= CreateHeadersSyncFeed(); - - private ReceiptsSyncFeed? _receiptsSyncFeed; - private ReceiptsSyncFeed? ReceiptsSyncFeed => _receiptsSyncFeed ??= CreateReceiptsSyncFeed(); - - private BodiesSyncFeed? _bodiesSyncFeed; - private BodiesSyncFeed? BodiesSyncFeed => _bodiesSyncFeed ??= CreateBodiesSyncFeed(); - - private SnapSyncFeed? _snapSyncFeed; - private SnapSyncFeed? SnapSyncFeed => _snapSyncFeed ??= CreateSnapSyncFeed(); - - private ISyncProgressResolver? _syncProgressResolver; - public ISyncProgressResolver SyncProgressResolver => _syncProgressResolver ??= new SyncProgressResolver( - _blockTree, - new FullStateFinder(_blockTree, _stateReader), - _syncConfig, - HeadersSyncFeed, - BodiesSyncFeed, - ReceiptsSyncFeed, - SnapSyncFeed, - _logManager); - - protected ISyncModeSelector? _syncModeSelector; - private readonly IStateReader _stateReader; - private readonly INodeStorage _nodeStorage; - private readonly ProgressTracker _progressTracker; - - public virtual ISyncModeSelector SyncModeSelector => _syncModeSelector ??= new MultiSyncModeSelector( - SyncProgressResolver, - _syncPeerPool!, - _syncConfig, - No.BeaconSync, - _betterPeerStrategy!, - _logManager, - _chainSpec?.SealEngineType == SealEngineType.Clique); - - public Synchronizer( - IDbProvider dbProvider, - INodeStorage nodeStorage, - ISpecProvider specProvider, - IBlockTree blockTree, - IReceiptStorage receiptStorage, - ISyncPeerPool peerPool, - INodeStatsManager nodeStatsManager, - ISyncConfig syncConfig, - IBlockDownloaderFactory blockDownloaderFactory, - IPivot pivot, - IProcessExitSource processExitSource, - IBetterPeerStrategy betterPeerStrategy, - ChainSpec chainSpec, - IStateReader stateReader, - ILogManager logManager) - { - _dbProvider = dbProvider ?? throw new ArgumentNullException(nameof(dbProvider)); - _nodeStorage = nodeStorage ?? throw new ArgumentNullException(nameof(nodeStorage)); - _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); - _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); - _receiptStorage = receiptStorage ?? throw new ArgumentNullException(nameof(receiptStorage)); - _syncConfig = syncConfig ?? throw new ArgumentNullException(nameof(syncConfig)); - _blockDownloaderFactory = blockDownloaderFactory ?? throw new ArgumentNullException(nameof(blockDownloaderFactory)); - _pivot = pivot ?? throw new ArgumentNullException(nameof(pivot)); - _syncPeerPool = peerPool ?? throw new ArgumentNullException(nameof(peerPool)); - _nodeStatsManager = nodeStatsManager ?? throw new ArgumentNullException(nameof(nodeStatsManager)); - _exitSource = processExitSource ?? throw new ArgumentNullException(nameof(processExitSource)); - _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); - _betterPeerStrategy = betterPeerStrategy ?? throw new ArgumentNullException(nameof(betterPeerStrategy)); - _chainSpec = chainSpec ?? throw new ArgumentNullException(nameof(chainSpec)); - _stateReader = stateReader ?? throw new ArgumentNullException(nameof(_stateReader)); - - _syncReport = new SyncReport(_syncPeerPool!, nodeStatsManager!, _syncConfig, _pivot, logManager); - - _progressTracker = new( - blockTree, - dbProvider.StateDb, - logManager, - _syncConfig.SnapSyncAccountRangePartitionCount); - SnapProvider = new SnapProvider(_progressTracker, dbProvider.CodeDb, nodeStorage, logManager); - } + public ISyncModeSelector SyncModeSelector => syncModeSelector; public virtual void Start() { - if (!_syncConfig.SynchronizationEnabled) + if (!syncConfig.SynchronizationEnabled) { return; } StartFullSyncComponents(); - if (_syncConfig.FastSync) + if (syncConfig.FastSync) { StartFastBlocksComponents(); StartFastSyncComponents(); - if (_syncConfig.SnapSync) + if (syncConfig.SnapSync) { StartSnapSyncComponents(); } @@ -170,72 +79,20 @@ public virtual void Start() StartStateSyncComponents(); } - if (_syncConfig.TuneDbMode != ITunableDb.TuneType.Default || _syncConfig.BlocksDbTuneDbMode != ITunableDb.TuneType.Default) + if (syncConfig.ExitOnSynced) { - SetupDbOptimizer(); - } - - if (_syncConfig.ExitOnSynced) - { - _exitSource.WatchForExit(SyncModeSelector, _logManager, TimeSpan.FromSeconds(_syncConfig.ExitOnSyncedWaitTimeSec)); + exitSource.WatchForExit(SyncModeSelector, logManager, TimeSpan.FromSeconds(syncConfig.ExitOnSyncedWaitTimeSec)); } WireMultiSyncModeSelector(); - s_trimmer ??= new MallocTrimmer(SyncModeSelector, TimeSpan.FromSeconds(_syncConfig.MallocTrimIntervalSec), _logManager); - SyncModeSelector.Changed += _syncReport.SyncModeSelectorOnChanged; - } - - private HeadersSyncFeed? CreateHeadersSyncFeed() - { - if (!_syncConfig.FastSync || !_syncConfig.DownloadHeadersInFastSync) return null; - return new HeadersSyncFeed(_blockTree, _syncPeerPool, _syncConfig, _syncReport, _logManager); - } - - private BodiesSyncFeed? CreateBodiesSyncFeed() - { - if (!_syncConfig.FastSync || !_syncConfig.DownloadHeadersInFastSync || !_syncConfig.DownloadBodiesInFastSync) return null; - return new BodiesSyncFeed(_specProvider, _blockTree, _syncPeerPool, _syncConfig, _syncReport, _dbProvider.BlocksDb, _dbProvider.MetadataDb, _logManager); - } - - private ReceiptsSyncFeed? CreateReceiptsSyncFeed() - { - if (!_syncConfig.FastSync || !_syncConfig.DownloadHeadersInFastSync || !_syncConfig.DownloadBodiesInFastSync || !_syncConfig.DownloadReceiptsInFastSync) return null; - return new ReceiptsSyncFeed(_specProvider, _blockTree, _receiptStorage, _syncPeerPool, _syncConfig, _syncReport, _dbProvider.MetadataDb, _logManager); - } - - private SnapSyncFeed? CreateSnapSyncFeed() - { - if (!_syncConfig.FastSync || !_syncConfig.SnapSync) return null; - return new SnapSyncFeed(SnapProvider, _logManager); - } - - private void SetupDbOptimizer() - { - s_dbTuner ??= new SyncDbTuner( - _syncConfig, - SnapSyncFeed, - BodiesSyncFeed, - ReceiptsSyncFeed, - _dbProvider.StateDb as ITunableDb, - _dbProvider.CodeDb as ITunableDb, - _dbProvider.BlocksDb as ITunableDb, - _dbProvider.ReceiptsDb as ITunableDb); + SyncModeSelector.Changed += syncReport.SyncModeSelectorOnChanged; } private void StartFullSyncComponents() { - _fullSyncFeed = new FullSyncFeed(); - BlockDownloader fullSyncBlockDownloader = _blockDownloaderFactory.Create(_fullSyncFeed, _blockTree, _receiptStorage, _syncPeerPool, _syncReport); - fullSyncBlockDownloader.SyncEvent += DownloaderOnSyncEvent; - - SyncDispatcher dispatcher = CreateDispatcher( - _fullSyncFeed, - fullSyncBlockDownloader, - _blockDownloaderFactory.CreateAllocationStrategyFactory() - ); - - dispatcher.Start(_syncCancellation!.Token).ContinueWith(t => + fullSyncComponent.BlockDownloader.SyncEvent += DownloaderOnSyncEvent; + fullSyncComponent.Dispatcher.Start(_syncCancellation!.Token).ContinueWith(t => { if (t.IsFaulted) { @@ -250,17 +107,8 @@ private void StartFullSyncComponents() private void StartFastSyncComponents() { - _fastSyncFeed = new FastSyncFeed(_syncConfig); - BlockDownloader downloader = _blockDownloaderFactory.Create(_fastSyncFeed, _blockTree, _receiptStorage, _syncPeerPool, _syncReport); - downloader.SyncEvent += DownloaderOnSyncEvent; - - SyncDispatcher dispatcher = CreateDispatcher( - _fastSyncFeed, - downloader, - _blockDownloaderFactory.CreateAllocationStrategyFactory() - ); - - dispatcher.Start(_syncCancellation!.Token).ContinueWith(t => + fastSyncComponent.BlockDownloader.SyncEvent += DownloaderOnSyncEvent; + fastSyncComponent.Dispatcher.Start(_syncCancellation!.Token).ContinueWith(t => { if (t.IsFaulted) { @@ -275,15 +123,7 @@ private void StartFastSyncComponents() private void StartStateSyncComponents() { - TreeSync treeSync = new(SyncMode.StateNodes, _dbProvider.CodeDb, _nodeStorage, _blockTree, _logManager); - _stateSyncFeed = new StateSyncFeed(treeSync, _logManager); - SyncDispatcher stateSyncDispatcher = CreateDispatcher( - _stateSyncFeed, - new StateSyncDownloader(_logManager), - new StateSyncAllocationStrategyFactory() - ); - - Task syncDispatcherTask = stateSyncDispatcher.Start(_syncCancellation.Token).ContinueWith(t => + Task syncDispatcherTask = stateSyncComponent.Dispatcher.Start(_syncCancellation.Token).ContinueWith(t => { if (t.IsFaulted) { @@ -296,15 +136,10 @@ private void StartStateSyncComponents() }); } + private void StartSnapSyncComponents() { - SyncDispatcher dispatcher = CreateDispatcher( - SnapSyncFeed, - new SnapSyncDownloader(_logManager), - new SnapSyncAllocationStrategyFactory() - ); - - Task _ = dispatcher.Start(_syncCancellation!.Token).ContinueWith(t => + Task _ = snapSyncComponent.Dispatcher.Start(_syncCancellation!.Token).ContinueWith(t => { if (t.IsFaulted) { @@ -319,14 +154,7 @@ private void StartSnapSyncComponents() private void StartFastBlocksComponents() { - FastBlocksPeerAllocationStrategyFactory fastFactory = new(); - SyncDispatcher headersDispatcher = CreateDispatcher( - HeadersSyncFeed, - new HeadersSyncDownloader(_logManager), - fastFactory - ); - - Task headersTask = headersDispatcher.Start(_syncCancellation!.Token).ContinueWith(t => + Task headersTask = fastHeaderComponent.Dispatcher.Start(_syncCancellation!.Token).ContinueWith(t => { if (t.IsFaulted) { @@ -338,18 +166,11 @@ private void StartFastBlocksComponents() } }); - if (_syncConfig.DownloadHeadersInFastSync) + if (syncConfig.DownloadHeadersInFastSync) { - if (_syncConfig.DownloadBodiesInFastSync) + if (syncConfig.DownloadBodiesInFastSync) { - - SyncDispatcher bodiesDispatcher = CreateDispatcher( - BodiesSyncFeed!, - new BodiesSyncDownloader(_logManager), - fastFactory - ); - - Task bodiesTask = bodiesDispatcher.Start(_syncCancellation.Token).ContinueWith(t => + Task bodiesTask = oldBodiesComponent.Dispatcher.Start(_syncCancellation.Token).ContinueWith(t => { if (t.IsFaulted) { @@ -362,15 +183,9 @@ private void StartFastBlocksComponents() }); } - if (_syncConfig.DownloadReceiptsInFastSync) + if (syncConfig.DownloadReceiptsInFastSync) { - SyncDispatcher receiptsDispatcher = CreateDispatcher( - ReceiptsSyncFeed!, - new ReceiptsSyncDispatcher(_logManager), - fastFactory - ); - - Task receiptsTask = receiptsDispatcher.Start(_syncCancellation.Token).ContinueWith(t => + Task receiptsTask = oldReceiptsComponent.Dispatcher.Start(_syncCancellation.Token).ContinueWith(t => { if (t.IsFaulted) { @@ -385,17 +200,6 @@ private void StartFastBlocksComponents() } } - protected SyncDispatcher CreateDispatcher(ISyncFeed feed, ISyncDownloader downloader, IPeerAllocationStrategyFactory peerAllocationStrategyFactory) - { - return new( - _syncConfig.MaxProcessingThreads, - feed!, - downloader, - _syncPeerPool, - peerAllocationStrategyFactory, - _logManager); - } - private static NodeStatsEventType Convert(SyncEvent syncEvent) { return syncEvent switch @@ -410,7 +214,7 @@ private static NodeStatsEventType Convert(SyncEvent syncEvent) private void DownloaderOnSyncEvent(object? sender, SyncEventArgs e) { - _nodeStatsManager.ReportSyncEvent(e.Peer.Node, Convert(e.SyncEvent)); + nodeStatsManager.ReportSyncEvent(e.Peer.Node, Convert(e.SyncEvent)); SyncEvent?.Invoke(this, e); } @@ -421,27 +225,27 @@ public Task StopAsync() return Task.WhenAny( Task.Delay(FeedsTerminationTimeout), Task.WhenAll( - _fastSyncFeed?.FeedTask ?? Task.CompletedTask, - _stateSyncFeed?.FeedTask ?? Task.CompletedTask, - SnapSyncFeed?.FeedTask ?? Task.CompletedTask, - _fullSyncFeed?.FeedTask ?? Task.CompletedTask, - HeadersSyncFeed?.FeedTask ?? Task.CompletedTask, - BodiesSyncFeed?.FeedTask ?? Task.CompletedTask, - ReceiptsSyncFeed?.FeedTask ?? Task.CompletedTask)); + fullSyncComponent.Feed.FeedTask, + fastSyncComponent.Feed.FeedTask, + stateSyncComponent.Feed.FeedTask, + snapSyncComponent.Feed.FeedTask, + fastHeaderComponent.Feed.FeedTask, + oldBodiesComponent.Feed.FeedTask, + oldReceiptsComponent.Feed.FeedTask)); } private void WireMultiSyncModeSelector() { - WireFeedWithModeSelector(_fastSyncFeed); - WireFeedWithModeSelector(_stateSyncFeed); - WireFeedWithModeSelector(SnapSyncFeed); - WireFeedWithModeSelector(_fullSyncFeed); - WireFeedWithModeSelector(HeadersSyncFeed); - WireFeedWithModeSelector(BodiesSyncFeed); - WireFeedWithModeSelector(ReceiptsSyncFeed); + WireFeedWithModeSelector(fullSyncComponent.Feed); + WireFeedWithModeSelector(fastSyncComponent.Feed); + WireFeedWithModeSelector(stateSyncComponent.Feed); + WireFeedWithModeSelector(snapSyncComponent.Feed); + WireFeedWithModeSelector(fastHeaderComponent.Feed); + WireFeedWithModeSelector(oldBodiesComponent.Feed); + WireFeedWithModeSelector(oldReceiptsComponent.Feed); } - protected void WireFeedWithModeSelector(ISyncFeed? feed) + public void WireFeedWithModeSelector(ISyncFeed? feed) { if (feed is null) return; SyncModeSelector.Changed += ((sender, args) => @@ -454,17 +258,141 @@ protected void WireFeedWithModeSelector(ISyncFeed? feed) public void Dispose() { CancellationTokenExtensions.CancelDisposeAndClear(ref _syncCancellation); - _syncReport.Dispose(); - _fastSyncFeed?.Dispose(); - _stateSyncFeed?.Dispose(); - _stateSyncFeed = null; - SnapSyncFeed?.Dispose(); - _snapSyncFeed = null; - _fullSyncFeed?.Dispose(); - HeadersSyncFeed?.Dispose(); - BodiesSyncFeed?.Dispose(); - ReceiptsSyncFeed?.Dispose(); - _progressTracker.Dispose(); } } } + +public class SynchronizerModule(ISyncConfig syncConfig) : Module +{ + protected override void Load(ContainerBuilder builder) + { + base.Load(builder); + + builder + .AddSingleton() + + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // For blocks. There are two block scope, Fast and Full + .AddScoped>() + .AddScoped, BlockDownloader>() + .AddScoped, BlocksSyncPeerAllocationStrategyFactory>() + .AddScoped>() + + // For headers. There are two header scope, Fast and Beacon + .AddScoped>() + .AddScoped, HeadersSyncDownloader>() + .AddScoped, FastBlocksPeerAllocationStrategyFactory>() + .AddScoped>() + + // SyncProgress resolver need one header sync batch feed, which is the fast header one. + .Register(ctx => ctx + .ResolveNamed>(nameof(HeadersSyncFeed)) + .Feed) + .Named>(nameof(HeadersSyncFeed)); + + ConfigureSnapComponent(builder); + ConfigureReceiptSyncComponent(builder); + ConfigureBodiesSyncComponent(builder); + ConfigureStateSyncComponent(builder); + + builder + .RegisterNamedComponentInItsOwnLifetime>(nameof(HeadersSyncFeed), ConfigureFastHeader) + .RegisterNamedComponentInItsOwnLifetime>(nameof(FastSyncFeed), ConfigureFastSync) + .RegisterNamedComponentInItsOwnLifetime>(nameof(FullSyncFeed), ConfigureFullSync); + } + + private void ConfigureFullSync(ContainerBuilder scopeConfig) + { + scopeConfig.AddScoped, FullSyncFeed>(); + } + + private void ConfigureFastSync(ContainerBuilder scopeConfig) + { + if (syncConfig.FastSync) + { + scopeConfig.AddScoped, FastSyncFeed>(); + } + else + { + scopeConfig.AddScoped, NoopSyncFeed>(); + } + } + + private void ConfigureFastHeader(ContainerBuilder scopeConfig) + { + if (!syncConfig.FastSync || !syncConfig.DownloadHeadersInFastSync) + { + scopeConfig.AddScoped, NoopSyncFeed>(); + } + else + { + scopeConfig.AddScoped, HeadersSyncFeed>(); + } + } + + private void ConfigureSnapComponent(ContainerBuilder serviceCollection) + { + serviceCollection + .AddSingleton() + .AddSingleton(); + + ConfigureSingletonSyncFeed(serviceCollection); + + if (!syncConfig.FastSync || !syncConfig.SnapSync) + { + serviceCollection.AddSingleton, NoopSyncFeed>(); + } + } + + private void ConfigureReceiptSyncComponent(ContainerBuilder serviceCollection) + { + ConfigureSingletonSyncFeed(serviceCollection); + + if (!syncConfig.FastSync || !syncConfig.DownloadHeadersInFastSync || + !syncConfig.DownloadBodiesInFastSync || + !syncConfig.DownloadReceiptsInFastSync) + { + serviceCollection.AddSingleton, NoopSyncFeed>(); + } + } + + private void ConfigureBodiesSyncComponent(ContainerBuilder serviceCollection) + { + ConfigureSingletonSyncFeed(serviceCollection); + + if (!syncConfig.FastSync || !syncConfig.DownloadHeadersInFastSync || + !syncConfig.DownloadBodiesInFastSync) + { + serviceCollection.AddSingleton, NoopSyncFeed>(); + } + + } + + private void ConfigureStateSyncComponent(ContainerBuilder serviceCollection) + { + serviceCollection + .AddSingleton(); + + ConfigureSingletonSyncFeed(serviceCollection); + + // Disable it by setting noop + if (!syncConfig.FastSync) serviceCollection.AddSingleton, NoopSyncFeed>(); + } + + private static void ConfigureSingletonSyncFeed(ContainerBuilder serviceCollection) where TFeed : class, ISyncFeed where TDownloader : class, ISyncDownloader where TAllocationStrategy : class, IPeerAllocationStrategyFactory + { + serviceCollection + .AddSingleton, TFeed>() + .AddSingleton>() + .AddSingleton, TDownloader>() + .AddSingleton, TAllocationStrategy>() + .AddSingleton>(); + } + +} diff --git a/src/Nethermind/Nethermind.Trie.Test/IFullTrieStoreExtensions.cs b/src/Nethermind/Nethermind.Trie.Test/IFullTrieStoreExtensions.cs new file mode 100644 index 00000000000..0880b8908a2 --- /dev/null +++ b/src/Nethermind/Nethermind.Trie.Test/IFullTrieStoreExtensions.cs @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Trie.Pruning; + +namespace Nethermind.Trie.Test; + +internal static class IFullTrieStoreExtensions +{ + // Small utility to not having to double wrap + public static ICommitter BeginStateBlockCommit(this ITrieStore trieStore, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) + { + IBlockCommitter blockCommitter = trieStore.BeginBlockCommit(blockNumber); + ICommitter stateTreeCommitter = trieStore.GetTrieStore(null).BeginCommit(root, writeFlags: writeFlags); + return new CommitterWithBlockCommitter(blockCommitter, stateTreeCommitter); + } + + public static void CommitPatriciaTrie(this ITrieStore trieStore, long blockNumber, PatriciaTree patriciaTree) + { + using (trieStore.BeginBlockCommit(blockNumber)) { patriciaTree.Commit(); } + } + + private class CommitterWithBlockCommitter(IBlockCommitter blockCommitter, ICommitter baseCommitter) : ICommitter + { + public void Dispose() + { + baseCommitter.Dispose(); + blockCommitter.Dispose(); + } + + public void CommitNode(ref TreePath path, NodeCommitInfo nodeCommitInfo) + { + baseCommitter.CommitNode(ref path, nodeCommitInfo); + } + } +} diff --git a/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs b/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs index e6cc6289e72..3527f248c8b 100644 --- a/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs +++ b/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs @@ -69,9 +69,8 @@ public void Memory_with_one_node_is_288() TrieNode trieNode = new(NodeType.Leaf, Keccak.Zero); // 56B using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new TestPruningStrategy(true)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TreePath emptyPath = TreePath.Empty; - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1234, null)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(1234, null)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode)); } @@ -88,13 +87,12 @@ public void Pruning_off_cache_should_not_change_commit_node() TrieNode trieNode3 = new(NodeType.Branch, TestItem.KeccakB); using TrieStore fullTrieStore = CreateTrieStore(); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TreePath emptyPath = TreePath.Empty; - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(1234, trieNode)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode)); } - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1235, trieNode)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(1235, trieNode)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode3)); @@ -110,10 +108,9 @@ public void When_commit_forward_write_flag_if_available() TestMemDb testMemDb = new TestMemDb(); using TrieStore fullTrieStore = CreateTrieStore(kvStore: testMemDb); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TreePath emptyPath = TreePath.Empty; - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode, WriteFlags.LowPriority)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(1234, trieNode, WriteFlags.LowPriority)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode)); } @@ -135,15 +132,14 @@ public void Should_always_announce_block_number_when_pruning_disabled_and_persis long reorgBoundaryCount = 0L; using TrieStore fullTrieStore = CreateTrieStore(persistenceStrategy: Archive.Instance); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); fullTrieStore.ReorgBoundaryReached += (_, e) => reorgBoundaryCount += e.BlockNumber; - trieStore.BeginCommit(TrieType.State, 1, trieNode).Dispose(); + fullTrieStore.BeginStateBlockCommit(1, trieNode).Dispose(); reorgBoundaryCount.Should().Be(0); - trieStore.BeginCommit(TrieType.State, 2, trieNode).Dispose(); + fullTrieStore.BeginStateBlockCommit(2, trieNode).Dispose(); reorgBoundaryCount.Should().Be(1); - trieStore.BeginCommit(TrieType.State, 3, trieNode).Dispose(); + fullTrieStore.BeginStateBlockCommit(3, trieNode).Dispose(); reorgBoundaryCount.Should().Be(3); - trieStore.BeginCommit(TrieType.State, 4, trieNode).Dispose(); + fullTrieStore.BeginStateBlockCommit(4, trieNode).Dispose(); reorgBoundaryCount.Should().Be(6); } @@ -154,12 +150,11 @@ public void Should_always_announce_zero_when_not_persisting() long reorgBoundaryCount = 0L; using TrieStore fullTrieStore = CreateTrieStore(); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); fullTrieStore.ReorgBoundaryReached += (_, e) => reorgBoundaryCount += e.BlockNumber; - trieStore.BeginCommit(TrieType.State, 1, trieNode).Dispose(); - trieStore.BeginCommit(TrieType.State, 2, trieNode).Dispose(); - trieStore.BeginCommit(TrieType.State, 3, trieNode).Dispose(); - trieStore.BeginCommit(TrieType.State, 4, trieNode).Dispose(); + fullTrieStore.BeginStateBlockCommit(1, trieNode).Dispose(); + fullTrieStore.BeginStateBlockCommit(2, trieNode).Dispose(); + fullTrieStore.BeginStateBlockCommit(3, trieNode).Dispose(); + fullTrieStore.BeginStateBlockCommit(4, trieNode).Dispose(); reorgBoundaryCount.Should().Be(0L); } @@ -202,9 +197,8 @@ public void Memory_with_two_nodes_is_correct() TrieNode trieNode2 = new(NodeType.Leaf, TestItem.KeccakB); using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new TestPruningStrategy(true)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TreePath emptyPath = TreePath.Empty; - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1234, null)) + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(1234, null)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode1)); committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); @@ -234,7 +228,10 @@ public void Memory_with_concurrent_commits_is_correct() tree.Set(key, value.ToArray()); } - tree.Commit(0); + using (fullTrieStore.BeginBlockCommit(0)) + { + tree.Commit(); + } fullTrieStore.MemoryUsedByDirtyCache.Should().Be(_scheme == INodeStorage.KeyScheme.Hash ? 591672L : 661820L); fullTrieStore.CommittedNodesCount.Should().Be(1349); @@ -249,15 +246,14 @@ public void Memory_with_two_times_two_nodes_is_correct() TrieNode trieNode4 = new(NodeType.Leaf, TestItem.KeccakB); using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new TestPruningStrategy(true)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TreePath emptyPath = TreePath.Empty; - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode2)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(1234, trieNode2)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode1)); committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); } - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1235, trieNode2)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(1235, trieNode2)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode3)); committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode4)); @@ -286,21 +282,20 @@ public void Dispatcher_will_try_to_clear_memory() trieNode4.ResolveKey(null!, ref emptyPath, true); using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new MemoryLimit(640)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode2)) + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(1234, trieNode2)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode1)); committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1235, trieNode2)) + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(1235, trieNode2)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode3)); committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode4)); } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 1236, trieNode2)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(1236, trieNode2)) { } fullTrieStore.MemoryUsedByDirtyCache.Should().Be( trieNode1.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize + @@ -325,15 +320,14 @@ public void Dispatcher_will_try_to_clear_memory_the_soonest_possible() trieNode4.ResolveKey(null!, ref emptyPath, true); using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new MemoryLimit(512)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode2)) + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(1234, trieNode2)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode1)); committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1235, trieNode2)) + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(1235, trieNode2)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode3)); committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode4)); @@ -350,13 +344,12 @@ public void Dispatcher_will_try_to_clear_memory_the_soonest_possible() public void Dispatcher_will_always_try_to_clear_memory() { TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new MemoryLimit(512)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TreePath emptyPath = TreePath.Empty; for (int i = 0; i < 1024; i++) { TrieNode fakeRoot = new(NodeType.Leaf, new byte[0]); // 192B fakeRoot.ResolveKey(NullTrieNodeResolver.Instance, ref emptyPath, true); - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, i, fakeRoot)) + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(i, fakeRoot)) { for (int j = 0; j < 1 + i % 3; j++) { @@ -384,16 +377,15 @@ public void Dispatcher_will_save_to_db_everything_from_snapshot_blocks() pruningStrategy: new MemoryLimit(16.MB()), kvStore: memDb, persistenceStrategy: new ConstantInterval(4)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 0, a)) + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(0, a)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 1, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(1, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(2, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(3, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(4, a)) { } storage.Get(null, TreePath.Empty, a.Keccak).Should().NotBeNull(); fullTrieStore.IsNodeCached(null, TreePath.Empty, a.Keccak).Should().BeTrue(); @@ -410,15 +402,14 @@ public void Stays_in_memory_until_persisted() NodeStorage storage = new NodeStorage(memDb); using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new MemoryLimit(16.MB())); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 0, a)) + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(0, a)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 1, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(1, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(2, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(3, a)) { } // <- do not persist in this test storage.Get(null, TreePath.Empty, a.Keccak).Should().BeNull(); @@ -451,20 +442,19 @@ public void Will_get_persisted_on_snapshot_if_referenced() pruningStrategy: new MemoryLimit(16.MB()), persistenceStrategy: new ConstantInterval(4) ); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, a)) + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(0, null)) { } + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(1, a)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(2, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(3, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(4, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(5, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(6, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(7, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(8, a)) { } storage.Get(null, TreePath.Empty, a.Keccak).Should().NotBeNull(); fullTrieStore.IsNodeCached(null, TreePath.Empty, a.Keccak).Should().BeTrue(); @@ -488,23 +478,21 @@ public void Will_not_get_dropped_on_snapshot_if_unreferenced_in_later_blocks() pruningStrategy: new MemoryLimit(16.MB()), persistenceStrategy: new ConstantInterval(4)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, a)) + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(0, null)) { } + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(1, a)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, a)) { } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 7, a)) + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(2, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(3, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(4, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(5, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(6, a)) { } + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(7, a)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(b)); } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(8, a)) { } nodeStorage.Get(null, TreePath.Empty, a.Keccak).Should().NotBeNull(); fullTrieStore.IsNodeCached(null, TreePath.Empty, a.Keccak).Should().BeTrue(); @@ -527,23 +515,21 @@ public void Will_get_dropped_on_snapshot_if_it_was_a_transient_node() pruningStrategy: new MemoryLimit(16.MB()), persistenceStrategy: new ConstantInterval(4)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, a)) + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(0, null)) { } + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(1, a)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 3, a)) + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(2, a)) { } + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(3, a)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(b)); // <- new root } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, b)) { } // should be 'a' to test properly - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, b)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, b)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, b)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, b)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(4, b)) { } // should be 'a' to test properly + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(5, b)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(6, b)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(7, b)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(8, b)) { } memDb[a.Keccak!.Bytes].Should().BeNull(); fullTrieStore.IsNodeCached(null, TreePath.Empty, a.Keccak).Should().BeTrue(); @@ -625,25 +611,28 @@ public void Will_store_storage_on_snapshot() pruningStrategy: new MemoryLimit(16.MB()), persistenceStrategy: new ConstantInterval(4)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); + using (fullTrieStore.BeginStateBlockCommit(0, null)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } - using (ICommitter committer = fullTrieStore.GetTrieStore(TestItem.KeccakA).BeginCommit(TrieType.Storage, 1, storage1)) - { - committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage1)); - } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, a)) + using (fullTrieStore.BeginBlockCommit(1)) { - committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + using (ICommitter committer = fullTrieStore.GetTrieStore(TestItem.KeccakA).BeginCommit(storage1)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage1)); + } + + using (ICommitter committer = fullTrieStore.GetTrieStore(null).BeginCommit(a)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + } } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(2, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(3, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(4, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(5, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(6, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(7, a)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(8, a)) { } asStorage.Get(null, TreePath.Empty, a.Keccak).Should().NotBeNull(); asStorage.Get(TestItem.KeccakA, TreePath.Empty, storage1.Keccak).Should().NotBeNull(); @@ -676,27 +665,31 @@ public void Will_drop_transient_storage() IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(0, null)) { } - using (ICommitter committer = trieStore.BeginCommit(TrieType.Storage, 1, storage1)) + using (fullTrieStore.BeginBlockCommit(1)) { - committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); - committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage1)); - } + using (ICommitter committer = fullTrieStore.GetTrieStore(Hash256.Zero).BeginCommit(storage1)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage1)); + } + + using (ICommitter _ = trieStore.BeginCommit(a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 1, a)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } + } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 2, b)) + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(2, a)) { } + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(2, b)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(b)); // <- new root } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, b)) { } // Should be 'a' to test properly - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, b)) { } // Should be 'a' to test properly - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, b)) { } // Should be 'a' to test properly - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, b)) { } // Should be 'a' to test properly - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, b)) { } // Should be 'a' to test properly + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(4, b)) { } // Should be 'a' to test properly + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(5, b)) { } // Should be 'a' to test properly + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(6, b)) { } // Should be 'a' to test properly + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(7, b)) { } // Should be 'a' to test properly + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(8, b)) { } // Should be 'a' to test properly memDb[a.Keccak!.Bytes].Should().BeNull(); memDb[storage1.Keccak!.Bytes].Should().BeNull(); @@ -744,34 +737,35 @@ public void Will_combine_same_storage() pruningStrategy: new MemoryLimit(16.MB()), persistenceStrategy: new ConstantInterval(4)); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(0, null)) { } - using (ICommitter committer = fullTrieStore.GetTrieStore(new Hash256(Nibbles.ToBytes(storage1Nib))).BeginCommit(TrieType.Storage, 1, storage1)) + using (fullTrieStore.BeginBlockCommit(1)) { - committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage1)); - } + using (ICommitter committer = fullTrieStore.GetTrieStore(new Hash256(Nibbles.ToBytes(storage1Nib))).BeginCommit(storage1)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage1)); + } - using (ICommitter committer = fullTrieStore.GetTrieStore(new Hash256(Nibbles.ToBytes(storage2Nib))).BeginCommit(TrieType.Storage, 1, storage2)) - { - committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage2)); - } + using (ICommitter committer = fullTrieStore.GetTrieStore(new Hash256(Nibbles.ToBytes(storage2Nib))).BeginCommit(storage2)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage2)); + } - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, branch)) - { - committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); - committer.CommitNode(ref emptyPath, new NodeCommitInfo(b)); - committer.CommitNode(ref emptyPath, new NodeCommitInfo(branch)); + using (ICommitter committer = fullTrieStore.GetTrieStore(null).BeginCommit(branch)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(b)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(branch)); + } } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, branch)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, branch)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, branch)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, branch)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, branch)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, branch)) { } - using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, branch)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(2, branch)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(3, branch)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(4, branch)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(5, branch)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(6, branch)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(7, branch)) { } + using (ICommitter _ = fullTrieStore.BeginStateBlockCommit(8, branch)) { } storage.Get(null, TreePath.FromNibble(new byte[] { 0 }), a.Keccak).Should().NotBeNull(); storage.Get(new Hash256(Nibbles.ToBytes(storage1Nib)), TreePath.Empty, storage1.Keccak).Should().NotBeNull(); @@ -800,7 +794,7 @@ public async Task Read_only_trie_store_is_allowing_many_thread_to_work_with_the_ IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TreePath emptyPath = TreePath.Empty; trieNode.ResolveKey(trieStore, ref emptyPath, false); - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 0, trieNode)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(0, trieNode)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode)); } @@ -857,7 +851,7 @@ public void ReadOnly_store_returns_copies(bool pruning) using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new TestPruningStrategy(pruning)); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 0, node)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(0, node)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); } @@ -891,13 +885,13 @@ public void After_commit_should_have_has_root() Account account = new(1); StateTree stateTree = new(trieStore, LimboLogs.Instance); stateTree.Set(TestItem.AddressA, account); - stateTree.Commit(0); + stateTree.Commit(); trieStore.HasRoot(stateTree.RootHash).Should().BeTrue(); stateTree.Get(TestItem.AddressA); account = account.WithChangedBalance(2); stateTree.Set(TestItem.AddressA, account); - stateTree.Commit(0); + stateTree.Commit(); trieStore.HasRoot(stateTree.RootHash).Should().BeTrue(); } @@ -911,13 +905,12 @@ public async Task Will_RemovePastKeys_OnSnapshot() pruningStrategy: new TestPruningStrategy(true, true, 2, 100000), persistenceStrategy: No.Persistence); - IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TreePath emptyPath = TreePath.Empty; for (int i = 0; i < 64; i++) { TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i], new byte[2]); - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, i, node)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(i, node)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); } @@ -955,7 +948,7 @@ public async Task Will_Trigger_ReorgBoundaryEvent_On_Prune() for (int i = 0; i < 64; i++) { TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i], new byte[2]); - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, i, node)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(i, node)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); } @@ -991,7 +984,7 @@ public async Task Will_Not_RemovePastKeys_OnSnapshot_DuringFullPruning() for (int i = 0; i < 64; i++) { TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i], new byte[2]); - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, i, node)) + using (ICommitter? committer = fullTrieStore.BeginStateBlockCommit(i, node)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); } @@ -1021,7 +1014,7 @@ public async Task Will_NotRemove_ReCommittedNode() for (int i = 0; i < 64; i++) { TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i % 4], new byte[2]); - using (ICommitter committer = trieStore.BeginCommit(TrieType.State, i, node)) + using (ICommitter committer = fullTrieStore.BeginStateBlockCommit(i, node)) { committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); } diff --git a/src/Nethermind/Nethermind.Trie.Test/TrieNodeTests.cs b/src/Nethermind/Nethermind.Trie.Test/TrieNodeTests.cs index 001be11d7d7..a2acfd0be71 100644 --- a/src/Nethermind/Nethermind.Trie.Test/TrieNodeTests.cs +++ b/src/Nethermind/Nethermind.Trie.Test/TrieNodeTests.cs @@ -925,7 +925,8 @@ void CheckChildren() [Test] public void Rlp_is_cloned_when_cloning() { - IScopedTrieStore trieStore = new TrieStore(new MemDb(), NullLogManager.Instance).GetTrieStore(null); + ITrieStore fullTrieStore = new TrieStore(new MemDb(), NullLogManager.Instance); + IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TrieNode leaf1 = new(NodeType.Leaf); leaf1.Key = Bytes.FromHexString("abc"); @@ -942,10 +943,13 @@ public void Rlp_is_cloned_when_cloning() TreePath path = TreePath.Empty; - using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 0, leaf2)) + using (IBlockCommitter _ = fullTrieStore.BeginBlockCommit(0)) { - committer.CommitNode(ref path, new NodeCommitInfo(leaf1)); - committer.CommitNode(ref path, new NodeCommitInfo(leaf2)); + using (ICommitter? committer = trieStore.BeginCommit(leaf2)) + { + committer.CommitNode(ref path, new NodeCommitInfo(leaf1)); + committer.CommitNode(ref path, new NodeCommitInfo(leaf2)); + } } TrieNode trieNode = new(NodeType.Branch); diff --git a/src/Nethermind/Nethermind.Trie.Test/TrieTests.cs b/src/Nethermind/Nethermind.Trie.Test/TrieTests.cs index 0d9e05c1e1f..6f61ae6b0c1 100644 --- a/src/Nethermind/Nethermind.Trie.Test/TrieTests.cs +++ b/src/Nethermind/Nethermind.Trie.Test/TrieTests.cs @@ -61,7 +61,7 @@ public void Single_leaf() using TrieStore trieStore = new(memDb, Prune.WhenCacheReaches(1.MB()), Persist.EveryBlock, _logManager); PatriciaTree patriciaTree = new(trieStore, _logManager); patriciaTree.Set(_keyA, _longLeaf1); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); // leaf (root) memDb.Keys.Should().HaveCount(1); @@ -75,7 +75,7 @@ public void Single_leaf_update_same_block() PatriciaTree patriciaTree = new(trieStore, _logManager); patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.Set(_keyA, _longLeaf2); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); // leaf (root) memDb.Keys.Should().HaveCount(1); @@ -92,9 +92,9 @@ public void Single_leaf_update_next_blocks() using TrieStore trieStore = new(memDb, Prune.WhenCacheReaches(1.MB()), Persist.EveryBlock, _logManager); PatriciaTree patriciaTree = new(trieStore, _logManager); patriciaTree.Set(_keyA, _longLeaf1); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.Set(_keyA, _longLeaf2); - patriciaTree.Commit(1); + trieStore.CommitPatriciaTrie(1, patriciaTree); patriciaTree.UpdateRootHash(); // leaf (root) @@ -113,7 +113,7 @@ public void Single_leaf_delete_same_block() PatriciaTree patriciaTree = new(trieStore, _logManager); patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.Set(_keyA, Array.Empty()); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); // leaf (root) memDb.Keys.Should().HaveCount(0); @@ -129,9 +129,9 @@ public void Single_leaf_delete_next_block() using TrieStore trieStore = new(memDb, Prune.WhenCacheReaches(1.MB()), Persist.EveryBlock, _logManager); PatriciaTree patriciaTree = new(trieStore, _logManager); patriciaTree.Set(_keyA, _longLeaf1); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.Set(_keyA, Array.Empty()); - patriciaTree.Commit(1); + trieStore.CommitPatriciaTrie(1, patriciaTree); patriciaTree.UpdateRootHash(); // leaf (root) @@ -147,24 +147,24 @@ public void Single_leaf_and_keep_for_multiple_dispatches_then_delete() MemDb memDb = new(); using TrieStore trieStore = new(memDb, Prune.WhenCacheReaches(1.MB()), new ConstantInterval(4), LimboLogs.Instance); PatriciaTree patriciaTree = new(trieStore, _logManager); - patriciaTree.Commit(0); - patriciaTree.Commit(1); - patriciaTree.Commit(2); + trieStore.CommitPatriciaTrie(0, patriciaTree); + trieStore.CommitPatriciaTrie(1, patriciaTree); + trieStore.CommitPatriciaTrie(2, patriciaTree); patriciaTree.Set(_keyA, _longLeaf1); - patriciaTree.Commit(3); - patriciaTree.Commit(4); + trieStore.CommitPatriciaTrie(3, patriciaTree); + trieStore.CommitPatriciaTrie(4, patriciaTree); patriciaTree.Set(_keyA, Array.Empty()); - patriciaTree.Commit(5); + trieStore.CommitPatriciaTrie(5, patriciaTree); patriciaTree.Set(_keyB, _longLeaf2); - patriciaTree.Commit(6); - patriciaTree.Commit(7); - patriciaTree.Commit(8); - patriciaTree.Commit(9); - patriciaTree.Commit(10); - patriciaTree.Commit(11); + trieStore.CommitPatriciaTrie(6, patriciaTree); + trieStore.CommitPatriciaTrie(7, patriciaTree); + trieStore.CommitPatriciaTrie(8, patriciaTree); + trieStore.CommitPatriciaTrie(9, patriciaTree); + trieStore.CommitPatriciaTrie(10, patriciaTree); + trieStore.CommitPatriciaTrie(11, patriciaTree); patriciaTree.Set(_keyB, Array.Empty()); - patriciaTree.Commit(12); - patriciaTree.Commit(13); + trieStore.CommitPatriciaTrie(12, patriciaTree); + trieStore.CommitPatriciaTrie(13, patriciaTree); patriciaTree.UpdateRootHash(); // leaf (root) @@ -184,7 +184,7 @@ public void Branch_with_branch_and_leaf() patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.Set(_keyB, _longLeaf1); patriciaTree.Set(_keyC, _longLeaf1); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); // leaf (root) memDb.Keys.Should().HaveCount(6); @@ -218,7 +218,7 @@ public void GetBranchNodesWithPartialPath() patriciaTree.Set(_keysA, _longLeaf1); patriciaTree.Set(_keysB, _longLeaf1); patriciaTree.Set(_keysC, _longLeaf1); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -252,11 +252,11 @@ public void Branch_with_branch_and_leaf_then_deleted() patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.Set(_keyB, _longLeaf1); patriciaTree.Set(_keyC, _longLeaf1); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.Set(_keyA, Array.Empty()); patriciaTree.Set(_keyB, Array.Empty()); patriciaTree.Set(_keyC, Array.Empty()); - patriciaTree.Commit(1); + trieStore.CommitPatriciaTrie(1, patriciaTree); patriciaTree.UpdateRootHash(); // leaf (root) @@ -280,7 +280,8 @@ public void Test_add_many(int i) patriciaTree.Set(key.Bytes, value); } - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); + patriciaTree.UpdateRootHash(); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -312,7 +313,7 @@ public void Test_try_delete_and_read_missing_nodes(int i) patriciaTree.Set(key.Bytes, Array.Empty()); } - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.UpdateRootHash(); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -353,7 +354,7 @@ public void Test_update_many(int i) patriciaTree.Set(key.Bytes, value); } - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.UpdateRootHash(); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -378,7 +379,7 @@ public void Test_update_many_next_block(int i) patriciaTree.Set(key.Bytes, value); } - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); for (int j = 0; j < i; j++) { @@ -388,7 +389,7 @@ public void Test_update_many_next_block(int i) _logger.Trace($"Setting {key.Bytes.ToHexString()} = {value.ToHexString()}"); } - patriciaTree.Commit(1); + trieStore.CommitPatriciaTrie(1, patriciaTree); patriciaTree.UpdateRootHash(); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -423,7 +424,7 @@ public void Test_add_and_delete_many_same_block(int i) patriciaTree.Set(key.Bytes, Array.Empty()); } - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.UpdateRootHash(); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -447,7 +448,7 @@ public void Test_add_and_delete_many_next_block(int i) patriciaTree.Set(key.Bytes, value); } - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); for (int j = 0; j < i; j++) { @@ -455,7 +456,7 @@ public void Test_add_and_delete_many_next_block(int i) patriciaTree.Set(key.Bytes, Array.Empty()); } - patriciaTree.Commit(1); + trieStore.CommitPatriciaTrie(1, patriciaTree); patriciaTree.UpdateRootHash(); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -493,7 +494,7 @@ public void Two_branches_exactly_same_leaf() patriciaTree.Set(_keyB, _longLeaf1); patriciaTree.Set(_keyC, _longLeaf1); patriciaTree.Set(_keyD, _longLeaf1); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); // leaf (root) memDb.Keys.Should().HaveCount(8); @@ -519,7 +520,7 @@ public void Two_branches_exactly_same_leaf_then_one_removed() patriciaTree.Set(_keyC, _longLeaf1); patriciaTree.Set(_keyD, _longLeaf1); patriciaTree.Set(_keyA, Array.Empty()); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); // leaf (root) memDb.Keys.Should().HaveCount(6); @@ -545,7 +546,7 @@ public void Extension_with_branch_with_two_different_children() PatriciaTree patriciaTree = new(trieStore, _logManager); patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.Set(_keyB, _longLeaf2); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); memDb.Keys.Should().HaveCount(4); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); checkTree.Get(_keyA).ToArray().Should().BeEquivalentTo(_longLeaf1); @@ -560,7 +561,7 @@ public void Extension_with_branch_with_two_same_children() PatriciaTree patriciaTree = new(trieStore, _logManager); patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.Set(_keyB, _longLeaf1); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); memDb.Keys.Should().HaveCount(4); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); checkTree.Get(_keyA).ToArray().Should().BeEquivalentTo(_longLeaf1); @@ -576,11 +577,11 @@ public void When_branch_with_two_different_children_change_one_and_change_back_n patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.Set(_keyB, _longLeaf2); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.Set(_keyA, _longLeaf3); patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(1); + trieStore.CommitPatriciaTrie(1, patriciaTree); // extension // branch @@ -597,11 +598,11 @@ public void When_branch_with_two_same_children_change_one_and_change_back_next_b patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.Set(_keyB, _longLeaf1); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.Set(_keyA, _longLeaf3); patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(1); + trieStore.CommitPatriciaTrie(1, patriciaTree); memDb.Keys.Should().HaveCount(4); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -631,7 +632,7 @@ L L - - - - - - - - - - - - - - */ patriciaTree.Set(key2, _longLeaf1); patriciaTree.Set(key3, _longLeaf1); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); memDb.Keys.Should().HaveCount(7); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -679,10 +680,10 @@ L L - - - - - - - - - - - - - - */ patriciaTree.Set(key2, _longLeaf1); patriciaTree.Set(key3, _longLeaf1); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.Set(key3, Array.Empty()); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(1); + trieStore.CommitPatriciaTrie(1, patriciaTree); memDb.Keys.Should().HaveCount(8); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -702,11 +703,11 @@ public void When_two_branches_with_two_same_children_change_one_and_change_back_ patriciaTree.Set(_keyC, _longLeaf1); patriciaTree.Set(_keyD, _longLeaf1); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(0); + trieStore.CommitPatriciaTrie(0, patriciaTree); patriciaTree.Set(_keyA, _longLeaf3); patriciaTree.Set(_keyA, _longLeaf1); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(1); + trieStore.CommitPatriciaTrie(1, patriciaTree); memDb.Keys.Should().HaveCount(8); PatriciaTree checkTree = CreateCheckTree(memDb, patriciaTree); @@ -788,7 +789,7 @@ public void Fuzz_accounts( streamWriter.WriteLine( $"Commit block {blockNumber} | empty: {isEmptyBlock}"); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(blockNumber); + trieStore.CommitPatriciaTrie(blockNumber, patriciaTree); rootQueue.Enqueue(patriciaTree.RootHash); } @@ -930,7 +931,7 @@ public void Fuzz_accounts_with_reorganizations( streamWriter.WriteLine( $"Commit block {blockCount} | empty: {isEmptyBlock}"); patriciaTree.UpdateRootHash(); - patriciaTree.Commit(blockCount); + trieStore.CommitPatriciaTrie(blockNumber, patriciaTree); rootQueue.Enqueue(patriciaTree.RootHash); rootStack.Push(patriciaTree.RootHash); blockCount++; diff --git a/src/Nethermind/Nethermind.Trie.Test/VisitingTests.cs b/src/Nethermind/Nethermind.Trie.Test/VisitingTests.cs index 28a7d2bb514..1f773711e92 100644 --- a/src/Nethermind/Nethermind.Trie.Test/VisitingTests.cs +++ b/src/Nethermind/Nethermind.Trie.Test/VisitingTests.cs @@ -39,7 +39,7 @@ public void Visitors_state(VisitingOptions options) patriciaTree.Set(raw, Rlp.Encode(new Account(10, (UInt256)(10_000_000 + i)))); } - patriciaTree.Commit(0); + using (trieStore.BeginBlockCommit(0)) { patriciaTree.Commit(); } var visitor = new AppendingVisitor(); @@ -70,6 +70,8 @@ public void Visitors_storage(VisitingOptions options) byte[] value = Enumerable.Range(1, 32).Select(i => (byte)i).ToArray(); Hash256 stateRootHash = Keccak.Zero; + var blockCommit = trieStore.BeginBlockCommit(0); + for (int outi = 0; outi < 64; outi++) { ValueHash256 stateKey = default; @@ -82,7 +84,7 @@ public void Visitors_storage(VisitingOptions options) storageKey.BytesAsSpan[i / 2] = (byte)(1 << (4 * (1 - i % 2))); storage.Set(storageKey, value); } - storage.Commit(0); + storage.Commit(); stateRootHash = storage.RootHash; } @@ -99,7 +101,8 @@ public void Visitors_storage(VisitingOptions options) new Account(10, (UInt256)(10_000_000 + i), stateRootHash, Keccak.OfAnEmptySequenceRlp)); } - stateTree.Commit(0); + stateTree.Commit(); + blockCommit.Dispose(); var visitor = new AppendingVisitor(); diff --git a/src/Nethermind/Nethermind.Trie/CachedTrieStore.cs b/src/Nethermind/Nethermind.Trie/CachedTrieStore.cs index 5b23ba6ad92..984ab33ec07 100644 --- a/src/Nethermind/Nethermind.Trie/CachedTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/CachedTrieStore.cs @@ -34,8 +34,8 @@ public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address) => public INodeStorage.KeyScheme Scheme => @base.Scheme; - public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => - @base.BeginCommit(trieType, blockNumber, root, writeFlags); + public ICommitter BeginCommit(TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => + @base.BeginCommit(root, writeFlags); public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => @base.IsPersisted(in path, in keccak); diff --git a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs index 88b443a3983..2ff2a442f5b 100644 --- a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs +++ b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs @@ -131,7 +131,7 @@ public PatriciaTree( _bufferPool = bufferPool; } - public void Commit(long blockNumber, bool skipRoot = false, WriteFlags writeFlags = WriteFlags.None) + public void Commit(bool skipRoot = false, WriteFlags writeFlags = WriteFlags.None) { if (!_allowCommits) { @@ -140,14 +140,15 @@ public void Commit(long blockNumber, bool skipRoot = false, WriteFlags writeFlag int maxLevelForConcurrentCommit = _writeBeforeCommit switch { - > 64 * 16 => 1, // we separate at two top levels - > 64 => 0, // we separate at top level + > 4 * 16 * 16 => 2, // we separate at three top levels + > 4 * 16 => 1, // we separate at two top levels + > 4 => 0, // we separate at top level _ => -1 }; _writeBeforeCommit = 0; - using (ICommitter committer = TrieStore.BeginCommit(TrieType, blockNumber, RootRef, writeFlags)) + using (ICommitter committer = TrieStore.BeginCommit(RootRef, writeFlags)) { if (RootRef is not null && RootRef.IsDirty) { @@ -159,14 +160,6 @@ public void Commit(long blockNumber, bool skipRoot = false, WriteFlags writeFlag SetRootHash(RootRef.Keccak!, true); } } - - if (_logger.IsDebug) Debug(blockNumber); - - [MethodImpl(MethodImplOptions.NoInlining)] - void Debug(long blockNumber) - { - _logger.Debug($"Finished committing {RootRef?.Keccak} in block {blockNumber}"); - } } private void Commit(ICommitter committer, ref TreePath path, NodeCommitInfo nodeCommitInfo, int maxLevelForConcurrentCommit, bool skipSelf = false) @@ -214,7 +207,7 @@ Task CreateTaskForPath(TreePath childPath, TrieNode childNode, int idx) => Task. { if (node.IsChildDirty(i)) { - if (i < 15 && committer.CanSpawnTask()) + if (i < 15 && committer.TryRequestConcurrentQuota()) { childTasks ??= new ArrayPoolList(15); TreePath childPath = path.Append(i); diff --git a/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs b/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs index 82d83a3e8f3..ece7d00245b 100644 --- a/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs @@ -5,7 +5,6 @@ using System.Collections.Concurrent; using System.Numerics; using Nethermind.Core; -using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Trie.Pruning; @@ -34,9 +33,14 @@ public void Dispose() _inner.Dispose(); } - public ICommitter BeginCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags) + public ICommitter BeginCommit(Hash256? address, TrieNode? root, WriteFlags writeFlags) { - return _inner.BeginCommit(trieType, blockNumber, address, root, writeFlags); + return _inner.BeginCommit(address, root, writeFlags); + } + + public IBlockCommitter BeginBlockCommit(long blockNumber) + { + return _inner.BeginBlockCommit(blockNumber); } public bool IsPersisted(Hash256? address, in TreePath path, in ValueHash256 keccak) diff --git a/src/Nethermind/Nethermind.Trie/Pruning/BlockCommitPackage.cs b/src/Nethermind/Nethermind.Trie/Pruning/BlockCommitPackage.cs index 3ea7daf397a..c5993090cac 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/BlockCommitPackage.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/BlockCommitPackage.cs @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Diagnostics; + namespace Nethermind.Trie.Pruning { internal class BlockCommitSet @@ -25,5 +27,23 @@ public override string ToString() { return $"{BlockNumber}({Root})"; } + + /// + /// Prunes persisted branches of the current commit set root. + /// + public void Prune() + { + long start = Stopwatch.GetTimestamp(); + + // We assume that the most recent package very likely resolved many persisted nodes and only replaced + // some top level branches. Any of these persisted nodes are held in cache now so we just prune them here + // to avoid the references still being held after we prune the cache. + // We prune them here but just up to two levels deep which makes it a very lightweight operation. + // Note that currently the TrieNode ResolveChild un-resolves any persisted child immediately which + // may make this call unnecessary. + Root?.PrunePersistedRecursively(2); + Metrics.DeepPruningTime = (long)Stopwatch.GetElapsedTime(start).TotalMilliseconds; + } + } } diff --git a/src/Nethermind/Nethermind.Trie/Pruning/IScopedTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/IScopedTrieStore.cs index 3dec5d49e25..6c4a445d86b 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/IScopedTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/IScopedTrieStore.cs @@ -13,7 +13,8 @@ namespace Nethermind.Trie.Pruning; /// public interface IScopedTrieStore : ITrieNodeResolver { - ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None); + // Begins a commit to update the trie store. The `ICommitter` provide `CommitNode` to add node into. + ICommitter BeginCommit(TrieNode? root, WriteFlags writeFlags = WriteFlags.None); // Only used by snap provider, so ValueHash instead of Hash bool IsPersisted(in TreePath path, in ValueHash256 keccak); @@ -26,6 +27,6 @@ public interface ICommitter : IDisposable { void CommitNode(ref TreePath path, NodeCommitInfo nodeCommitInfo); - bool CanSpawnTask() => false; + bool TryRequestConcurrentQuota() => false; void ReturnConcurrencyQuota() { } } diff --git a/src/Nethermind/Nethermind.Trie/Pruning/ITrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/ITrieStore.cs index f9f2f59d55e..3178da3fd6d 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/ITrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/ITrieStore.cs @@ -11,10 +11,8 @@ namespace Nethermind.Trie.Pruning /// /// Full traditional trie store. /// - public interface ITrieStore : IDisposable + public interface ITrieStore : IDisposable, ITrieStoreInternal { - bool IsPersisted(Hash256? address, in TreePath path, in ValueHash256 keccak); - IReadOnlyTrieStore AsReadOnly(INodeStorage? keyValueStore = null); event EventHandler? ReorgBoundaryReached; @@ -22,22 +20,49 @@ public interface ITrieStore : IDisposable // Used for serving via hash IReadOnlyKeyValueStore TrieNodeRlpStore { get; } - // Used by healing - void Set(Hash256? address, in TreePath path, in ValueHash256 keccak, byte[] rlp); - bool HasRoot(Hash256 stateRoot); IScopedTrieStore GetTrieStore(Hash256? address); + INodeStorage.KeyScheme Scheme { get; } + + /// + /// Begin a block commit for this block number. This call may be blocked if a memory pruning is currently happening. + /// This call is required during block processing for memory pruning and reorg boundary to function. + /// + /// + /// + IBlockCommitter BeginBlockCommit(long blockNumber); + } + /// + /// These methods are to be used by ScopedTrieStore. + /// It should be considered internal to TrieStore. + /// It should not be used directly, nor intercepted. + /// + public interface ITrieStoreInternal + { + // Used by healing + void Set(Hash256? address, in TreePath path, in ValueHash256 keccak, byte[] rlp); + ICommitter BeginCommit(Hash256? address, TrieNode? root, WriteFlags writeFlags); TrieNode FindCachedOrUnknown(Hash256? address, in TreePath path, Hash256 hash); byte[]? LoadRlp(Hash256? address, in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None); byte[]? TryLoadRlp(Hash256? address, in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None); - INodeStorage.KeyScheme Scheme { get; } - ICommitter BeginCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags); + bool IsPersisted(Hash256? address, in TreePath path, in ValueHash256 keccak); } public interface IPruningTrieStore { public void PersistCache(CancellationToken cancellationToken); } + + /// + /// A block committer identifies the scope at which a commit for a block should happen. + /// The commit started via which is called by + /// Depending on , multiple patricia trie commit may run at the same time. + /// + public interface IBlockCommitter : IDisposable + { + bool TryRequestConcurrencyQuota() => false; + void ReturnConcurrencyQuota() { } + } } diff --git a/src/Nethermind/Nethermind.Trie/Pruning/NullCommitter.cs b/src/Nethermind/Nethermind.Trie/Pruning/NullCommitter.cs new file mode 100644 index 00000000000..c39ddc374bf --- /dev/null +++ b/src/Nethermind/Nethermind.Trie/Pruning/NullCommitter.cs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; + +namespace Nethermind.Trie.Pruning; + +internal class NullCommitter : ICommitter, IBlockCommitter +{ + public static NullCommitter Instance = new NullCommitter(); + + private NullCommitter() + { + } + + public void Dispose() { } + + public void CommitNode(ref TreePath path, NodeCommitInfo nodeCommitInfo) { } + public ICommitter GetTrieCommitter(Hash256? address, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => this; +} diff --git a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs index 585050f7150..5e96cb610ea 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Nethermind.Core; @@ -21,7 +22,7 @@ private NullTrieStore() { } public byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => []; - public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => new NullCommitter(); + public ICommitter BeginCommit(TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => NullCommitter.Instance; public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => true; @@ -30,12 +31,5 @@ public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) { } public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256 storageRoot) => this; public INodeStorage.KeyScheme Scheme => INodeStorage.KeyScheme.HalfPath; - - internal class NullCommitter : ICommitter - { - public void Dispose() { } - - public void CommitNode(ref TreePath path, NodeCommitInfo nodeCommitInfo) { } - } } } diff --git a/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs index 4bb2c8d537f..4f66fb73e70 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs @@ -29,8 +29,12 @@ public byte[] LoadRlp(Hash256? address, in TreePath treePath, Hash256 hash, Read public IReadOnlyTrieStore AsReadOnly(INodeStorage nodeStore) => new ReadOnlyTrieStore(_trieStore, nodeStore); - public ICommitter BeginCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags) => - new NullTrieStore.NullCommitter(); + public ICommitter BeginCommit(Hash256? address, TrieNode? root, WriteFlags writeFlags) => NullCommitter.Instance; + + public IBlockCommitter BeginBlockCommit(long blockNumber) + { + return NullCommitter.Instance; + } public event EventHandler ReorgBoundaryReached { @@ -63,8 +67,7 @@ public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address1) => public INodeStorage.KeyScheme Scheme => fullTrieStore.Scheme; - public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => - new NullTrieStore.NullCommitter(); + public ICommitter BeginCommit(TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => NullCommitter.Instance; public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => fullTrieStore.IsPersisted(address, path, in keccak); diff --git a/src/Nethermind/Nethermind.Trie/Pruning/ScopedTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/ScopedTrieStore.cs index 650ee399086..66a61af6f74 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/ScopedTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/ScopedTrieStore.cs @@ -22,8 +22,8 @@ public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address1) => public INodeStorage.KeyScheme Scheme => fullTrieStore.Scheme; - public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => - fullTrieStore.BeginCommit(trieType, blockNumber, address, root, writeFlags); + public ICommitter BeginCommit(TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => + fullTrieStore.BeginCommit(address, root, writeFlags); public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => fullTrieStore.IsPersisted(address, path, in keccak); diff --git a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs index ac44f42a3e0..b5c6bbcab66 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs @@ -27,8 +27,6 @@ public class TrieStore : ITrieStore, IPruningTrieStore private int _isFirst; - private INodeStorage.WriteBatch? _currentBatch = null; - private readonly TrieStoreDirtyNodesCache[] _dirtyNodes = []; private readonly Task[] _dirtyNodesTasks = []; private readonly ConcurrentDictionary[] _persistedHashes = []; @@ -164,8 +162,10 @@ public int CachedNodesCount } } - private void CommitNode(long blockNumber, Hash256? address, ref TreePath path, in NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None) + private void CommitAndInsertToDirtyNodes(long blockNumber, Hash256? address, ref TreePath path, in NodeCommitInfo nodeCommitInfo) { + Debug.Assert(_pruningStrategy.PruningEnabled); + if (_logger.IsTrace) Trace(blockNumber, in nodeCommitInfo); if (!nodeCommitInfo.IsEmptyBlockMarker && !nodeCommitInfo.Node.IsBoundaryProofNode) { @@ -181,18 +181,9 @@ private void CommitNode(long blockNumber, Hash256? address, ref TreePath path, i ThrowNodeHasBeenSeen(blockNumber, node); } - if (_pruningStrategy.PruningEnabled) - { - node = SaveOrReplaceInDirtyNodesCache(address, ref path, nodeCommitInfo, node); - } - + node = SaveOrReplaceInDirtyNodesCache(address, ref path, nodeCommitInfo, node); node.LastSeen = Math.Max(blockNumber, node.LastSeen); - if (!_pruningStrategy.PruningEnabled) - { - PersistNode(address, path, node, blockNumber, writeFlags); - } - IncrementCommittedNodesCount(); } @@ -211,6 +202,29 @@ void Trace(long blockNumber, in NodeCommitInfo nodeCommitInfo) static void ThrowNodeHasBeenSeen(long blockNumber, TrieNode node) => throw new TrieStoreException($"{nameof(TrieNode.LastSeen)} set on {node} committed at {blockNumber}."); } + private void CommitAndPersistNode(Hash256? address, ref TreePath path, in NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags, INodeStorage.WriteBatch? writeBatch) + { + Debug.Assert(!_pruningStrategy.PruningEnabled); + + if (!nodeCommitInfo.IsEmptyBlockMarker && !nodeCommitInfo.Node.IsBoundaryProofNode) + { + TrieNode node = nodeCommitInfo.Node; + + if (node.Keccak is null) + { + ThrowUnknownHash(node); + } + + PersistNode(address, path, node, writeBatch!, writeFlags); + + IncrementCommittedNodesCount(); + } + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowUnknownHash(TrieNode node) => throw new TrieStoreException($"The hash of {node} should be known at the time of committing."); + } + private int GetNodeShardIdx(in TreePath path, Hash256 hash) => // When enabled, the shard have dictionaries for tracking past path hash also. // So the same path need to be in the same shard for the remove logic to work. @@ -289,70 +303,61 @@ static void ThrowNodeIsNotSame(TrieNode node, TrieNode cachedNodeCopy) => throw new InvalidOperationException($"The hash of replacement node {cachedNodeCopy} is not the same as the original {node}."); } - public ICommitter BeginCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags) + public ICommitter BeginCommit(Hash256? address, TrieNode? root, WriteFlags writeFlags) { - ArgumentOutOfRangeException.ThrowIfNegative(blockNumber); - EnsureCommitSetExistsForBlock(blockNumber); - - int concurrency = _pruningStrategy.PruningEnabled - ? Environment.ProcessorCount - : 0; // The write batch when pruning is not enabled is not concurrent safe + if (_pruningStrategy.PruningEnabled) + { + if (_currentBlockCommitter is null) throw new InvalidOperationException($"With pruning triestore, {nameof(BeginBlockCommit)} must be called."); + } - return new TrieStoreCommitter(this, trieType, blockNumber, address, root, writeFlags, concurrency); + return _currentBlockCommitter is not null + // Note, archive node still use this path. This is because it needs the block commit set to handle reorg announcement. + ? _currentBlockCommitter.GetTrieCommitter(address, root, writeFlags) + // This happens when there are no block involved, such as during snap sync or just calculating patricia root. + : new NonPruningTrieStoreCommitter(this, address, _nodeStorage.StartWriteBatch(), writeFlags); } - private void FinishBlockCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) + public IBlockCommitter BeginBlockCommit(long blockNumber) { - try + if (_pruningStrategy.PruningEnabled) { - if (trieType == TrieType.State) // storage tries happen before state commits + if (_currentBlockCommitter is not null) { - if (_logger.IsTrace) _logger.Trace($"Enqueued blocks {_commitSetQueue?.Count ?? 0}"); - BlockCommitSet set = CurrentPackage; - if (set is not null) - { - if (_logger.IsTrace) _logger.Trace($"Current root (block {blockNumber}): {root}, block {set.BlockNumber}"); - set.Seal(root); - } - - bool shouldPersistSnapshot = _persistenceStrategy.ShouldPersist(set.BlockNumber); - if (shouldPersistSnapshot) - { - _currentBatch ??= _nodeStorage.StartWriteBatch(); - try - { - PersistBlockCommitSet(address, set, _currentBatch, writeFlags: writeFlags); - PruneCurrentSet(); - } - finally - { - // For safety we prefer to commit half of the batch rather than not commit at all. - // Generally hanging nodes are not a problem in the DB but anything missing from the DB is. - _currentBatch?.Dispose(); - _currentBatch = null; - } - } - else - { - PruneCurrentSet(); - } - - CurrentPackage = null; - if (_pruningStrategy.PruningEnabled && Monitor.IsEntered(_dirtyNodesLock)) - { - Monitor.Exit(_dirtyNodesLock); - } + throw new InvalidOperationException("Cannot start a new block commit when an existing one is still not closed"); } + + Monitor.Enter(_dirtyNodesLock); } - finally + + _currentBlockCommitter = new BlockCommitter(this, CreateCommitSet(blockNumber)); + return _currentBlockCommitter; + } + + private void FinishBlockCommit(BlockCommitSet set, TrieNode? root) + { + if (_logger.IsTrace) _logger.Trace($"Enqueued blocks {_commitSetQueue?.Count ?? 0}"); + set.Seal(root); + + bool shouldPersistSnapshot = _persistenceStrategy.ShouldPersist(set.BlockNumber); + if (shouldPersistSnapshot) { - _currentBatch?.Dispose(); - _currentBatch = null; + // For safety we prefer to commit half of the batch rather than not commit at all. + // Generally hanging nodes are not a problem in the DB but anything missing from the DB is. + using INodeStorage.WriteBatch currentBatch = _nodeStorage.StartWriteBatch(); + ParallelPersistBlockCommitSet(set); } + set.Prune(); + + _currentBlockCommitter = null; + + if (_pruningStrategy.PruningEnabled) + Monitor.Exit(_dirtyNodesLock); + Prune(); } + public event EventHandler? ReorgBoundaryReached; public byte[]? TryLoadRlp(Hash256? address, in TreePath path, Hash256 keccak, INodeStorage? nodeStorage, ReadFlags readFlags = ReadFlags.None) @@ -544,7 +549,7 @@ private bool SaveSnapshot() { BlockCommitSet blockCommitSet = candidateSets[index]; if (_logger.IsDebug) _logger.Debug($"Elevated pruning for candidate {blockCommitSet.BlockNumber}"); - ParallelPersistBlockCommitSet(null, blockCommitSet, persistedNodeRecorder); + ParallelPersistBlockCommitSet(blockCommitSet, persistedNodeRecorder); } Task deleteTask = shouldDeletePersistedNode ? RemovePastKeys() : Task.CompletedTask; @@ -599,23 +604,6 @@ private void PersistedNodeRecorder(TreePath treePath, Hash256 address, TrieNode } } - /// - /// Prunes persisted branches of the current commit set root. - /// - private void PruneCurrentSet() - { - long start = Stopwatch.GetTimestamp(); - - // We assume that the most recent package very likely resolved many persisted nodes and only replaced - // some top level branches. Any of these persisted nodes are held in cache now so we just prune them here - // to avoid the references still being held after we prune the cache. - // We prune them here but just up to two levels deep which makes it a very lightweight operation. - // Note that currently the TrieNode ResolveChild un-resolves any persisted child immediately which - // may make this call unnecessary. - CurrentPackage?.Root?.PrunePersistedRecursively(2); - Metrics.DeepPruningTime = (long)Stopwatch.GetElapsedTime(start).TotalMilliseconds; - } - /// /// This method is responsible for reviewing the nodes that are directly in the cache and /// removing ones that are either no longer referenced or already persisted. @@ -682,6 +670,13 @@ public void WaitForPruning() private ConcurrentQueue _commitSetQueue; + private ConcurrentQueue CommitSetQueue => + (_commitSetQueue ?? CreateQueueAtomic(ref _commitSetQueue)); + +#if DEBUG + private BlockCommitSet? _lastCommitSet = null; +#endif + private long _memoryUsedByDirtyCache; private int _committedNodesCount; @@ -690,9 +685,8 @@ public void WaitForPruning() private long _latestPersistedBlockNumber; - private BlockCommitSet? CurrentPackage { get; set; } + private BlockCommitter? _currentBlockCommitter = null; - private bool IsCurrentListSealed => CurrentPackage is null || CurrentPackage.IsSealed; private long LatestCommittedBlockNumber { get; set; } public INodeStorage.KeyScheme Scheme => _nodeStorage.Scheme; @@ -705,22 +699,30 @@ private static ConcurrentQueue CreateQueueAtomic(ref ConcurrentQ return prior ?? instance; } - private void CreateCommitSet(long blockNumber) + private BlockCommitSet CreateCommitSet(long blockNumber) { if (_logger.IsDebug) _logger.Debug($"Beginning new {nameof(BlockCommitSet)} - {blockNumber}"); // TODO: this throws on reorgs, does it not? let us recreate it in test - Debug.Assert(CurrentPackage is null || blockNumber == CurrentPackage.BlockNumber + 1, "Newly begun block is not a successor of the last one"); - Debug.Assert(IsCurrentListSealed, "Not sealed when beginning new block"); +#if DEBUG + Debug.Assert(_lastCommitSet == null || blockNumber == _lastCommitSet.BlockNumber + 1 || _lastCommitSet.BlockNumber == 0, $"Newly begun block is not a successor of the last one."); + Debug.Assert(_lastCommitSet == null || _lastCommitSet.IsSealed, "Not sealed when beginning new block"); +#endif BlockCommitSet commitSet = new(blockNumber); - (_commitSetQueue ?? CreateQueueAtomic(ref _commitSetQueue)).Enqueue(commitSet); + CommitSetQueue.Enqueue(commitSet); + +#if DEBUG + _lastCommitSet = commitSet; +#endif + LatestCommittedBlockNumber = Math.Max(blockNumber, LatestCommittedBlockNumber); + // Why are we announcing **before** committing next block?? + // Should it be after commit? AnnounceReorgBoundaries(); DequeueOldCommitSets(); - CurrentPackage = commitSet; - Debug.Assert(ReferenceEquals(CurrentPackage, commitSet), $"Current {nameof(BlockCommitSet)} is not same as the new package just after adding"); + return commitSet; } /// @@ -728,40 +730,10 @@ private void CreateCommitSet(long blockNumber) /// Already persisted nodes are skipped. After this action we are sure that the full state is available /// for the block represented by this commit set. /// - /// /// A commit set of a block which root is to be persisted. - /// The write batch to write to - /// Track persisted hashes in this dictionary if not null + /// Special action to be called on each persist. Used to track which node to remove. /// - private void PersistBlockCommitSet( - Hash256? address, - BlockCommitSet commitSet, - INodeStorage.WriteBatch writeBatch, - Action? persistedNodeRecorder = null, - WriteFlags writeFlags = WriteFlags.None - ) - { - void PersistNode(TrieNode tn, Hash256? address2, TreePath path) - { - persistedNodeRecorder?.Invoke(path, address2, tn); - this.PersistNode(address2, path, tn, commitSet.BlockNumber, writeFlags, writeBatch); - } - - if (_logger.IsDebug) _logger.Debug($"Persisting from root {commitSet.Root} in {commitSet.BlockNumber}"); - - long start = Stopwatch.GetTimestamp(); - TreePath path = TreePath.Empty; - commitSet.Root?.CallRecursively(PersistNode, address, ref path, GetTrieStore(null), true, _logger); - long elapsedMilliseconds = (long)Stopwatch.GetElapsedTime(start).TotalMilliseconds; - Metrics.SnapshotPersistenceTime = elapsedMilliseconds; - - if (_logger.IsDebug) _logger.Debug($"Persisted trie from {commitSet.Root} at {commitSet.BlockNumber} in {elapsedMilliseconds}ms (cache memory {MemoryUsedByDirtyCache})"); - - LastPersistedBlockNumber = commitSet.BlockNumber; - } - private void ParallelPersistBlockCommitSet( - Hash256? address, BlockCommitSet commitSet, Action? persistedNodeRecorder = null, WriteFlags writeFlags = WriteFlags.None @@ -777,7 +749,7 @@ void TopLevelPersist(TrieNode tn, Hash256? address2, TreePath path) if (path.Length < parallelBoundaryPathLength) { persistedNodeRecorder?.Invoke(path, address2, tn); - PersistNode(address2, path, tn, commitSet.BlockNumber, writeFlags, topLevelWriteBatch); + PersistNode(address2, path, tn, topLevelWriteBatch, writeFlags); } else { @@ -791,7 +763,7 @@ void TopLevelPersist(TrieNode tn, Hash256? address2, TreePath path) // The first CallRecursive stop at two level, yielding 256 node in parallelStartNodes, which is run concurrently TreePath path = TreePath.Empty; - commitSet.Root?.CallRecursively(TopLevelPersist, address, ref path, GetTrieStore(null), true, _logger, maxPathLength: parallelBoundaryPathLength); + commitSet.Root?.CallRecursively(TopLevelPersist, null, ref path, GetTrieStore(null), true, _logger, maxPathLength: parallelBoundaryPathLength); // The amount of change in the subtrees are not balanced at all. So their writes ares buffered here // which get disposed in parallel instead of being disposed in `PersistNodeStartingFrom`. @@ -815,7 +787,7 @@ void TopLevelPersist(TrieNode tn, Hash256? address2, TreePath path) Task.WaitAll(parallelStartNodes.Select(entry => Task.Run(() => { (TrieNode trieNode, Hash256? address2, TreePath path2) = entry; - PersistNodeStartingFrom(trieNode, address2, path2, commitSet, persistedNodeRecorder, writeFlags, disposeQueue); + PersistNodeStartingFrom(trieNode, address2, path2, persistedNodeRecorder, writeFlags, disposeQueue); })).ToArray()); disposeQueue.CompleteAdding(); @@ -832,7 +804,7 @@ void TopLevelPersist(TrieNode tn, Hash256? address2, TreePath path) LastPersistedBlockNumber = commitSet.BlockNumber; } - private void PersistNodeStartingFrom(TrieNode tn, Hash256 address2, TreePath path, BlockCommitSet commitSet, + private void PersistNodeStartingFrom(TrieNode tn, Hash256 address2, TreePath path, Action? persistedNodeRecorder, WriteFlags writeFlags, BlockingCollection disposeQueue) { @@ -842,7 +814,7 @@ private void PersistNodeStartingFrom(TrieNode tn, Hash256 address2, TreePath pat void DoPersist(TrieNode node, Hash256? address3, TreePath path2) { persistedNodeRecorder?.Invoke(path2, address3, node); - PersistNode(address3, path2, node, commitSet.BlockNumber, writeFlags, writeBatch); + PersistNode(address3, path2, node, writeBatch, writeFlags); persistedNodeCount++; if (persistedNodeCount % 512 == 0) @@ -856,23 +828,20 @@ void DoPersist(TrieNode node, Hash256? address3, TreePath path2) disposeQueue.Add(writeBatch); } - private void PersistNode(Hash256? address, in TreePath path, TrieNode currentNode, long blockNumber, WriteFlags writeFlags = WriteFlags.None, INodeStorage.WriteBatch? writeBatch = null) + private void PersistNode(Hash256? address, in TreePath path, TrieNode currentNode, INodeStorage.WriteBatch writeBatch, WriteFlags writeFlags = WriteFlags.None) { - writeBatch ??= _currentBatch ??= _nodeStorage.StartWriteBatch(); ArgumentNullException.ThrowIfNull(currentNode); if (currentNode.Keccak is not null) { - Debug.Assert(currentNode.LastSeen >= 0, $"Cannot persist a dangling node (without {(nameof(TrieNode.LastSeen))} value set)."); // Note that the LastSeen value here can be 'in the future' (greater than block number // if we replaced a newly added node with an older copy and updated the LastSeen value. // Here we reach it from the old root so it appears to be out of place but it is correct as we need // to prevent it from being removed from cache and also want to have it persisted. - if (_logger.IsTrace) _logger.Trace($"Persisting {nameof(TrieNode)} {currentNode} in snapshot {blockNumber}."); + if (_logger.IsTrace) _logger.Trace($"Persisting {nameof(TrieNode)} {currentNode}."); writeBatch.Set(address, path, currentNode.Keccak, currentNode.FullRlp, writeFlags); currentNode.IsPersisted = true; - currentNode.LastSeen = Math.Max(blockNumber, currentNode.LastSeen); IncrementPersistedNodesCount(); } else @@ -912,19 +881,6 @@ private void DequeueOldCommitSets() } } - private void EnsureCommitSetExistsForBlock(long blockNumber) - { - if (CurrentPackage is null) - { - if (_pruningStrategy.PruningEnabled && !Monitor.IsEntered(_dirtyNodesLock)) - { - Monitor.Enter(_dirtyNodesLock); - } - - CreateCommitSet(blockNumber); - } - } - private void AnnounceReorgBoundaries() { if (LatestCommittedBlockNumber < 1) @@ -993,7 +949,7 @@ private void PersistOnShutdown() { BlockCommitSet blockCommitSet = candidateSets[index]; if (_logger.IsDebug) _logger.Debug($"Persisting on disposal {blockCommitSet} (cache memory at {MemoryUsedByDirtyCache})"); - PersistBlockCommitSet(null, blockCommitSet, writeBatch); + ParallelPersistBlockCommitSet(blockCommitSet); } writeBatch.Dispose(); @@ -1031,10 +987,8 @@ void ClearCommitSetQueue() } commitSetCount++; - using INodeStorage.WriteBatch writeBatch = _nodeStorage.StartWriteBatch(); - PersistBlockCommitSet(null, commitSet, writeBatch); + ParallelPersistBlockCommitSet(commitSet); } - PruneCurrentSet(); } if (!(_commitSetQueue?.IsEmpty ?? true)) @@ -1130,19 +1084,50 @@ public bool HasRoot(Hash256 stateRoot) return true; } - private class TrieStoreCommitter( + private class BlockCommitter( + TrieStore trieStore, + BlockCommitSet commitSet + ) : IBlockCommitter + { + internal TrieNode? StateRoot = null; + private int _concurrency = trieStore._pruningStrategy.PruningEnabled ? Environment.ProcessorCount : 0; + + public void Dispose() + { + trieStore.FinishBlockCommit(commitSet, StateRoot); + } + + public ICommitter GetTrieCommitter(Hash256? address, TrieNode? root, WriteFlags writeFlags) + { + if (address is null) StateRoot = root; + return trieStore._pruningStrategy.PruningEnabled + ? new PruningTrieStoreCommitter(this, trieStore, commitSet.BlockNumber, address, root) + : new NonPruningTrieStoreCommitter(trieStore, address, trieStore._nodeStorage.StartWriteBatch(), writeFlags); + } + + public bool TryRequestConcurrencyQuota() + { + if (Interlocked.Decrement(ref _concurrency) >= 0) + { + return true; + } + + ReturnConcurrencyQuota(); + return false; + } + + public void ReturnConcurrencyQuota() => Interlocked.Increment(ref _concurrency); + } + + private class PruningTrieStoreCommitter( + BlockCommitter blockCommitter, TrieStore trieStore, - TrieType trieType, long blockNumber, Hash256? address, - TrieNode? root, - WriteFlags writeFlags, - int concurrency + TrieNode root ) : ICommitter { private readonly bool _needToResetRoot = root is not null && root.IsDirty; - private int _concurrency = concurrency; - private TrieNode? _root = root; public void Dispose() { @@ -1150,29 +1135,40 @@ public void Dispose() { // During commit it PatriciaTrie, the root may get resolved to an existing node (same keccak). // This ensure that the root that we use here is the same. - _root = trieStore.FindCachedOrUnknown(address, TreePath.Empty, _root?.Keccak); + // This is only needed for state tree as the root need to be put in the block commit set. + if (address == null) blockCommitter.StateRoot = trieStore.FindCachedOrUnknown(address, TreePath.Empty, root?.Keccak); } - - trieStore.FinishBlockCommit(trieType, blockNumber, address, _root, writeFlags); } public void CommitNode(ref TreePath path, NodeCommitInfo nodeCommitInfo) => - trieStore.CommitNode(blockNumber, address, ref path, nodeCommitInfo, writeFlags: writeFlags); + trieStore.CommitAndInsertToDirtyNodes(blockNumber, address, ref path, nodeCommitInfo); - public bool CanSpawnTask() + public bool TryRequestConcurrentQuota() => blockCommitter.TryRequestConcurrencyQuota(); + + public void ReturnConcurrencyQuota() => blockCommitter.ReturnConcurrencyQuota(); + } + + private class NonPruningTrieStoreCommitter( + TrieStore trieStore, + Hash256? address, + INodeStorage.WriteBatch writeBatch, + WriteFlags writeFlags = WriteFlags.None + ) : ICommitter + { + public void Dispose() { - if (Interlocked.Decrement(ref _concurrency) >= 0) - { - return true; - } + writeBatch.Dispose(); + } - ReturnConcurrencyQuota(); - return false; + public void CommitNode(ref TreePath path, NodeCommitInfo nodeCommitInfo) + { + trieStore.CommitAndPersistNode(address, ref path, nodeCommitInfo, writeFlags: writeFlags, writeBatch: writeBatch); } - public void ReturnConcurrencyQuota() => Interlocked.Increment(ref _concurrency); - } + public bool TryRequestConcurrentQuota() => false; + public void ReturnConcurrencyQuota() { } + } internal static class HashHelpers { diff --git a/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs b/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs index e93f1096d06..499f6f18501 100644 --- a/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs +++ b/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs @@ -10,8 +10,8 @@ namespace Nethermind.Trie; public class TrieStoreWithReadFlags(IScopedTrieStore implementation, ReadFlags flags) : TrieNodeResolverWithReadFlags(implementation, flags), IScopedTrieStore { - public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => - implementation.BeginCommit(trieType, blockNumber, root, writeFlags); + public ICommitter BeginCommit(TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => + implementation.BeginCommit(root, writeFlags); public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => implementation.IsPersisted(in path, in keccak);