From 173a6631146b283f460f445e077e1df1789a50aa Mon Sep 17 00:00:00 2001 From: Ruben Buniatyan Date: Thu, 31 Oct 2024 20:31:21 +0100 Subject: [PATCH] Switch to `System.CommandLine` (#7653) --- scripts/dev-setup.sh | 4 +- scripts/infra.sh | 4 +- .../private-networking/clique-validators.sh | 6 +- scripts/syncSettings.py | 4 +- src/Nethermind/Chains/kovan.json | 268 ------ src/Nethermind/Directory.Packages.props | 5 +- .../Nethermind.Api.Test/PluginLoaderTests.cs | 29 +- .../SinglePluginLoaderTests.cs | 24 +- .../Extensions/IPluginLoader.cs | 14 +- .../Nethermind.Api/Extensions/PluginLoader.cs | 174 ++-- .../Extensions/SinglePluginLoader.cs | 28 +- .../Nethermind.Cli/Nethermind.Cli.csproj | 2 +- src/Nethermind/Nethermind.Cli/Program.cs | 249 +++-- ...figProvider_FindIncorrectSettings_Tests.cs | 238 +++-- .../JsonConfigProviderTests.cs | 4 +- .../Nethermind.Config.Test.csproj | 11 +- ...nfigWithTypos.cfg => ConfigWithTypos.json} | 0 ...tingNames.cfg => CorrectSettingNames.json} | 0 ...leJsonConfig.cfg => SampleJsonConfig.json} | 0 .../Nethermind.Config/ConfigProvider.cs | 21 +- .../Nethermind.Config/INoCategoryConfig.cs | 23 +- .../Nethermind.Config/IProcessExitSource.cs | 18 +- .../Nethermind.Config/JsonConfigSource.cs | 205 ++--- .../Nethermind.Config/NoCategoryConfig.cs | 28 +- src/Nethermind/Nethermind.Hive/HivePlugin.cs | 109 ++- .../Nethermind.Logging/PathUtils.cs | 81 +- .../MergePluginTests.cs | 2 +- ...redConfig.cfg => MisconfiguredConfig.json} | 0 .../Nethermind.Merge.Plugin.Test.csproj | 2 +- .../Framework/TestBuilder.cs | 443 +++++---- .../Nethermind.Overseer.Test.csproj | 6 +- .../configs/{auRaMiner.cfg => auRaMiner.json} | 0 .../{cliqueMiner.cfg => cliqueMiner.json} | 0 .../{cliqueNode.cfg => cliqueNode.json} | 0 .../ConfigFilesTests.cs | 74 +- .../EthereumRunnerTests.cs | 24 +- .../Nethermind.Runner.Test.csproj | 2 +- .../Ethereum/Api/ApiBuilder.cs | 121 ++- .../Ethereum/EthereumRunner.cs | 150 ++- .../Logging/NLogConfigurator.cs | 117 ++- src/Nethermind/Nethermind.Runner/NLog.config | 4 +- .../Nethermind.Runner.csproj | 5 +- src/Nethermind/Nethermind.Runner/Program.cs | 870 ++++++++---------- .../Properties/launchSettings.json | 136 +-- .../configs/{AuraTest.cfg => AuraTest.json} | 0 .../{base-mainnet.cfg => base-mainnet.json} | 0 ..._archive.cfg => base-mainnet_archive.json} | 0 .../{base-sepolia.cfg => base-sepolia.json} | 0 ..._archive.cfg => base-sepolia_archive.json} | 0 .../configs/{chiado.cfg => chiado.json} | 0 ...chiado_archive.cfg => chiado_archive.json} | 0 .../configs/{energyweb.cfg => energyweb.json} | 0 ...web_archive.cfg => energyweb_archive.json} | 0 .../configs/{exosama.cfg => exosama.json} | 0 ...osama_archive.cfg => exosama_archive.json} | 0 .../configs/{gnosis.cfg => gnosis.json} | 0 ...gnosis_archive.cfg => gnosis_archive.json} | 0 .../configs/{hive.cfg => hive.json} | 0 .../configs/{holesky.cfg => holesky.json} | 0 ...lesky_archive.cfg => holesky_archive.json} | 0 .../{joc-mainnet.cfg => joc-mainnet.json} | 0 ...t_archive.cfg => joc-mainnet_archive.json} | 0 .../{joc-testnet.cfg => joc-testnet.json} | 0 ...t_archive.cfg => joc-testnet_archive.json} | 0 .../{linea-mainnet.cfg => linea-mainnet.json} | 0 ...archive.cfg => linea-mainnet_archive.json} | 0 .../{linea-sepolia.cfg => linea-sepolia.json} | 0 ...archive.cfg => linea-sepolia_archive.json} | 0 .../configs/{mainnet.cfg => mainnet.json} | 0 ...innet_archive.cfg => mainnet_archive.json} | 0 .../configs/{none.cfg => none.json} | 0 .../{op-mainnet.cfg => op-mainnet.json} | 0 ...et_archive.cfg => op-mainnet_archive.json} | 0 .../{op-sepolia.cfg => op-sepolia.json} | 0 ...ia_archive.cfg => op-sepolia_archive.json} | 0 .../configs/{poacore.cfg => poacore.json} | 0 ...acore_archive.cfg => poacore_archive.json} | 0 ...e_validator.cfg => poacore_validator.json} | 0 .../configs/{sepolia.cfg => sepolia.json} | 0 ...polia_archive.cfg => sepolia_archive.json} | 0 .../configs/{spaceneth.cfg => spaceneth.json} | 0 ...rsistent.cfg => spaceneth_persistent.json} | 0 .../{taiko-hekla.cfg => taiko-hekla.json} | 0 .../{taiko-mainnet.cfg => taiko-mainnet.json} | 0 .../configs/{volta.cfg => volta.json} | 0 .../{volta_archive.cfg => volta_archive.json} | 0 .../Nethermind.Test.Runner.csproj | 2 +- .../Nethermind.Test.Runner/Program.cs | 162 ++-- src/Nethermind/nuget.config | 9 +- tools/DocGen/ConfigGenerator.cs | 1 + tools/DocGen/DocGen.csproj | 3 +- tools/DocGen/Program.cs | 99 +- tools/DocGen/Properties/launchSettings.json | 2 +- tools/DocGen/nuget.config | 7 - .../HiveCompare/HiveCompare.csproj | 2 +- tools/HiveCompare/HiveCompare/Program.cs | 59 +- tools/Nethermind.Tools.Kute/Config.cs | 173 ++-- .../Nethermind.Tools.Kute.csproj | 2 +- tools/Nethermind.Tools.Kute/Program.cs | 75 +- tools/SendBlobs/Program.cs | 42 +- tools/SendBlobs/SendBlobs.csproj | 2 +- tools/SendBlobs/SetupCli.cs | 435 +++++---- tools/nuget.config | 21 + 103 files changed, 2077 insertions(+), 2524 deletions(-) delete mode 100644 src/Nethermind/Chains/kovan.json rename src/Nethermind/Nethermind.Config.Test/SampleJson/{ConfigWithTypos.cfg => ConfigWithTypos.json} (100%) rename src/Nethermind/Nethermind.Config.Test/SampleJson/{CorrectSettingNames.cfg => CorrectSettingNames.json} (100%) rename src/Nethermind/Nethermind.Config.Test/SampleJson/{SampleJsonConfig.cfg => SampleJsonConfig.json} (100%) rename src/Nethermind/Nethermind.Merge.Plugin.Test/{MisconfiguredConfig.cfg => MisconfiguredConfig.json} (100%) rename src/Nethermind/Nethermind.Overseer.Test/configs/{auRaMiner.cfg => auRaMiner.json} (100%) rename src/Nethermind/Nethermind.Overseer.Test/configs/{cliqueMiner.cfg => cliqueMiner.json} (100%) rename src/Nethermind/Nethermind.Overseer.Test/configs/{cliqueNode.cfg => cliqueNode.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{AuraTest.cfg => AuraTest.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{base-mainnet.cfg => base-mainnet.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{base-mainnet_archive.cfg => base-mainnet_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{base-sepolia.cfg => base-sepolia.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{base-sepolia_archive.cfg => base-sepolia_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{chiado.cfg => chiado.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{chiado_archive.cfg => chiado_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{energyweb.cfg => energyweb.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{energyweb_archive.cfg => energyweb_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{exosama.cfg => exosama.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{exosama_archive.cfg => exosama_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{gnosis.cfg => gnosis.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{gnosis_archive.cfg => gnosis_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{hive.cfg => hive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{holesky.cfg => holesky.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{holesky_archive.cfg => holesky_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{joc-mainnet.cfg => joc-mainnet.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{joc-mainnet_archive.cfg => joc-mainnet_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{joc-testnet.cfg => joc-testnet.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{joc-testnet_archive.cfg => joc-testnet_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{linea-mainnet.cfg => linea-mainnet.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{linea-mainnet_archive.cfg => linea-mainnet_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{linea-sepolia.cfg => linea-sepolia.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{linea-sepolia_archive.cfg => linea-sepolia_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{mainnet.cfg => mainnet.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{mainnet_archive.cfg => mainnet_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{none.cfg => none.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{op-mainnet.cfg => op-mainnet.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{op-mainnet_archive.cfg => op-mainnet_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{op-sepolia.cfg => op-sepolia.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{op-sepolia_archive.cfg => op-sepolia_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{poacore.cfg => poacore.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{poacore_archive.cfg => poacore_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{poacore_validator.cfg => poacore_validator.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{sepolia.cfg => sepolia.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{sepolia_archive.cfg => sepolia_archive.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{spaceneth.cfg => spaceneth.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{spaceneth_persistent.cfg => spaceneth_persistent.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{taiko-hekla.cfg => taiko-hekla.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{taiko-mainnet.cfg => taiko-mainnet.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{volta.cfg => volta.json} (100%) rename src/Nethermind/Nethermind.Runner/configs/{volta_archive.cfg => volta_archive.json} (100%) delete mode 100644 tools/DocGen/nuget.config create mode 100644 tools/nuget.config diff --git a/scripts/dev-setup.sh b/scripts/dev-setup.sh index 40a7953666b..ee84104d15f 100644 --- a/scripts/dev-setup.sh +++ b/scripts/dev-setup.sh @@ -34,11 +34,11 @@ echo ======================================================= echo -e "\033[32m"; read -e -p "Which configuration/s (space separated) you wish to run? " -i "mainnet" config -for cfg in $config; do cp nethermind/configs/$cfg.cfg ~; done +for cfg in $config; do cp nethermind/configs/$cfg.json ~; done echo -e "\033[00m"; echo ======================================================= echo To run the node type: echo 1. screen -S node -echo 2. ./infra.sh config-name.cfg +echo 2. ./infra.sh config-name.json echo ======================================================= diff --git a/scripts/infra.sh b/scripts/infra.sh index aad2014ac36..9d8d0f13c27 100644 --- a/scripts/infra.sh +++ b/scripts/infra.sh @@ -17,5 +17,5 @@ mkdir ~/nethermind_$CONFIG/keystore cp ~/$CONFIG.key ~/nethermind_$CONFIG/keystore/node.key.plain DB_PATH="/root/db/$CONFIG" echo "DB PATH: " $DB_PATH -cat ~/$CONFIG.cfg | jq '.Init.BaseDbPath = "'$DB_PATH'"' | sponge ~/$CONFIG.cfg -dotnet nethermind.dll -c ../$CONFIG.cfg +cat ~/$CONFIG.json | jq '.Init.BaseDbPath = "'$DB_PATH'"' | sponge ~/$CONFIG.json +dotnet nethermind.dll -c ../$CONFIG.json diff --git a/scripts/private-networking/clique-validators.sh b/scripts/private-networking/clique-validators.sh index 1a8775772ca..cce07bc0119 100644 --- a/scripts/private-networking/clique-validators.sh +++ b/scripts/private-networking/clique-validators.sh @@ -127,7 +127,7 @@ docker-compose up #END of main function writeNethermindConfig() { -cat < node_$1/configs/config.cfg +cat < node_$1/configs/config.json { "Init": { "WebSocketsEnabled": false, @@ -196,7 +196,7 @@ cat <> docker-compose.yml command: --config config volumes: - ./genesis:/config/genesis - - ./node_$1/configs/config.cfg:/nethermind/configs/config.cfg + - ./node_$1/configs/config.json:/nethermind/configs/config.json - ./static-nodes.json:/nethermind/Data/static-nodes.json - ./node_$1/db/clique:/nethermind/nethermind_db/clique - ./node_$1/keystore:/nethermind/keystore @@ -233,4 +233,4 @@ function clearDbs() { done } -main \ No newline at end of file +main diff --git a/scripts/syncSettings.py b/scripts/syncSettings.py index d137de19890..730b3122511 100644 --- a/scripts/syncSettings.py +++ b/scripts/syncSettings.py @@ -121,12 +121,12 @@ def fastBlocksSettings(configuration, apiUrl, blockReduced, multiplierRequiremen print(configuration + 'PivotHash: ' + str(pivotHash)) print(configuration + 'PivotTotalDifficulty: ' + str(pivotTotalDifficulty)) data = {} - with open(f'{configsPath}/{configuration}.cfg', 'r') as mainnetCfg: + with open(f'{configsPath}/{configuration}.json', 'r') as mainnetCfg: data = json.load(mainnetCfg) data['Sync']['PivotNumber'] = baseBlock data['Sync']['PivotHash'] = pivotHash data['Sync']['PivotTotalDifficulty'] = str(pivotTotalDifficulty) - with open(f'{configsPath}/{configuration}.cfg', 'w') as mainnetCfgChanged: + with open(f'{configsPath}/{configuration}.json', 'w') as mainnetCfgChanged: json.dump(data, mainnetCfgChanged, indent=2) for config, value in configs.items(): diff --git a/src/Nethermind/Chains/kovan.json b/src/Nethermind/Chains/kovan.json deleted file mode 100644 index aa1efddb802..00000000000 --- a/src/Nethermind/Chains/kovan.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "name": "Kovan Testnet", - "dataDir": "kovan", - "engine": { - "authorityRound": { - "params": { - "stepDuration": "0x4", - "blockReward": "0x4563918244F40000", - "validators": { - "multi": { - "0": { - "list": [ - "0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED", - "0x00427feae2419c15b89d1c21af10d1b6650a4d3d", - "0x4Ed9B08e6354C70fE6F8CB0411b0d3246b424d6c", - "0x0020ee4Be0e2027d76603cB751eE069519bA81A1", - "0x0010f94b296a852aaac52ea6c5ac72e03afd032d", - "0x007733a1FE69CF3f2CF989F81C7b4cAc1693387A", - "0x00E6d2b931F55a3f1701c7389d592a7778897879", - "0x00e4a10650e5a6D6001C38ff8E64F97016a1645c", - "0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de" - ] - }, - "10960440": { - "list": [ - "0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED", - "0x0010f94b296a852aaac52ea6c5ac72e03afd032d", - "0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de" - ] - }, - "10960500": { - "safeContract": "0xaE71807C1B0a093cB1547b682DC78316D945c9B8" - } - } - }, - "validateScoreTransition": "0x41a3c4", - "validateStepTransition": "0x16e360", - "maximumUncleCountTransition": "0x4d50f8", - "maximumUncleCount": "0x0" - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x400", - "registrar": "0xfAb104398BBefbd47752E7702D9fE23047E1Bca3", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID": "0x2A", - "forkBlock": "0x9766dc", - "forkCanonHash": "0xf2fa4bcc417ad374100c2035aa865ff60fb568a83db1b6f6cb8fb52cfebc28b5", - "eip155Transition": "0xf4240", - "maxCodeSize": "0x6000", - "maxCodeSizeTransition": "0x64b540", - "validateChainIdTransition": "0xf4240", - "validateReceiptsTransition": "0xf4240", - "eip98Transition": "0x0", - "eip140Transition": "0x4d50f8", - "eip211Transition": "0x4d50f8", - "eip214Transition": "0x4d50f8", - "eip658Transition": "0x4d50f8", - "wasmActivationTransition": "0x64b540", - "wasmDisableTransition": "0x198096c", - "eip145Transition": "0x8c6180", - "eip1014Transition": "0x8c6180", - "eip1052Transition": "0x8c6180", - "eip1283Transition": "0x8c6180", - "eip1283DisableTransition": "0x9c7b61", - "eip1283ReenableTransition": "0xd751a5", - "eip1344Transition": "0xd751a5", - "eip1706Transition": "0xd751a5", - "eip1884Transition": "0xd751a5", - "eip2028Transition": "0xd751a5", - "eip2929Transition": "0x179f954", - "eip2930Transition": "0x179f954", - "eip1559Transition": "0x198096c", - "eip3198Transition": "0x198096c", - "eip3541Transition": "0x198096c", - "eip3529Transition": "0x198096c", - "eip1559BaseFeeMaxChangeDenominator": "0x8", - "eip1559ElasticityMultiplier": "0x2", - "eip1559BaseFeeInitialValue": "0x3B9ACA00", - "kip4Transition": "0x8c6180", - "kip6Transition": "0x8c6180" - }, - "genesis": { - "seal": { - "authorityRound": { - "step": "0x0", - "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x20000", - "gasLimit": "0x5B8D80" - }, - "accounts": { - "0x0000000000000000000000000000000000000001": { - "balance": "0x1", - "builtin": { - "name": "ecrecover", - "pricing": { - "0": { - "price": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "0x1", - "builtin": { - "name": "sha256", - "pricing": { - "0": { - "price": { - "linear": { - "base": 60, - "word": 12 - } - } - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "0x1", - "builtin": { - "name": "ripemd160", - "pricing": { - "0": { - "price": { - "linear": { - "base": 600, - "word": 120 - } - } - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "0x1", - "builtin": { - "name": "identity", - "pricing": { - "0": { - "price": { - "linear": { - "base": 15, - "word": 3 - } - } - } - } - } - }, - "0x0000000000000000000000000000000000000005": { - "builtin": { - "name": "modexp", - "pricing": { - "0x4d50f8": { - "price": { - "modexp": { - "divisor": 20 - } - } - }, - "0x179f954": { - "info": "EIP-2565: ModExp Gas Cost. Berlin hardfork (24_770_900)", - "price": { - "modexp2565": {} - } - } - } - } - }, - "0x0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "pricing": { - "0x4d50f8": { - "price": { - "alt_bn128_const_operations": { - "price": 500 - } - } - }, - "0xd751a5": { - "price": { - "alt_bn128_const_operations": { - "price": 150 - } - } - } - } - } - }, - "0x0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "pricing": { - "0x4d50f8": { - "price": { - "alt_bn128_const_operations": { - "price": 40000 - } - } - }, - "0xd751a5": { - "price": { - "alt_bn128_const_operations": { - "price": 6000 - } - } - } - } - } - }, - "0x0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "pricing": { - "0x4d50f8": { - "price": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000 - } - } - }, - "0xd751a5": { - "price": { - "alt_bn128_pairing": { - "base": 45000, - "pair": 34000 - } - } - } - } - } - }, - "0x0000000000000000000000000000000000000009": { - "builtin": { - "name": "blake2_f", - "pricing": { - "0xd751a5": { - "price": { - "blake2_f": { - "gas_per_round": 1 - } - } - } - } - } - }, - "0x00521965e7bd230323c423d96c657db5b79d099f": { - "balance": "1606938044258990275541962092341162602522202993782792835301376" - } - }, - "nodes": [ - "enode://30499bde23362f7d310a34518a2a6ff765921870bf0c3e63d21153cfa7ba9cf39cc7c8e54e9dad2f2b3c07288b3e91b220656833cc2d843a54875c229f3f959a@8.9.8.175:30303", - "enode://16898006ba2cd4fa8bf9a3dfe32684c178fa861df144bfc21fe800dc4838a03e342056951fa9fd533dcb0be1219e306106442ff2cf1f7e9f8faa5f2fc1a3aa45@116.203.116.241:30303", - "enode://49a0e1aa38caa12cbf31222cb4e31cef1e8794cb4dd38012f84498ac867b19584e29bbf6d53201d7dfd3b5eb0998a4d908d096ed4ddb5f9102c623852cd331ec@54.87.247.5:30303" - ] -} \ No newline at end of file diff --git a/src/Nethermind/Directory.Packages.props b/src/Nethermind/Directory.Packages.props index a19efb10615..7d129079f0f 100644 --- a/src/Nethermind/Directory.Packages.props +++ b/src/Nethermind/Directory.Packages.props @@ -13,7 +13,6 @@ - @@ -37,7 +36,6 @@ - @@ -76,6 +74,7 @@ + @@ -84,4 +83,4 @@ - \ No newline at end of file + diff --git a/src/Nethermind/Nethermind.Api.Test/PluginLoaderTests.cs b/src/Nethermind/Nethermind.Api.Test/PluginLoaderTests.cs index 9671c959d0f..252b02cede9 100644 --- a/src/Nethermind/Nethermind.Api.Test/PluginLoaderTests.cs +++ b/src/Nethermind/Nethermind.Api.Test/PluginLoaderTests.cs @@ -23,9 +23,9 @@ public class PluginLoaderTests public void full_lexicographical_order() { IFileSystem fileSystem = Substitute.For(); - IPluginLoader loader = new PluginLoader("", fileSystem, typeof(AuRaPlugin), typeof(CliquePlugin), - typeof(EthashPlugin), typeof(NethDevPlugin), typeof(HivePlugin), typeof(TestPlugin)); - loader.Load(new TestLogManager()); + IPluginLoader loader = new PluginLoader(string.Empty, fileSystem, new TestLogManager().GetClassLogger(), + typeof(AuRaPlugin), typeof(CliquePlugin), typeof(EthashPlugin), typeof(NethDevPlugin), typeof(HivePlugin), typeof(TestPlugin)); + loader.Load(); loader.OrderPlugins(new PluginConfig { PluginOrder = Array.Empty() }); var expected = new List { @@ -43,11 +43,11 @@ public void full_lexicographical_order() public void full_order() { IFileSystem fileSystem = Substitute.For(); - IPluginLoader loader = new PluginLoader("", fileSystem, typeof(AuRaPlugin), typeof(CliquePlugin), - typeof(EthashPlugin), typeof(NethDevPlugin), typeof(HivePlugin), typeof(TestPlugin)); - loader.Load(new TestLogManager()); + IPluginLoader loader = new PluginLoader(string.Empty, fileSystem, new TestLogManager().GetClassLogger(), + typeof(AuRaPlugin), typeof(CliquePlugin), typeof(EthashPlugin), typeof(NethDevPlugin), typeof(HivePlugin), typeof(TestPlugin)); + loader.Load(); IPluginConfig pluginConfig = - new PluginConfig { PluginOrder = new[] { "Hive", "TestPlugin", "NethDev", "Ethash", "Clique", "Aura" } }; + new PluginConfig { PluginOrder = ["Hive", "TestPlugin", "NethDev", "Ethash", "Clique", "Aura"] }; loader.OrderPlugins(pluginConfig); var expected = new List @@ -66,11 +66,11 @@ public void full_order() public void partial_lexicographical_order() { IFileSystem fileSystem = Substitute.For(); - IPluginLoader loader = new PluginLoader("", fileSystem, typeof(AuRaPlugin), typeof(CliquePlugin), - typeof(EthashPlugin), typeof(NethDevPlugin), typeof(HivePlugin), typeof(TestPlugin)); - loader.Load(new TestLogManager()); + IPluginLoader loader = new PluginLoader(string.Empty, fileSystem, new TestLogManager().GetClassLogger(), + typeof(AuRaPlugin), typeof(CliquePlugin), typeof(EthashPlugin), typeof(NethDevPlugin), typeof(HivePlugin), typeof(TestPlugin)); + loader.Load(); IPluginConfig pluginConfig = - new PluginConfig() { PluginOrder = new[] { "Hive", "NethDev", "Ethash" } }; + new PluginConfig() { PluginOrder = ["Hive", "NethDev", "Ethash"] }; loader.OrderPlugins(pluginConfig); var expected = new List @@ -89,10 +89,9 @@ public void partial_lexicographical_order() public void default_config() { IFileSystem fileSystem = Substitute.For(); - IPluginLoader loader = new PluginLoader("", fileSystem, - typeof(EthashPlugin), typeof(NethDevPlugin), typeof(HivePlugin), typeof(HealthChecksPlugin), - typeof(MergePlugin)); - loader.Load(new TestLogManager()); + IPluginLoader loader = new PluginLoader(string.Empty, fileSystem, new TestLogManager().GetClassLogger(), + typeof(EthashPlugin), typeof(NethDevPlugin), typeof(HivePlugin), typeof(HealthChecksPlugin), typeof(MergePlugin)); + loader.Load(); IPluginConfig pluginConfig = new PluginConfig(); loader.OrderPlugins(pluginConfig); diff --git a/src/Nethermind/Nethermind.Api.Test/SinglePluginLoaderTests.cs b/src/Nethermind/Nethermind.Api.Test/SinglePluginLoaderTests.cs index ed735e2e5f2..f8ea82485de 100644 --- a/src/Nethermind/Nethermind.Api.Test/SinglePluginLoaderTests.cs +++ b/src/Nethermind/Nethermind.Api.Test/SinglePluginLoaderTests.cs @@ -4,23 +4,21 @@ using System.Linq; using FluentAssertions; using Nethermind.Api.Extensions; -using Nethermind.Logging; using NUnit.Framework; -namespace Nethermind.Api.Test +namespace Nethermind.Api.Test; + +public class SinglePluginLoaderTests { - public class SinglePluginLoaderTests + [Test] + public void Can_load() { - [Test] - public void Can_load() - { - SinglePluginLoader.Instance.Load(LimboLogs.Instance); - } + SinglePluginLoader.Instance.Load(); + } - [Test] - public void Returns_correct_plugin() - { - SinglePluginLoader.Instance.PluginTypes.FirstOrDefault().Should().Be(typeof(TestPlugin)); - } + [Test] + public void Returns_correct_plugin() + { + SinglePluginLoader.Instance.PluginTypes.FirstOrDefault().Should().Be(typeof(TestPlugin)); } } diff --git a/src/Nethermind/Nethermind.Api/Extensions/IPluginLoader.cs b/src/Nethermind/Nethermind.Api/Extensions/IPluginLoader.cs index 033246a1ba1..23145817eac 100644 --- a/src/Nethermind/Nethermind.Api/Extensions/IPluginLoader.cs +++ b/src/Nethermind/Nethermind.Api/Extensions/IPluginLoader.cs @@ -3,16 +3,14 @@ using System; using System.Collections.Generic; -using Nethermind.Logging; -namespace Nethermind.Api.Extensions +namespace Nethermind.Api.Extensions; + +public interface IPluginLoader { - public interface IPluginLoader - { - IEnumerable PluginTypes { get; } + IEnumerable PluginTypes { get; } - void Load(ILogManager logManager); + void Load(); - public void OrderPlugins(IPluginConfig pluginConfig); - } + public void OrderPlugins(IPluginConfig pluginConfig); } diff --git a/src/Nethermind/Nethermind.Api/Extensions/PluginLoader.cs b/src/Nethermind/Nethermind.Api/Extensions/PluginLoader.cs index 5cddfabf5b5..8a4f3741934 100644 --- a/src/Nethermind/Nethermind.Api/Extensions/PluginLoader.cs +++ b/src/Nethermind/Nethermind.Api/Extensions/PluginLoader.cs @@ -10,127 +10,119 @@ using System.Runtime.Loader; using Nethermind.Logging; -namespace Nethermind.Api.Extensions +namespace Nethermind.Api.Extensions; + +public class PluginLoader(string pluginPath, IFileSystem fileSystem, ILogger logger, params Type[] embedded) : IPluginLoader { - public class PluginLoader : IPluginLoader - { - private readonly List _pluginTypes = new(); - private readonly IFileSystem _fileSystem; - private readonly Type[] _embedded; - private readonly string _pluginsDirectory; + private readonly ILogger _logger = logger; + private readonly List _pluginTypes = []; + private readonly IFileSystem _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); + private readonly Type[] _embedded = embedded; + private readonly string _pluginsDirectory = pluginPath ?? throw new ArgumentNullException(nameof(pluginPath)); - public IEnumerable PluginTypes => _pluginTypes; + public IEnumerable PluginTypes => _pluginTypes; - public PluginLoader(string pluginPath, IFileSystem fileSystem, params Type[] embedded) + public void Load() + { + if (_logger.IsInfo) _logger.Info("Loading embedded plugins"); + foreach (Type embeddedPlugin in _embedded) { - _pluginsDirectory = pluginPath ?? throw new ArgumentNullException(nameof(pluginPath)); - _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); - _embedded = embedded; + if (_logger.IsInfo) _logger.Info($" Found plugin type {embeddedPlugin}"); + _pluginTypes.Add(embeddedPlugin); } - public void Load(ILogManager logManager) + string baseDir = string.Empty.GetApplicationResourcePath(); + string pluginAssembliesDir = _pluginsDirectory.GetApplicationResourcePath(); + if (!_fileSystem.Directory.Exists(pluginAssembliesDir)) { - ILogger logger = logManager.GetClassLogger(); - if (logger.IsInfo) logger.Info("Loading embedded plugins"); - foreach (Type embeddedPlugin in _embedded) - { - if (logger.IsInfo) logger.Info($" Found plugin type {embeddedPlugin}"); - _pluginTypes.Add(embeddedPlugin); - } + if (_logger.IsWarn) _logger.Warn($"Plugin assemblies folder {pluginAssembliesDir} was not found. Skipping."); + return; + } - string baseDir = string.Empty.GetApplicationResourcePath(); - string pluginAssembliesDir = _pluginsDirectory.GetApplicationResourcePath(); - if (!_fileSystem.Directory.Exists(pluginAssembliesDir)) - { - if (logger.IsWarn) logger.Warn($"Plugin assemblies folder {pluginAssembliesDir} was not found. Skipping."); - return; - } + string[] assemblies = _fileSystem.Directory.GetFiles(pluginAssembliesDir, "*.dll"); + if (assemblies.Length > 0) + { + if (_logger.IsInfo) _logger.Info($"Loading {assemblies.Length} assemblies from {pluginAssembliesDir}"); + } - string[] assemblies = _fileSystem.Directory.GetFiles(pluginAssembliesDir, "*.dll"); - if (assemblies.Length > 0) - { - if (logger.IsInfo) logger.Info($"Loading {assemblies.Length} assemblies from {pluginAssembliesDir}"); - } + foreach (string assemblyName in assemblies) + { + string pluginAssembly = _fileSystem.Path.GetFileNameWithoutExtension(assemblyName); - foreach (string assemblyName in assemblies) + try { - string pluginAssembly = _fileSystem.Path.GetFileNameWithoutExtension(assemblyName); - - try + if (_logger.IsInfo) _logger.Warn($"Loading assembly {pluginAssembly}"); + string assemblyPath = _fileSystem.Path.Combine(pluginAssembliesDir, assemblyName); + Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); + AssemblyLoadContext.Default.Resolving += (_, name) => { - if (logger.IsInfo) logger.Warn($"Loading assembly {pluginAssembly}"); - string assemblyPath = _fileSystem.Path.Combine(pluginAssembliesDir, assemblyName); - Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); - AssemblyLoadContext.Default.Resolving += (_, name) => + string fileName = name.Name + ".dll"; + try { - string fileName = name.Name + ".dll"; - try - { - return AssemblyLoadContext.Default.LoadFromAssemblyPath(_fileSystem.Path.Combine(pluginAssembliesDir, fileName)); - } - catch (FileNotFoundException) - { - return AssemblyLoadContext.Default.LoadFromAssemblyPath(_fileSystem.Path.Combine(baseDir, fileName)); - } - }; + return AssemblyLoadContext.Default.LoadFromAssemblyPath(_fileSystem.Path.Combine(pluginAssembliesDir, fileName)); + } + catch (FileNotFoundException) + { + return AssemblyLoadContext.Default.LoadFromAssemblyPath(_fileSystem.Path.Combine(baseDir, fileName)); + } + }; - foreach (Type type in assembly.GetExportedTypes().Where(t => !t.IsInterface)) + foreach (Type type in assembly.GetExportedTypes().Where(t => !t.IsInterface)) + { + if (typeof(INethermindPlugin).IsAssignableFrom(type)) { - if (typeof(INethermindPlugin).IsAssignableFrom(type)) + if (!PluginTypes.Contains(type)) { - if (!PluginTypes.Contains(type)) - { - if (logger.IsInfo) logger.Warn($" Found plugin type {pluginAssembly}"); - _pluginTypes.Add(type); - } + if (_logger.IsInfo) _logger.Warn($" Found plugin type {pluginAssembly}"); + _pluginTypes.Add(type); } } } - catch (Exception e) - { - logger.Error($"Failed to load plugin {pluginAssembly}", e); - } + } + catch (Exception e) + { + _logger.Error($"Failed to load plugin {pluginAssembly}", e); } } + } - public void OrderPlugins(IPluginConfig pluginConfig) + public void OrderPlugins(IPluginConfig pluginConfig) + { + List order = pluginConfig.PluginOrder.Select(s => s.ToLower() + "plugin").ToList(); + _pluginTypes.Sort((f, s) => { - List order = pluginConfig.PluginOrder.Select(s => s.ToLower() + "plugin").ToList(); - _pluginTypes.Sort((f, s) => + bool fIsConsensus = typeof(IConsensusPlugin).IsAssignableFrom(f); + bool sIsConsensus = typeof(IConsensusPlugin).IsAssignableFrom(s); + + // Consensus plugins always at front + if (fIsConsensus && !sIsConsensus) { - bool fIsConsensus = typeof(IConsensusPlugin).IsAssignableFrom(f); - bool sIsConsensus = typeof(IConsensusPlugin).IsAssignableFrom(s); + return -1; + } - // Consensus plugins always at front - if (fIsConsensus && !sIsConsensus) - { - return -1; - } + if (sIsConsensus && !fIsConsensus) + { + return 1; + } - if (sIsConsensus && !fIsConsensus) + int fPos = order.IndexOf(f.Name.ToLower()); + int sPos = order.IndexOf(s.Name.ToLower()); + if (fPos == -1) + { + if (sPos == -1) { - return 1; + return f.Name.CompareTo(s.Name); } - int fPos = order.IndexOf(f.Name.ToLower()); - int sPos = order.IndexOf(s.Name.ToLower()); - if (fPos == -1) - { - if (sPos == -1) - { - return f.Name.CompareTo(s.Name); - } - - return 1; - } + return 1; + } - if (sPos == -1) - { - return -1; - } + if (sPos == -1) + { + return -1; + } - return fPos.CompareTo(sPos); - }); - } + return fPos.CompareTo(sPos); + }); } } diff --git a/src/Nethermind/Nethermind.Api/Extensions/SinglePluginLoader.cs b/src/Nethermind/Nethermind.Api/Extensions/SinglePluginLoader.cs index def9267ed72..845bd2a4279 100644 --- a/src/Nethermind/Nethermind.Api/Extensions/SinglePluginLoader.cs +++ b/src/Nethermind/Nethermind.Api/Extensions/SinglePluginLoader.cs @@ -4,25 +4,23 @@ using System; using System.Collections.Generic; using System.Linq; -using Nethermind.Logging; -namespace Nethermind.Api.Extensions +namespace Nethermind.Api.Extensions; + +/// +/// This class is introduced for easier testing of the plugins under construction - it allows to load a plugin +/// directly from the current solution +/// +/// Type of the plugin to load +public class SinglePluginLoader : IPluginLoader where T : INethermindPlugin { - /// - /// This class is introduced for easier testing of the plugins under construction - it allows to load a plugin - /// directly from the current solution - /// - /// Type of the plugin to load - public class SinglePluginLoader : IPluginLoader where T : INethermindPlugin - { - private SinglePluginLoader() { } + private SinglePluginLoader() { } - public static IPluginLoader Instance { get; } = new SinglePluginLoader(); + public static IPluginLoader Instance { get; } = new SinglePluginLoader(); - public IEnumerable PluginTypes => Enumerable.Repeat(typeof(T), 1); + public IEnumerable PluginTypes => Enumerable.Repeat(typeof(T), 1); - public void Load(ILogManager logManager) { } + public void Load() { } - public void OrderPlugins(IPluginConfig pluginConfig) { } - } + public void OrderPlugins(IPluginConfig pluginConfig) { } } diff --git a/src/Nethermind/Nethermind.Cli/Nethermind.Cli.csproj b/src/Nethermind/Nethermind.Cli/Nethermind.Cli.csproj index 6e9f05573db..b67d8aca117 100644 --- a/src/Nethermind/Nethermind.Cli/Nethermind.Cli.csproj +++ b/src/Nethermind/Nethermind.Cli/Nethermind.Cli.csproj @@ -10,8 +10,8 @@ - + diff --git a/src/Nethermind/Nethermind.Cli/Program.cs b/src/Nethermind/Nethermind.Cli/Program.cs index efadd38771c..848183c4c01 100644 --- a/src/Nethermind/Nethermind.Cli/Program.cs +++ b/src/Nethermind/Nethermind.Cli/Program.cs @@ -3,12 +3,12 @@ using System; using System.Collections.Generic; +using System.CommandLine; using System.IO; using System.IO.Abstractions; using System.Runtime.CompilerServices; using System.Text; using Jint.Native; -using McMaster.Extensions.CommandLineUtils; using Nethermind.Cli.Console; using Nethermind.Cli.Modules; using Nethermind.Config; @@ -16,161 +16,160 @@ using Nethermind.Serialization.Json; [assembly: InternalsVisibleTo("Nethermind.Cli.Test")] -namespace Nethermind.Cli +namespace Nethermind.Cli; + +public static class Program { - public static class Program + private static readonly Dictionary _availableColorSchemes = new(){ {"basic", BasicColorScheme.Instance }, + {"dracula", DraculaColorScheme.Instance }}; + private static readonly IJsonSerializer Serializer = new EthereumJsonSerializer(); + + public static void Main(string[] args) { - private static readonly Dictionary _availableColorSchemes = new(){ {"basic", BasicColorScheme.Instance }, - {"dracula", DraculaColorScheme.Instance }}; - private static readonly IJsonSerializer Serializer = new EthereumJsonSerializer(); + CliOption colorSchemeOption = new("--colorScheme", "-cs") + { + Description = "Color Scheme. Possible values: Basic|Dracula", + HelpName = "colorScheme" + }; + CliOption nodeAddressOption = new("--address", "-a") + { + Description = "Node Address", + HelpName = "address" + }; + CliRootCommand rootCommand = [colorSchemeOption, nodeAddressOption]; - public static void Main(string[] args) + rootCommand.SetAction(parseResult => { - CommandLineApplication app = new() { Name = "Nethermind.Cli" }; - _ = app.HelpOption("-?|-h|--help"); + string? colorSchemeValue = parseResult.GetValue(colorSchemeOption); + ColorScheme? cs; + ICliConsole cliConsole = colorSchemeValue is not null && (cs = MapColorScheme(colorSchemeValue)) is not null + ? new ColorfulCliConsole(cs) + : new CliConsole(); + + var historyManager = new StatementHistoryManager(cliConsole, new FileSystem()); + ILogManager logManager = new OneLoggerLogManager(new(new CliLogger(cliConsole))); + ICliEngine engine = new CliEngine(cliConsole); + INodeManager nodeManager = new NodeManager(engine, Serializer, cliConsole, logManager); + var moduleLoader = new CliModuleLoader(engine, nodeManager, cliConsole); + + engine.JintEngine.SetValue("serialize", new Action(v => + { + string text = Serializer.Serialize(v.ToObject(), true); + cliConsole.WriteGood(text); + })); - var colorSchemeOption = app.Option("-cs|--colorScheme ", "Color Scheme. Possible values: Basic|Dracula", CommandOptionType.SingleValue); - var nodeAddressOption = app.Option("-a|--address
", "Node Address", CommandOptionType.SingleValue); + moduleLoader.DiscoverAndLoadModules(); + ReadLine.AutoCompletionHandler = new AutoCompletionHandler(moduleLoader); - app.OnExecute(() => - { - ColorScheme? cs; - ICliConsole cliConsole = colorSchemeOption.HasValue() && (cs = MapColorScheme(colorSchemeOption.Value()!)) is not null - ? new ColorfulCliConsole(cs) - : new CliConsole(); - - var historyManager = new StatementHistoryManager(cliConsole, new FileSystem()); - ILogManager logManager = new OneLoggerLogManager(new(new CliLogger(cliConsole))); - ICliEngine engine = new CliEngine(cliConsole); - INodeManager nodeManager = new NodeManager(engine, Serializer, cliConsole, logManager); - var moduleLoader = new CliModuleLoader(engine, nodeManager, cliConsole); - - engine.JintEngine.SetValue("serialize", new Action(v => - { - string text = Serializer.Serialize(v.ToObject(), true); - cliConsole.WriteGood(text); - })); - - moduleLoader.DiscoverAndLoadModules(); - ReadLine.AutoCompletionHandler = new AutoCompletionHandler(moduleLoader); - - string nodeAddress = nodeAddressOption.HasValue() - ? nodeAddressOption.Value()! - : "http://localhost:8545"; - nodeManager.SwitchUri(new Uri(nodeAddress)); - historyManager.Init(); - TestConnection(nodeManager, engine, cliConsole); - cliConsole.WriteLine(); - RunEvalLoop(engine, historyManager, cliConsole); - - cliConsole.ResetColor(); - return ExitCodes.Ok; - }); + string nodeAddress = parseResult.GetValue(nodeAddressOption) ?? "http://localhost:8545"; + nodeManager.SwitchUri(new Uri(nodeAddress)); + historyManager.Init(); + TestConnection(nodeManager, engine, cliConsole); + cliConsole.WriteLine(); + RunEvalLoop(engine, historyManager, cliConsole); - try - { - app.Execute(args); - } - catch (CommandParsingException) - { - app.ShowHelp(); - } - } + cliConsole.ResetColor(); + return ExitCodes.Ok; + }); + + CliConfiguration cli = new(rootCommand); - private static void TestConnection(INodeManager nodeManager, ICliEngine cliEngine, ICliConsole cliConsole) + cli.Invoke(args); + } + + private static void TestConnection(INodeManager nodeManager, ICliEngine cliEngine, ICliConsole cliConsole) + { + cliConsole.WriteLine($"Connecting to {nodeManager.CurrentUri}"); + JsValue result = cliEngine.Execute("web3.clientVersion"); + if (result != JsValue.Null) { - cliConsole.WriteLine($"Connecting to {nodeManager.CurrentUri}"); - JsValue result = cliEngine.Execute("web3.clientVersion"); - if (result != JsValue.Null) - { - cliConsole.WriteGood("Connected"); - } + cliConsole.WriteGood("Connected"); } + } - internal static string RemoveDangerousCharacters(string statement) + internal static string RemoveDangerousCharacters(string statement) + { + if (string.IsNullOrWhiteSpace(statement)) { - if (string.IsNullOrWhiteSpace(statement)) - { - return ""; - } + return ""; + } - StringBuilder cleaned = new(); - for (int i = 0; i < statement.Length; i++) + StringBuilder cleaned = new(); + for (int i = 0; i < statement.Length; i++) + { + switch (statement[i]) { - switch (statement[i]) - { - case '\x0008': - if (cleaned.Length != 0) - { - cleaned.Remove(cleaned.Length - 1, 1); - } - break; - case '\x0000': - return cleaned.ToString(); - default: - cleaned.Append(statement[i]); - break; - } + case '\x0008': + if (cleaned.Length != 0) + { + cleaned.Remove(cleaned.Length - 1, 1); + } + break; + case '\x0000': + return cleaned.ToString(); + default: + cleaned.Append(statement[i]); + break; } - - return cleaned.ToString(); } - private static void RunEvalLoop(ICliEngine engine, StatementHistoryManager historyManager, ICliConsole console) + return cleaned.ToString(); + } + + private static void RunEvalLoop(ICliEngine engine, StatementHistoryManager historyManager, ICliConsole console) + { + while (true) { - while (true) + try { - try + const int bufferSize = 1024 * 16; + string statement; + using (Stream inStream = System.Console.OpenStandardInput(bufferSize)) { - const int bufferSize = 1024 * 16; - string statement; - using (Stream inStream = System.Console.OpenStandardInput(bufferSize)) - { - Colorful.Console.SetIn(new StreamReader(inStream, Colorful.Console.InputEncoding, false, bufferSize)); - console.WriteLessImportant("nethermind> "); - statement = console.Terminal == Terminal.Cygwin ? Colorful.Console.ReadLine() : ReadLine.Read(); - statement = RemoveDangerousCharacters(statement); + Colorful.Console.SetIn(new StreamReader(inStream, Colorful.Console.InputEncoding, false, bufferSize)); + console.WriteLessImportant("nethermind> "); + statement = console.Terminal == Terminal.Cygwin ? Colorful.Console.ReadLine() : ReadLine.Read(); + statement = RemoveDangerousCharacters(statement); - historyManager.UpdateHistory(statement); - } - - if (statement == "exit") - { - break; - } - - JsValue result = engine.Execute(statement); - WriteResult(console, result); + historyManager.UpdateHistory(statement); } - catch (Exception e) + + if (statement == "exit") { - console.WriteException(e); + break; } - } - } - private static void WriteResult(ICliConsole cliConsole, JsValue result) - { - if (result.IsObject() && result.AsObject().Class == "Function") - { - cliConsole.WriteGood(result.ToString()); - cliConsole.WriteLine(); + JsValue result = engine.Execute(statement); + WriteResult(console, result); } - else if (!result.IsNull()) + catch (Exception e) { - string text = Serializer.Serialize(result.ToObject(), true); - cliConsole.WriteGood(text); - } - else - { - cliConsole.WriteLessImportant("null"); - cliConsole.WriteLine(); + console.WriteException(e); } } + } - private static ColorScheme? MapColorScheme(string colorSchemeOption) + private static void WriteResult(ICliConsole cliConsole, JsValue result) + { + if (result.IsObject() && result.AsObject().Class == "Function") { - return _availableColorSchemes.TryGetValue(colorSchemeOption.ToLower(), out var scheme) ? scheme : null; + cliConsole.WriteGood(result.ToString()); + cliConsole.WriteLine(); } + else if (!result.IsNull()) + { + string text = Serializer.Serialize(result.ToObject(), true); + cliConsole.WriteGood(text); + } + else + { + cliConsole.WriteLessImportant("null"); + cliConsole.WriteLine(); + } + } + + private static ColorScheme? MapColorScheme(string colorSchemeOption) + { + return _availableColorSchemes.TryGetValue(colorSchemeOption.ToLower(), out var scheme) ? scheme : null; } } diff --git a/src/Nethermind/Nethermind.Config.Test/ConfigProvider_FindIncorrectSettings_Tests.cs b/src/Nethermind/Nethermind.Config.Test/ConfigProvider_FindIncorrectSettings_Tests.cs index d4352462177..d6c489d6eb9 100644 --- a/src/Nethermind/Nethermind.Config.Test/ConfigProvider_FindIncorrectSettings_Tests.cs +++ b/src/Nethermind/Nethermind.Config.Test/ConfigProvider_FindIncorrectSettings_Tests.cs @@ -6,141 +6,113 @@ using NSubstitute; using NUnit.Framework; -namespace Nethermind.Config.Test +namespace Nethermind.Config.Test; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class ConfigProvider_FindIncorrectSettings_Tests { - [TestFixture] - [Parallelizable(ParallelScope.All)] - public class ConfigProvider_FindIncorrectSettings_Tests + [Test] + public void CorrectSettingNames_CaseInsensitive() + { + JsonConfigSource? jsonSource = new("SampleJson/CorrectSettingNames.json"); + + IEnvironment? env = Substitute.For(); + env.GetEnvironmentVariables().Returns(new Dictionary() { { "NETHERMIND_NETWORKCONFIG_MAXCANDIDATEPEERCOUNT", "500" } }); + EnvConfigSource? envSource = new(env); + + ArgsConfigSource? argsSource = new(new Dictionary() { + { "DiscoveryConfig.BucketSize", "10" }, + { "NetworkConfig.DiscoveryPort", "30301" } }); + + ConfigProvider? configProvider = new(); + configProvider.AddSource(jsonSource); + configProvider.AddSource(envSource); + configProvider.AddSource(argsSource); + + configProvider.Initialize(); + + + (string ErrorMsg, IList<(IConfigSource Source, string Category, string Name)> Errors) res = configProvider.FindIncorrectSettings(); + + Assert.That(res.Errors.Count, Is.EqualTo(0)); + } + + [Test] + public void NoCategorySettings() { - [Test] - public void CorrectSettingNames_CaseInsensitive() - { - JsonConfigSource? jsonSource = new("SampleJson/CorrectSettingNames.cfg"); - - IEnvironment? env = Substitute.For(); - env.GetEnvironmentVariables().Returns(new Dictionary() { { "NETHERMIND_NETWORKCONFIG_MAXCANDIDATEPEERCOUNT", "500" } }); - EnvConfigSource? envSource = new(env); - - ArgsConfigSource? argsSource = new(new Dictionary() { - { "DiscoveryConfig.BucketSize", "10" }, - { "NetworkConfig.DiscoveryPort", "30301" } }); - - ConfigProvider? configProvider = new(); - configProvider.AddSource(jsonSource); - configProvider.AddSource(envSource); - configProvider.AddSource(argsSource); - - configProvider.Initialize(); - - - (string ErrorMsg, IList<(IConfigSource Source, string Category, string Name)> Errors) res = configProvider.FindIncorrectSettings(); - - Assert.That(res.Errors.Count, Is.EqualTo(0)); - } - - [Test] - public void NoCategorySettings() - { - IEnvironment? env = Substitute.For(); - env.GetEnvironmentVariables().Returns(new Dictionary() { - { "NETHERMIND_CLI_SWITCH_LOCAL", "http://localhost:80" }, - { "NETHERMIND_MONITORING_JOB", "nethermindJob" }, - { "NETHERMIND_MONITORING_GROUP", "nethermindGroup" }, - { "NETHERMIND_ENODE_IPADDRESS", "1.2.3.4" }, - { "NETHERMIND_HIVE_ENABLED", "true" }, - { "NETHERMIND_URL", "http://test:80" }, - { "NETHERMIND_CORS_ORIGINS", "*" }, - { "NETHERMIND_CONFIG", "test2.cfg" }, - { "NETHERMIND_XYZ", "xyz" }, // not existing, should get error - { "QWER", "qwerty" } // not Nethermind setting, no error - }); - EnvConfigSource? envSource = new(env); - - ArgsConfigSource? argsSource = new(new Dictionary() { - { "config", "test.cfg" }, - { "datadir", "Data" }, - { "ConfigsDirectory", "ConfDir" }, - { "baseDbPath", "DB" }, - { "log", "info" }, - { "loggerConfigSource", "logSource" }, - { "pluginsDirectory", "Plugins" }, - { "Abc", "abc" } // not existing, should get error - }); - - ConfigProvider? configProvider = new(); - configProvider.AddSource(envSource); - configProvider.AddSource(argsSource); - - configProvider.Initialize(); - - (string ErrorMsg, IList<(IConfigSource Source, string Category, string Name)> Errors) res = configProvider.FindIncorrectSettings(); - - Assert.That(res.Errors.Count, Is.EqualTo(2)); - Assert.That(res.Errors[0].Name, Is.EqualTo("XYZ")); - Assert.That(res.Errors[1].Name, Is.EqualTo("Abc")); - Assert.That(res.ErrorMsg, Is.EqualTo($"ConfigType:EnvironmentVariable(NETHERMIND_*)|Category:|Name:XYZ{Environment.NewLine}ConfigType:RuntimeOption|Category:|Name:Abc")); - - } - - [Test] - public void SettingWithTypos() - { - JsonConfigSource? jsonSource = new("SampleJson/ConfigWithTypos.cfg"); - - IEnvironment? env = Substitute.For(); - env.GetEnvironmentVariables().Returns(new Dictionary() { - { "NETHERMIND_NETWORKCONFIG_MAXCANDIDATEPERCOUNT", "500" } // incorrect, should be NETHERMIND_NETWORKCONFIG_MAXCANDIDATEPEERCOUNT - }); - EnvConfigSource? envSource = new(env); - - ArgsConfigSource? argsSource = new(new Dictionary() { - { "DiscoveryConfig.BucketSize", "10" }, - { "NetworkConfig.DiscoverPort", "30301" }, // incorrect, should be NetworkConfig.DiscoveryPort - { "Network.P2PPort", "30301" } }); - - ConfigProvider? configProvider = new(); - configProvider.AddSource(jsonSource); - configProvider.AddSource(envSource); - configProvider.AddSource(argsSource); - - configProvider.Initialize(); - - (string ErrorMsg, IList<(IConfigSource Source, string Category, string Name)> Errors) res = configProvider.FindIncorrectSettings(); - - Assert.That(res.Errors.Count, Is.EqualTo(4)); - Assert.That(res.Errors[0].Name, Is.EqualTo("Concurrenc")); - Assert.That(res.Errors[1].Category, Is.EqualTo("BlomConfig")); - Assert.That(res.Errors[2].Name, Is.EqualTo("MAXCANDIDATEPERCOUNT")); - Assert.That(res.Errors[3].Name, Is.EqualTo("DiscoverPort")); - Assert.That(res.ErrorMsg, Is.EqualTo($"ConfigType:JsonConfigFile|Category:DiscoveRyConfig|Name:Concurrenc{Environment.NewLine}ConfigType:JsonConfigFile|Category:BlomConfig|Name:IndexLevelBucketSizes{Environment.NewLine}ConfigType:EnvironmentVariable(NETHERMIND_*)|Category:NETWORKCONFIG|Name:MAXCANDIDATEPERCOUNT{Environment.NewLine}ConfigType:RuntimeOption|Category:NetworkConfig|Name:DiscoverPort")); - } - - [Test] - public void IncorrectFormat() - { - IEnvironment? env = Substitute.For(); - env.GetEnvironmentVariables().Returns(new Dictionary() { - { "NETHERMIND_NETWORKCONFIGMAXCANDIDATEPEERCOUNT", "500" } // incorrect, should be NETHERMIND_NETWORKCONFIG_MAXCANDIDATEPEERCOUNT - }); - EnvConfigSource? envSource = new(env); - - ArgsConfigSource? argsSource = new(new Dictionary() { - { "DiscoveryConfig.BucketSize", "10" }, - { "NetworkConfigP2PPort", "30301" } }); // incorrect, should be Network.P2PPort - - ConfigProvider? configProvider = new(); - configProvider.AddSource(envSource); - configProvider.AddSource(argsSource); - - configProvider.Initialize(); - - (string ErrorMsg, IList<(IConfigSource Source, string Category, string Name)> Errors) res = configProvider.FindIncorrectSettings(); - - Assert.That(res.Errors.Count, Is.EqualTo(2)); - Assert.That(res.Errors[0].Name, Is.EqualTo("NETWORKCONFIGMAXCANDIDATEPEERCOUNT")); - Assert.That(res.Errors[1].Name, Is.EqualTo("NetworkConfigP2PPort")); - Assert.That(res.ErrorMsg, Is.EqualTo($"ConfigType:EnvironmentVariable(NETHERMIND_*)|Category:|Name:NETWORKCONFIGMAXCANDIDATEPEERCOUNT{Environment.NewLine}ConfigType:RuntimeOption|Category:|Name:NetworkConfigP2PPort")); - } + IEnvironment? env = Substitute.For(); + env.GetEnvironmentVariables().Returns(new Dictionary() { + { "NETHERMIND_CLI_SWITCH_LOCAL", "http://localhost:80" }, + { "NETHERMIND_MONITORING_JOB", "nethermindJob" }, + { "NETHERMIND_MONITORING_GROUP", "nethermindGroup" }, + { "NETHERMIND_ENODE_IPADDRESS", "1.2.3.4" }, + { "NETHERMIND_URL", "http://test:80" }, + { "NETHERMIND_CORS_ORIGINS", "*" }, + { "NETHERMIND_CONFIG", "test2.json" }, + { "NETHERMIND_XYZ", "xyz" }, // not existing, should get error + { "QWER", "qwerty" } // not Nethermind setting, no error + }); + EnvConfigSource? envSource = new(env); + + ConfigProvider? configProvider = new(); + configProvider.AddSource(envSource); + + configProvider.Initialize(); + + (string ErrorMsg, IList<(IConfigSource Source, string Category, string Name)> Errors) res = configProvider.FindIncorrectSettings(); + + Assert.That(res.Errors.Count, Is.EqualTo(1)); + Assert.That(res.Errors[0].Name, Is.EqualTo("XYZ")); + Assert.That(res.ErrorMsg, Is.EqualTo($"ConfigType:EnvironmentVariable(NETHERMIND_*)|Category:|Name:XYZ")); } + + [Test] + public void SettingWithTypos() + { + JsonConfigSource? jsonSource = new("SampleJson/ConfigWithTypos.json"); + + IEnvironment? env = Substitute.For(); + env.GetEnvironmentVariables().Returns(new Dictionary() { + { "NETHERMIND_NETWORKCONFIG_MAXCANDIDATEPERCOUNT", "500" } // incorrect, should be NETHERMIND_NETWORKCONFIG_MAXCANDIDATEPEERCOUNT + }); + EnvConfigSource? envSource = new(env); + + ConfigProvider? configProvider = new(); + configProvider.AddSource(jsonSource); + configProvider.AddSource(envSource); + + configProvider.Initialize(); + + (string ErrorMsg, IList<(IConfigSource Source, string Category, string Name)> Errors) res = configProvider.FindIncorrectSettings(); + + Assert.That(res.Errors.Count, Is.EqualTo(3)); + Assert.That(res.Errors[0].Name, Is.EqualTo("Concurrenc")); + Assert.That(res.Errors[1].Category, Is.EqualTo("BlomConfig")); + Assert.That(res.Errors[2].Name, Is.EqualTo("MAXCANDIDATEPERCOUNT")); + Assert.That(res.ErrorMsg, Is.EqualTo($"ConfigType:JsonConfigFile|Category:DiscoveRyConfig|Name:Concurrenc{Environment.NewLine}ConfigType:JsonConfigFile|Category:BlomConfig|Name:IndexLevelBucketSizes{Environment.NewLine}ConfigType:EnvironmentVariable(NETHERMIND_*)|Category:NETWORKCONFIG|Name:MAXCANDIDATEPERCOUNT")); + } + + [Test] + public void IncorrectFormat() + { + IEnvironment? env = Substitute.For(); + env.GetEnvironmentVariables().Returns(new Dictionary() { + { "NETHERMIND_NETWORKCONFIGMAXCANDIDATEPEERCOUNT", "500" } // incorrect, should be NETHERMIND_NETWORKCONFIG_MAXCANDIDATEPEERCOUNT + }); + EnvConfigSource? envSource = new(env); + + ConfigProvider? configProvider = new(); + configProvider.AddSource(envSource); + + configProvider.Initialize(); + + (string ErrorMsg, IList<(IConfigSource Source, string Category, string Name)> Errors) res = configProvider.FindIncorrectSettings(); + + Assert.That(res.Errors.Count, Is.EqualTo(1)); + Assert.That(res.Errors[0].Name, Is.EqualTo("NETWORKCONFIGMAXCANDIDATEPEERCOUNT")); + Assert.That(res.ErrorMsg, Is.EqualTo($"ConfigType:EnvironmentVariable(NETHERMIND_*)|Category:|Name:NETWORKCONFIGMAXCANDIDATEPEERCOUNT")); + } + } diff --git a/src/Nethermind/Nethermind.Config.Test/JsonConfigProviderTests.cs b/src/Nethermind/Nethermind.Config.Test/JsonConfigProviderTests.cs index 5017671f906..5878c6d80a8 100644 --- a/src/Nethermind/Nethermind.Config.Test/JsonConfigProviderTests.cs +++ b/src/Nethermind/Nethermind.Config.Test/JsonConfigProviderTests.cs @@ -31,7 +31,7 @@ public void Initialize() JsonRpcConfig jsonRpcConfig = new(); StatsParameters statsConfig = StatsParameters.Instance; - _configProvider = new JsonConfigProvider("SampleJson/SampleJsonConfig.cfg"); + _configProvider = new JsonConfigProvider("SampleJson/SampleJsonConfig.json"); } [TestCase(12ul, typeof(BlocksConfig), nameof(BlocksConfig.SecondsPerSlot))] @@ -48,7 +48,7 @@ public void Test_getDefaultValue(T expected, Type type, string propName) [Test] public void Provides_helpful_error_message_when_file_does_not_exist() { - Assert.Throws(() => _configProvider = new JsonConfigProvider("SampleJson.cfg")); + Assert.Throws(() => _configProvider = new JsonConfigProvider("SampleJson.json")); } [Test] diff --git a/src/Nethermind/Nethermind.Config.Test/Nethermind.Config.Test.csproj b/src/Nethermind/Nethermind.Config.Test/Nethermind.Config.Test.csproj index e81ac159db0..298ab14cf31 100644 --- a/src/Nethermind/Nethermind.Config.Test/Nethermind.Config.Test.csproj +++ b/src/Nethermind/Nethermind.Config.Test/Nethermind.Config.Test.csproj @@ -28,16 +28,15 @@ - - - - + + + PreserveNewest - + PreserveNewest - + PreserveNewest diff --git a/src/Nethermind/Nethermind.Config.Test/SampleJson/ConfigWithTypos.cfg b/src/Nethermind/Nethermind.Config.Test/SampleJson/ConfigWithTypos.json similarity index 100% rename from src/Nethermind/Nethermind.Config.Test/SampleJson/ConfigWithTypos.cfg rename to src/Nethermind/Nethermind.Config.Test/SampleJson/ConfigWithTypos.json diff --git a/src/Nethermind/Nethermind.Config.Test/SampleJson/CorrectSettingNames.cfg b/src/Nethermind/Nethermind.Config.Test/SampleJson/CorrectSettingNames.json similarity index 100% rename from src/Nethermind/Nethermind.Config.Test/SampleJson/CorrectSettingNames.cfg rename to src/Nethermind/Nethermind.Config.Test/SampleJson/CorrectSettingNames.json diff --git a/src/Nethermind/Nethermind.Config.Test/SampleJson/SampleJsonConfig.cfg b/src/Nethermind/Nethermind.Config.Test/SampleJson/SampleJsonConfig.json similarity index 100% rename from src/Nethermind/Nethermind.Config.Test/SampleJson/SampleJsonConfig.cfg rename to src/Nethermind/Nethermind.Config.Test/SampleJson/SampleJsonConfig.json diff --git a/src/Nethermind/Nethermind.Config/ConfigProvider.cs b/src/Nethermind/Nethermind.Config/ConfigProvider.cs index bbc2ca7ad21..ce5b5100184 100644 --- a/src/Nethermind/Nethermind.Config/ConfigProvider.cs +++ b/src/Nethermind/Nethermind.Config/ConfigProvider.cs @@ -15,10 +15,10 @@ public class ConfigProvider : IConfigProvider { private readonly ConcurrentDictionary _instances = new(); - private readonly List _configSource = new(); + private readonly List _configSource = []; private Dictionary Categories { get; set; } = new(StringComparer.InvariantCultureIgnoreCase); - private readonly Dictionary _implementations = new(); + private readonly Dictionary _implementations = []; public T GetConfig() where T : IConfig { @@ -114,23 +114,24 @@ public void Initialize() Initialize(); } - HashSet propertySet = _instances.Values + var propertySet = _instances.Values .SelectMany(i => i.GetType() .GetProperties() .Select(p => GetKey(i.GetType().Name, p.Name))) .ToHashSet(StringComparer.OrdinalIgnoreCase); - List<(IConfigSource Source, string Category, string Name)> incorrectSettings = new(); + List<(IConfigSource Source, string Category, string Name)> incorrectSettings = []; - foreach (var source in _configSource) + // Skip the validation for ArgsConfigSource items as they are already validated by the CLI parser + foreach (IConfigSource source in _configSource.Where(s => s is not ArgsConfigSource)) { var configs = source.GetConfigKeys(); - foreach (var conf in configs) + foreach ((string category, string name) in configs) { - if (!propertySet.Contains(GetKey(conf.Category, conf.Name))) + if (!propertySet.Contains(GetKey(category, name))) { - incorrectSettings.Add((source, conf.Category, conf.Name)); + incorrectSettings.Add((source, category, name)); } } } @@ -155,10 +156,10 @@ static string GetKey(string category, string name) } else if (!category.EndsWith("config", StringComparison.OrdinalIgnoreCase)) { - category += "Config"; + category = $"{category}Config"; } - return category + '.' + name; + return $"{category}.{name}"; } } } diff --git a/src/Nethermind/Nethermind.Config/INoCategoryConfig.cs b/src/Nethermind/Nethermind.Config/INoCategoryConfig.cs index e40fa35577f..d1b038857f9 100644 --- a/src/Nethermind/Nethermind.Config/INoCategoryConfig.cs +++ b/src/Nethermind/Nethermind.Config/INoCategoryConfig.cs @@ -6,27 +6,9 @@ namespace Nethermind.Config; [ConfigCategory(HiddenFromDocs = true)] public interface INoCategoryConfig : IConfig { - [ConfigItem(Description = "Parent directory or path for BaseDbPath, KeyStoreDirectory, LogDirectory configurations.")] - public string DataDir { get; set; } - - [ConfigItem(Description = "Path to the JSON configuration file.")] + [ConfigItem(Description = "Path to the configuration file.")] public string Config { get; set; } - [ConfigItem(Description = "Path or directory for configuration files.", DefaultValue = "configs")] - public string ConfigsDirectory { get; set; } - - [ConfigItem(Description = "Path or directory for database files.", DefaultValue = "db")] - public string BaseDbPath { get; set; } - - [ConfigItem(Description = "Log level override. Possible values: OFF|TRACE|DEBUG|INFO|WARN|ERROR")] - public string Log { get; set; } - - [ConfigItem(Description = "Path to the NLog config file")] - public string LoggerConfigSource { get; set; } - - [ConfigItem(Description = "Plugins directory")] - public string PluginsDirectory { get; set; } - [ConfigItem(Description = "Sets the job name for metrics monitoring.", EnvironmentVariable = "NETHERMIND_MONITORING_JOB")] public string MonitoringJob { get; set; } @@ -36,9 +18,6 @@ public interface INoCategoryConfig : IConfig [ConfigItem(Description = "Sets the external IP for the node.", EnvironmentVariable = "NETHERMIND_ENODE_IPADDRESS")] public string EnodeIpAddress { get; set; } - [ConfigItem(Description = "Enables Hive plugin used for executing Hive Ethereum Tests.", EnvironmentVariable = "NETHERMIND_HIVE_ENABLED", DefaultValue = "false")] - public bool HiveEnabled { get; set; } - [ConfigItem(Description = "Defines default URL for JSON RPC.", EnvironmentVariable = "NETHERMIND_URL")] public string Url { get; set; } diff --git a/src/Nethermind/Nethermind.Config/IProcessExitSource.cs b/src/Nethermind/Nethermind.Config/IProcessExitSource.cs index ea447bd4c22..22945eb257e 100644 --- a/src/Nethermind/Nethermind.Config/IProcessExitSource.cs +++ b/src/Nethermind/Nethermind.Config/IProcessExitSource.cs @@ -16,14 +16,14 @@ public interface IProcessExitSource public class ProcessExitSource : IProcessExitSource { - private CancellationTokenSource _cancellationTokenSource = new(); - public int ExitCode { get; set; } = ExitCodes.Ok; - + private CancellationTokenSource _cancellationTokenSource; private readonly TaskCompletionSource _exitResult = new(); - public Task ExitTask => _exitResult.Task; - - public CancellationToken Token => _cancellationTokenSource!.Token; + public ProcessExitSource(CancellationToken cancellationToken) + { + _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + _cancellationTokenSource.Token.Register(() => Exit(ExitCodes.SigInt)); + } public void Exit(int exitCode) { @@ -33,4 +33,10 @@ public void Exit(int exitCode) _exitResult.SetResult(); } } + + public int ExitCode { get; set; } = ExitCodes.Ok; + + public Task ExitTask => _exitResult.Task; + + public CancellationToken Token => _cancellationTokenSource.Token; } diff --git a/src/Nethermind/Nethermind.Config/JsonConfigSource.cs b/src/Nethermind/Nethermind.Config/JsonConfigSource.cs index 9e9f49a18b3..2f647f104f1 100644 --- a/src/Nethermind/Nethermind.Config/JsonConfigSource.cs +++ b/src/Nethermind/Nethermind.Config/JsonConfigSource.cs @@ -8,153 +8,152 @@ using System.Text.Json; using System.Linq; -namespace Nethermind.Config +namespace Nethermind.Config; + +public class JsonConfigSource : IConfigSource { - public class JsonConfigSource : IConfigSource + public JsonConfigSource(string configFilePath) { - public JsonConfigSource(string configFilePath) - { - LoadJsonConfig(configFilePath); - } + LoadJsonConfig(configFilePath); + } - private void ApplyJsonConfig(string jsonContent) + private void ApplyJsonConfig(string jsonContent) + { + try { - try + using var json = JsonDocument.Parse(jsonContent); + foreach (var moduleEntry in json.RootElement.EnumerateObject()) { - using var json = JsonDocument.Parse(jsonContent); - foreach (var moduleEntry in json.RootElement.EnumerateObject()) - { - LoadModule(moduleEntry.Name, moduleEntry.Value); - } - } - catch (JsonException e) - { - throw new System.Configuration.ConfigurationErrorsException($"Config is not correctly formed JSON. See inner exception for details.", e); + LoadModule(moduleEntry.Name, moduleEntry.Value); } } + catch (JsonException e) + { + throw new System.Configuration.ConfigurationErrorsException($"Config is not correctly formed JSON. See inner exception for details.", e); + } + } - private void LoadJsonConfig(string configFilePath) + private void LoadJsonConfig(string configFilePath) + { + if (!File.Exists(configFilePath)) { - if (!File.Exists(configFilePath)) + StringBuilder missingConfigFileMessage = new($"Config file {configFilePath} does not exist."); + try { - StringBuilder missingConfigFileMessage = new($"Config file {configFilePath} does not exist."); - try - { - string directory = Path.GetDirectoryName(configFilePath); - directory = Path.IsPathRooted(configFilePath) - ? directory - : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, directory); + string directory = Path.GetDirectoryName(configFilePath); + directory = Path.IsPathRooted(configFilePath) + ? directory + : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, directory); - missingConfigFileMessage.AppendLine().AppendLine($"Search directory: {directory}"); + missingConfigFileMessage.AppendLine().AppendLine($"Search directory: {directory}"); - string[] configFiles = Directory.GetFiles(directory, "*.cfg"); - if (configFiles.Length > 0) + string[] configFiles = Directory.GetFiles(directory, "*.json"); + if (configFiles.Length > 0) + { + missingConfigFileMessage.AppendLine("Found the following config files:"); + for (int i = 0; i < configFiles.Length; i++) { - missingConfigFileMessage.AppendLine("Found the following config files:"); - for (int i = 0; i < configFiles.Length; i++) - { - missingConfigFileMessage.AppendLine($" * {configFiles[i]}"); - } + missingConfigFileMessage.AppendLine($" * {configFiles[i]}"); } } - catch (Exception) - { - // do nothing - the lines above just give extra info and config is loaded at the beginning so unlikely we have any catastrophic errors here - } - - throw new IOException(missingConfigFileMessage.ToString()); + } + catch (Exception) + { + // do nothing - the lines above just give extra info and config is loaded at the beginning so unlikely we have any catastrophic errors here } - ApplyJsonConfig(File.ReadAllText(configFilePath)); + throw new IOException(missingConfigFileMessage.ToString()); } - private void LoadModule(string moduleName, JsonElement configItems) - { - var itemsDict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + ApplyJsonConfig(File.ReadAllText(configFilePath)); + } + + private void LoadModule(string moduleName, JsonElement configItems) + { + var itemsDict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (var configItem in configItems.EnumerateObject()) + foreach (var configItem in configItems.EnumerateObject()) + { + var key = configItem.Name; + if (!itemsDict.ContainsKey(key)) { - var key = configItem.Name; - if (!itemsDict.ContainsKey(key)) + var value = configItem.Value; + if (value.ValueKind == JsonValueKind.Number) { - var value = configItem.Value; - if (value.ValueKind == JsonValueKind.Number) - { - itemsDict[key] = value.GetInt64().ToString(); - } - else if (value.ValueKind == JsonValueKind.True) - { - itemsDict[key] = "true"; - } - else if (value.ValueKind == JsonValueKind.False) - { - itemsDict[key] = "false"; - } - else - { - itemsDict[key] = configItem.Value.ToString(); - } + itemsDict[key] = value.GetInt64().ToString(); + } + else if (value.ValueKind == JsonValueKind.True) + { + itemsDict[key] = "true"; + } + else if (value.ValueKind == JsonValueKind.False) + { + itemsDict[key] = "false"; } else { - throw new System.Configuration.ConfigurationErrorsException($"Duplicated config value: {key}, module: {moduleName}"); + itemsDict[key] = configItem.Value.ToString(); } } - - ApplyConfigValues(moduleName, itemsDict); + else + { + throw new System.Configuration.ConfigurationErrorsException($"Duplicated config value: {key}, module: {moduleName}"); + } } - private readonly Dictionary> _values = new(StringComparer.InvariantCultureIgnoreCase); - - private readonly Dictionary> _parsedValues = new(StringComparer.InvariantCultureIgnoreCase); + ApplyConfigValues(moduleName, itemsDict); + } - private void ApplyConfigValues(string configModule, Dictionary items) - { - if (!configModule.EndsWith("Config")) - { - configModule += "Config"; - } + private readonly Dictionary> _values = new(StringComparer.InvariantCultureIgnoreCase); - _values[configModule] = items; - _parsedValues[configModule] = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } + private readonly Dictionary> _parsedValues = new(StringComparer.InvariantCultureIgnoreCase); - private void ParseValue(Type type, string category, string name) + private void ApplyConfigValues(string configModule, Dictionary items) + { + if (!configModule.EndsWith("Config")) { - string valueString = _values[category][name]; - _parsedValues[category][name] = ConfigSourceHelper.ParseValue(type, valueString, category, name); + configModule += "Config"; } - public (bool IsSet, object Value) GetValue(Type type, string category, string name) - { - (bool isSet, _) = GetRawValue(category, name); - if (isSet) - { - if (!_parsedValues[category].ContainsKey(name)) - { - ParseValue(type, category, name); - } - - return (true, _parsedValues[category][name]); - } + _values[configModule] = items; + _parsedValues[configModule] = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } - return (false, ConfigSourceHelper.GetDefault(type)); - } + private void ParseValue(Type type, string category, string name) + { + string valueString = _values[category][name]; + _parsedValues[category][name] = ConfigSourceHelper.ParseValue(type, valueString, category, name); + } - public (bool IsSet, string Value) GetRawValue(string category, string name) + public (bool IsSet, object Value) GetValue(Type type, string category, string name) + { + (bool isSet, _) = GetRawValue(category, name); + if (isSet) { - if (string.IsNullOrEmpty(category) || string.IsNullOrEmpty(name)) + if (!_parsedValues[category].ContainsKey(name)) { - return (false, null); + ParseValue(type, category, name); } - bool isSet = _values.ContainsKey(category) && _values[category].ContainsKey(name); - return (isSet, isSet ? _values[category][name] : null); + return (true, _parsedValues[category][name]); } - public IEnumerable<(string Category, string Name)> GetConfigKeys() + return (false, ConfigSourceHelper.GetDefault(type)); + } + + public (bool IsSet, string Value) GetRawValue(string category, string name) + { + if (string.IsNullOrEmpty(category) || string.IsNullOrEmpty(name)) { - return _values.SelectMany(m => m.Value.Keys.Select(n => (m.Key, n))); + return (false, null); } + + bool isSet = _values.ContainsKey(category) && _values[category].ContainsKey(name); + return (isSet, isSet ? _values[category][name] : null); + } + + public IEnumerable<(string Category, string Name)> GetConfigKeys() + { + return _values.SelectMany(m => m.Value.Keys.Select(n => (m.Key, n))); } } diff --git a/src/Nethermind/Nethermind.Config/NoCategoryConfig.cs b/src/Nethermind/Nethermind.Config/NoCategoryConfig.cs index 56a73d77ac4..e231c770f24 100644 --- a/src/Nethermind/Nethermind.Config/NoCategoryConfig.cs +++ b/src/Nethermind/Nethermind.Config/NoCategoryConfig.cs @@ -1,23 +1,15 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -namespace Nethermind.Config +namespace Nethermind.Config; + +public class NoCategoryConfig : INoCategoryConfig { - public class NoCategoryConfig : INoCategoryConfig - { - public string Config { get; set; } = null; - public string DataDir { get; set; } - public string ConfigsDirectory { get; set; } - public string BaseDbPath { get; set; } - public string Log { get; set; } - public string LoggerConfigSource { get; set; } - public string PluginsDirectory { get; set; } - public string MonitoringJob { get; set; } - public string MonitoringGroup { get; set; } - public string EnodeIpAddress { get; set; } - public bool HiveEnabled { get; set; } - public string Url { get; set; } - public string CorsOrigins { get; set; } - public string CliSwitchLocal { get; set; } - } + public string Config { get; set; } = null; + public string MonitoringJob { get; set; } + public string MonitoringGroup { get; set; } + public string EnodeIpAddress { get; set; } + public string Url { get; set; } + public string CorsOrigins { get; set; } + public string CliSwitchLocal { get; set; } } diff --git a/src/Nethermind/Nethermind.Hive/HivePlugin.cs b/src/Nethermind/Nethermind.Hive/HivePlugin.cs index 62aaef0a766..cc60b017556 100644 --- a/src/Nethermind/Nethermind.Hive/HivePlugin.cs +++ b/src/Nethermind/Nethermind.Hive/HivePlugin.cs @@ -8,72 +8,71 @@ using Nethermind.Api.Extensions; using Nethermind.Logging; -namespace Nethermind.Hive +namespace Nethermind.Hive; + +public class HivePlugin : INethermindPlugin { - public class HivePlugin : INethermindPlugin - { - private INethermindApi _api = null!; - private IHiveConfig _hiveConfig = null!; - private ILogger _logger; - private readonly CancellationTokenSource _disposeCancellationToken = new(); + private INethermindApi _api = null!; + private IHiveConfig _hiveConfig = null!; + private ILogger _logger; + private readonly CancellationTokenSource _disposeCancellationToken = new(); - public ValueTask DisposeAsync() - { - _disposeCancellationToken.Cancel(); - _disposeCancellationToken.Dispose(); - return ValueTask.CompletedTask; - } + public ValueTask DisposeAsync() + { + _disposeCancellationToken.Cancel(); + _disposeCancellationToken.Dispose(); + return ValueTask.CompletedTask; + } - public string Name => "Hive"; + public string Name => "Hive"; - public string Description => "Plugin used for executing Hive Ethereum Tests"; + public string Description => "Plugin used for executing Hive Ethereum Tests"; - public string Author => "Nethermind"; + public string Author => "Nethermind"; - public Task Init(INethermindApi api) - { - _api = api ?? throw new ArgumentNullException(nameof(api)); - _hiveConfig = _api.ConfigProvider.GetConfig(); - _logger = _api.LogManager.GetClassLogger(); + public Task Init(INethermindApi api) + { + _api = api ?? throw new ArgumentNullException(nameof(api)); + _hiveConfig = _api.ConfigProvider.GetConfig(); + _logger = _api.LogManager.GetClassLogger(); - Enabled = Environment.GetEnvironmentVariable("NETHERMIND_HIVE_ENABLED")?.ToLowerInvariant() == "true" || _hiveConfig.Enabled; + Enabled = _hiveConfig.Enabled; - return Task.CompletedTask; - } - - public async Task InitNetworkProtocol() - { - if (Enabled) - { - if (_api.BlockTree is null) throw new ArgumentNullException(nameof(_api.BlockTree)); - if (_api.BlockProcessingQueue is null) throw new ArgumentNullException(nameof(_api.BlockProcessingQueue)); - if (_api.ConfigProvider is null) throw new ArgumentNullException(nameof(_api.ConfigProvider)); - if (_api.LogManager is null) throw new ArgumentNullException(nameof(_api.LogManager)); - if (_api.FileSystem is null) throw new ArgumentNullException(nameof(_api.FileSystem)); - if (_api.BlockValidator is null) throw new ArgumentNullException(nameof(_api.BlockValidator)); - - _api.TxGossipPolicy.Policies.Clear(); - - HiveRunner hiveRunner = new( - _api.BlockTree, - _api.BlockProcessingQueue, - _api.ConfigProvider, - _api.LogManager.GetClassLogger(), - _api.FileSystem, - _api.BlockValidator - ); - - if (_logger.IsInfo) _logger.Info("Hive is starting"); - - await hiveRunner.Start(_disposeCancellationToken.Token); - } - } + return Task.CompletedTask; + } - public Task InitRpcModules() + public async Task InitNetworkProtocol() + { + if (Enabled) { - return Task.CompletedTask; + if (_api.BlockTree is null) throw new ArgumentNullException(nameof(_api.BlockTree)); + if (_api.BlockProcessingQueue is null) throw new ArgumentNullException(nameof(_api.BlockProcessingQueue)); + if (_api.ConfigProvider is null) throw new ArgumentNullException(nameof(_api.ConfigProvider)); + if (_api.LogManager is null) throw new ArgumentNullException(nameof(_api.LogManager)); + if (_api.FileSystem is null) throw new ArgumentNullException(nameof(_api.FileSystem)); + if (_api.BlockValidator is null) throw new ArgumentNullException(nameof(_api.BlockValidator)); + + _api.TxGossipPolicy.Policies.Clear(); + + HiveRunner hiveRunner = new( + _api.BlockTree, + _api.BlockProcessingQueue, + _api.ConfigProvider, + _api.LogManager.GetClassLogger(), + _api.FileSystem, + _api.BlockValidator + ); + + if (_logger.IsInfo) _logger.Info("Hive is starting"); + + await hiveRunner.Start(_disposeCancellationToken.Token); } + } - private bool Enabled { get; set; } + public Task InitRpcModules() + { + return Task.CompletedTask; } + + private bool Enabled { get; set; } } diff --git a/src/Nethermind/Nethermind.Logging/PathUtils.cs b/src/Nethermind/Nethermind.Logging/PathUtils.cs index 62a8cd6b29b..9a16cedbfca 100644 --- a/src/Nethermind/Nethermind.Logging/PathUtils.cs +++ b/src/Nethermind/Nethermind.Logging/PathUtils.cs @@ -7,57 +7,56 @@ using System.Linq; using System.Reflection; -namespace Nethermind.Logging +namespace Nethermind.Logging; + +public static class PathUtils { - public static class PathUtils + public static string ExecutingDirectory { get; } + + static PathUtils() { - public static string ExecutingDirectory { get; } + Process process = Process.GetCurrentProcess(); + if (process.ProcessName.StartsWith("dotnet", StringComparison.InvariantCultureIgnoreCase) + || process.ProcessName.Equals("ReSharperTestRunner", StringComparison.InvariantCultureIgnoreCase)) + { + ExecutingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + } + else + { + ExecutingDirectory = Path.GetDirectoryName(process.MainModule.FileName); + //Console.WriteLine($"Resolved executing directory as {ExecutingDirectory}."); + } + } - static PathUtils() + public static string GetApplicationResourcePath(this string resourcePath, string overridePrefixPath = null) + { + if (string.IsNullOrWhiteSpace(resourcePath)) { - Process process = Process.GetCurrentProcess(); - if (process.ProcessName.StartsWith("dotnet", StringComparison.InvariantCultureIgnoreCase) - || process.ProcessName.Equals("ReSharperTestRunner", StringComparison.InvariantCultureIgnoreCase)) - { - ExecutingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - } - else - { - ExecutingDirectory = Path.GetDirectoryName(process.MainModule.FileName); - Console.WriteLine($"Resolved executing directory as {ExecutingDirectory}."); - } + resourcePath = string.Empty; } - public static string GetApplicationResourcePath(this string resourcePath, string overridePrefixPath = null) + if (Path.IsPathRooted(resourcePath) || IsExplicitlyRelative(resourcePath)) { - if (string.IsNullOrWhiteSpace(resourcePath)) - { - resourcePath = string.Empty; - } - - if (Path.IsPathRooted(resourcePath) || IsExplicitlyRelative(resourcePath)) - { - return resourcePath; - } - - if (string.IsNullOrEmpty(overridePrefixPath)) - { - return Path.Combine(ExecutingDirectory, resourcePath); - } - - return Path.IsPathRooted(overridePrefixPath) || IsExplicitlyRelative(overridePrefixPath) - ? Path.Combine(overridePrefixPath, resourcePath) - : Path.Combine(ExecutingDirectory, overridePrefixPath, resourcePath); + return resourcePath; } - static readonly string[] RelativePrefixes = new[] + if (string.IsNullOrEmpty(overridePrefixPath)) { - "." + Path.DirectorySeparatorChar, - "." + Path.AltDirectorySeparatorChar, - ".." + Path.DirectorySeparatorChar, - ".." + Path.AltDirectorySeparatorChar, - }.Distinct().ToArray(); + return Path.Combine(ExecutingDirectory, resourcePath); + } - public static bool IsExplicitlyRelative(string resourcePath) => RelativePrefixes.Any(resourcePath.StartsWith); + return Path.IsPathRooted(overridePrefixPath) || IsExplicitlyRelative(overridePrefixPath) + ? Path.Combine(overridePrefixPath, resourcePath) + : Path.Combine(ExecutingDirectory, overridePrefixPath, resourcePath); } + + static readonly string[] RelativePrefixes = new[] + { + "." + Path.DirectorySeparatorChar, + "." + Path.AltDirectorySeparatorChar, + ".." + Path.DirectorySeparatorChar, + ".." + Path.AltDirectorySeparatorChar, + }.Distinct().ToArray(); + + public static bool IsExplicitlyRelative(string resourcePath) => RelativePrefixes.Any(resourcePath.StartsWith); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs index d93e6c8a649..e86e16fc5a8 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs @@ -75,7 +75,7 @@ public void Setup() public void SlotPerSeconds_has_different_value_in_mergeConfig_and_blocksConfig() { - JsonConfigSource? jsonSource = new("MisconfiguredConfig.cfg"); + JsonConfigSource? jsonSource = new("MisconfiguredConfig.json"); ConfigProvider? configProvider = new(); configProvider.AddSource(jsonSource); configProvider.Initialize(); diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/MisconfiguredConfig.cfg b/src/Nethermind/Nethermind.Merge.Plugin.Test/MisconfiguredConfig.json similarity index 100% rename from src/Nethermind/Nethermind.Merge.Plugin.Test/MisconfiguredConfig.cfg rename to src/Nethermind/Nethermind.Merge.Plugin.Test/MisconfiguredConfig.json diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/Nethermind.Merge.Plugin.Test.csproj b/src/Nethermind/Nethermind.Merge.Plugin.Test/Nethermind.Merge.Plugin.Test.csproj index b89fce1e281..91f60f830a6 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/Nethermind.Merge.Plugin.Test.csproj +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/Nethermind.Merge.Plugin.Test.csproj @@ -28,7 +28,7 @@ - + PreserveNewest diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestBuilder.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/TestBuilder.cs index 72dd3fac275..17ee00acea8 100644 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/TestBuilder.cs +++ b/src/Nethermind/Nethermind.Overseer.Test/Framework/TestBuilder.cs @@ -10,292 +10,291 @@ using Nethermind.Overseer.Test.Framework.Steps; using NUnit.Framework; -namespace Nethermind.Overseer.Test.Framework +namespace Nethermind.Overseer.Test.Framework; + +/// +/// https://stackoverflow.com/questions/32112418/how-to-make-a-fluent-async-inferface-in-c-sharp +/// +public class TestBuilder { - /// - /// https://stackoverflow.com/questions/32112418/how-to-make-a-fluent-async-inferface-in-c-sharp - /// - public class TestBuilder + [TearDown] + public void TearDown() { - [TearDown] - public void TearDown() - { - var passedCount = _results.Count(r => r.Passed); - var failedCount = _results.Count - passedCount; + var passedCount = _results.Count(r => r.Passed); + var failedCount = _results.Count - passedCount; - TestContext.Out.WriteLine("=========================== TESTS RESULTS ==========================="); - TestContext.Out.WriteLine($"TESTS PASSED: {passedCount}, FAILED: {failedCount}"); - foreach (var testResult in _results) - { - string message = $"{testResult.Order}. {testResult.Name} has " + - $"{(testResult.Passed ? "passed [+]" : "failed [-]")}"; - TestContext.Out.WriteLine(message); - } + TestContext.Out.WriteLine("=========================== TESTS RESULTS ==========================="); + TestContext.Out.WriteLine($"TESTS PASSED: {passedCount}, FAILED: {failedCount}"); + foreach (var testResult in _results) + { + string message = $"{testResult.Order}. {testResult.Name} has " + + $"{(testResult.Passed ? "passed [+]" : "failed [-]")}"; + TestContext.Out.WriteLine(message); } + } #pragma warning disable NUnit1032 - /// - /// Gets the task representing the fluent work. - /// - /// - /// The task. - /// - public Task ScenarioCompletion { get; private set; } + /// + /// Gets the task representing the fluent work. + /// + /// + /// The task. + /// + public Task ScenarioCompletion { get; private set; } #pragma warning restore NUnit1032 - /// - /// Queues up asynchronous work. - /// - /// The work to be queued. - public void QueueWork(Action work) + /// + /// Queues up asynchronous work. + /// + /// The work to be queued. + public void QueueWork(Action work) + { + // queue up the work + ScenarioCompletion = ScenarioCompletion.ContinueWith(task => { - // queue up the work - ScenarioCompletion = ScenarioCompletion.ContinueWith(task => + try { - try - { - work(); - } - catch (Exception e) - { - TestContext.Out.WriteLine(e.ToString()); - throw; - } - - return this; - }, TaskContinuationOptions.OnlyOnRanToCompletion); - } - - /// - /// Queues up asynchronous work. - /// - /// The work to be queued. - public void QueueWork(Func work) - { - // queue up the work - ScenarioCompletion = ScenarioCompletion.ContinueWith(async task => + work(); + } + catch (Exception e) { - try - { - await work(); - } - catch (Exception e) - { - TestContext.Out.WriteLine(e.ToString()); - throw; - } - - return this; - }, TaskContinuationOptions.OnlyOnRanToCompletion); - } + TestContext.Out.WriteLine(e.ToString()); + throw; + } - public void QueueWork(TestStepBase step) + return this; + }, TaskContinuationOptions.OnlyOnRanToCompletion); + } + + /// + /// Queues up asynchronous work. + /// + /// The work to be queued. + public void QueueWork(Func work) + { + // queue up the work + ScenarioCompletion = ScenarioCompletion.ContinueWith(async task => { - // queue up the work - ScenarioCompletion = ScenarioCompletion.ContinueWith(async task => + try { - TestContext.Out.WriteLine($"Awaiting step {step.Name}"); - try - { - _results.Add(await step.ExecuteAsync()); - } - catch (Exception e) - { - TestContext.Out.WriteLine($"Step {step.Name} failed with error: {e}"); - throw; - } - - TestContext.Out.WriteLine($"Step {step.Name} complete"); - }, TaskContinuationOptions.OnlyOnRanToCompletion).Unwrap(); - } - - private readonly ProcessBuilder _processBuilder; + await work(); + } + catch (Exception e) + { + TestContext.Out.WriteLine(e.ToString()); + throw; + } - private static readonly string _runnerDir; - private static readonly string _dbsDir; - private static readonly string _configsDir; + return this; + }, TaskContinuationOptions.OnlyOnRanToCompletion); + } - static TestBuilder() + public void QueueWork(TestStepBase step) + { + // queue up the work + ScenarioCompletion = ScenarioCompletion.ContinueWith(async task => { - string testContextDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "context"); - _runnerDir = Path.Combine(testContextDir, "runner"); - _configsDir = Path.Combine(testContextDir, "configs"); - _dbsDir = Path.Combine(testContextDir, "dbs"); - - if (Directory.Exists(testContextDir)) + TestContext.Out.WriteLine($"Awaiting step {step.Name}"); + try { - Directory.Delete(testContextDir, true); + _results.Add(await step.ExecuteAsync()); + } + catch (Exception e) + { + TestContext.Out.WriteLine($"Step {step.Name} failed with error: {e}"); + throw; } - Directory.CreateDirectory(_dbsDir); - Directory.CreateDirectory(_configsDir); - } + TestContext.Out.WriteLine($"Step {step.Name} complete"); + }, TaskContinuationOptions.OnlyOnRanToCompletion).Unwrap(); + } - public TestBuilder() - { - _processBuilder = new ProcessBuilder(); + private readonly ProcessBuilder _processBuilder; - if (!Directory.Exists(_runnerDir)) - { - Directory.CreateDirectory(_runnerDir); - CopyRunnerFiles(_runnerDir); - } + private static readonly string _runnerDir; + private static readonly string _dbsDir; + private static readonly string _configsDir; - // The entry point for the async work. - // Spin up a completed task to start with - // so that we dont have to do null checks - this.ScenarioCompletion = Task.FromResult(0); + static TestBuilder() + { + string testContextDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "context"); + _runnerDir = Path.Combine(testContextDir, "runner"); + _configsDir = Path.Combine(testContextDir, "configs"); + _dbsDir = Path.Combine(testContextDir, "dbs"); + + if (Directory.Exists(testContextDir)) + { + Directory.Delete(testContextDir, true); } - public T SetContext(T newContext) where T : ITestContext + Directory.CreateDirectory(_dbsDir); + Directory.CreateDirectory(_configsDir); + } + + public TestBuilder() + { + _processBuilder = new ProcessBuilder(); + + if (!Directory.Exists(_runnerDir)) { - newContext.SetBuilder(this); - return newContext; + Directory.CreateDirectory(_runnerDir); + CopyRunnerFiles(_runnerDir); } - private const int _startHttpPort = 8600; - private const int _startPort = 30200; + // The entry point for the async work. + // Spin up a completed task to start with + // so that we dont have to do null checks + this.ScenarioCompletion = Task.FromResult(0); + } - private byte _nodeCounter; + public T SetContext(T newContext) where T : ITestContext + { + newContext.SetBuilder(this); + return newContext; + } - public NethermindProcessWrapper CurrentNode { get; private set; } + private const int _startHttpPort = 8600; + private const int _startPort = 30200; - public List _results = new List(); + private byte _nodeCounter; - public TestBuilder SwitchNode(string node) - { - CurrentNode = Nodes[node]; - return this; - } + public NethermindProcessWrapper CurrentNode { get; private set; } - public TestBuilder Wait(int delay = 5000, string name = "Wait") - { - QueueWork(async () => await Task.Delay(delay)); - return this; - } + public List _results = new List(); - public TestBuilder StartCliqueNode(string name) - { - return StartNode(name, "configs/cliqueNode.cfg"); - } + public TestBuilder SwitchNode(string node) + { + CurrentNode = Nodes[node]; + return this; + } - public TestBuilder StartCliqueMiner(string name) - { - return StartNode(name, "configs/cliqueMiner.cfg"); - } + public TestBuilder Wait(int delay = 5000, string name = "Wait") + { + QueueWork(async () => await Task.Delay(delay)); + return this; + } - public TestBuilder StartAuRaMiner(string name, string key) - { - return StartNode(name, "configs/auRaMiner.cfg", key); - } + public TestBuilder StartCliqueNode(string name) + { + return StartNode(name, "configs/cliqueNode.json"); + } - public TestBuilder StartNode(string name, string baseConfigFile, string key = null) - { - CurrentNode = GetOrCreateNode(name, baseConfigFile, key); - var step = new StartProcessTestStep($"Start {name}", CurrentNode); - QueueWork(step); - return this; - } + public TestBuilder StartCliqueMiner(string name) + { + return StartNode(name, "configs/cliqueMiner.json"); + } - private NethermindProcessWrapper GetOrCreateNode(string name, string baseConfigFile, string key) - { - if (!Nodes.TryGetValue(name, out NethermindProcessWrapper value)) - { - string bootnodes = string.Empty; - foreach ((_, NethermindProcessWrapper process) in Nodes) - { - bootnodes += $",{process.Enode}"; - } - - bootnodes = bootnodes.TrimStart(','); - - var nodeKey = GetNodeKey(key); - - string dbDir = Path.Combine(_dbsDir, name); - string configPath = Path.Combine(_configsDir, $"{name}.cfg"); - File.Copy(baseConfigFile, configPath); - int p2pPort = _startPort + _nodeCounter; - int httpPort = _startHttpPort + _nodeCounter; - TestContext.Out.WriteLine($"Creating {name} at {p2pPort}, http://localhost:{httpPort}"); - value = _processBuilder.Create(name, _runnerDir, configPath, dbDir, httpPort, p2pPort, nodeKey, bootnodes); - Nodes[name] = value; - _nodeCounter++; - } + public TestBuilder StartAuRaMiner(string name, string key) + { + return StartNode(name, "configs/auRaMiner.json", key); + } - return value; - } + public TestBuilder StartNode(string name, string baseConfigFile, string key = null) + { + CurrentNode = GetOrCreateNode(name, baseConfigFile, key); + var step = new StartProcessTestStep($"Start {name}", CurrentNode); + QueueWork(step); + return this; + } - private string GetNodeKey(string key) + private NethermindProcessWrapper GetOrCreateNode(string name, string baseConfigFile, string key) + { + if (!Nodes.TryGetValue(name, out NethermindProcessWrapper value)) { - if (key is null) + string bootnodes = string.Empty; + foreach ((_, NethermindProcessWrapper process) in Nodes) { - byte[] keyArray = new byte[32]; - keyArray[0] = 1; - keyArray[31] = _nodeCounter; - key = keyArray.ToHexString(); + bootnodes += $",{process.Enode}"; } - return key; + bootnodes = bootnodes.TrimStart(','); + + var nodeKey = GetNodeKey(key); + + string dbDir = Path.Combine(_dbsDir, name); + string configPath = Path.Combine(_configsDir, $"{name}.json"); + File.Copy(baseConfigFile, configPath); + int p2pPort = _startPort + _nodeCounter; + int httpPort = _startHttpPort + _nodeCounter; + TestContext.Out.WriteLine($"Creating {name} at {p2pPort}, http://localhost:{httpPort}"); + value = _processBuilder.Create(name, _runnerDir, configPath, dbDir, httpPort, p2pPort, nodeKey, bootnodes); + Nodes[name] = value; + _nodeCounter++; } - public Dictionary Nodes { get; } = new Dictionary(); + return value; + } - public TestBuilder Kill() + private string GetNodeKey(string key) + { + if (key is null) { - return Kill(CurrentNode.Name); + byte[] keyArray = new byte[32]; + keyArray[0] = 1; + keyArray[31] = _nodeCounter; + key = keyArray.ToHexString(); } - public TestBuilder Kill(string name) + return key; + } + + public Dictionary Nodes { get; } = new Dictionary(); + + public TestBuilder Kill() + { + return Kill(CurrentNode.Name); + } + + public TestBuilder Kill(string name) + { + var step = new KillProcessTestStep($"Kill {name}", Nodes[name]); + QueueWork(step); + return this; + } + + public TestBuilder KillAll() + { + foreach (KeyValuePair keyValuePair in Nodes) { - var step = new KillProcessTestStep($"Kill {name}", Nodes[name]); + var step = new KillProcessTestStep($"Kill {keyValuePair.Key}", Nodes[keyValuePair.Key]); QueueWork(step); - return this; } - public TestBuilder KillAll() - { - foreach (KeyValuePair keyValuePair in Nodes) - { - var step = new KillProcessTestStep($"Kill {keyValuePair.Key}", Nodes[keyValuePair.Key]); - QueueWork(step); - } - - return this; - } + return this; + } #if DEBUG - const string buildConfiguration = "Debug"; + const string buildConfiguration = "Debug"; #else - const string buildConfiguration = "Release"; + const string buildConfiguration = "Release"; #endif - private void CopyRunnerFiles(string targetDirectory) + private void CopyRunnerFiles(string targetDirectory) + { + string sourceDirectory = Path.Combine(Directory.GetCurrentDirectory(), $"../../../../artifacts/bin/Nethermind.Runner/{buildConfiguration}/"); + if (!Directory.Exists(sourceDirectory)) { - string sourceDirectory = Path.Combine(Directory.GetCurrentDirectory(), $"../../../../artifacts/bin/Nethermind.Runner/{buildConfiguration}/"); - if (!Directory.Exists(sourceDirectory)) - { - throw new IOException($"Runner not found at {sourceDirectory}"); - } - - TestContext.Out.WriteLine($"Copying runner files from {sourceDirectory} to {targetDirectory}"); - CopyDir(sourceDirectory, targetDirectory); - string chainsDir = Path.Combine(Directory.GetCurrentDirectory(), "chainspec"); - CopyDir(chainsDir, Path.Combine(targetDirectory, "chainspec")); + throw new IOException($"Runner not found at {sourceDirectory}"); } - private void CopyDir(string sourceDirectory, string targetDirectory) + TestContext.Out.WriteLine($"Copying runner files from {sourceDirectory} to {targetDirectory}"); + CopyDir(sourceDirectory, targetDirectory); + string chainsDir = Path.Combine(Directory.GetCurrentDirectory(), "chainspec"); + CopyDir(chainsDir, Path.Combine(targetDirectory, "chainspec")); + } + + private void CopyDir(string sourceDirectory, string targetDirectory) + { + foreach (string file in Directory.GetFiles(sourceDirectory)) { - foreach (string file in Directory.GetFiles(sourceDirectory)) - { - File.Copy(file, Path.Combine(targetDirectory, Path.GetFileName(file)), true); - } + File.Copy(file, Path.Combine(targetDirectory, Path.GetFileName(file)), true); + } - foreach (string directory in Directory.GetDirectories(sourceDirectory)) - { - string targetSubDir = Path.Combine(targetDirectory, Path.GetFileName(directory)); - Directory.CreateDirectory(targetSubDir); - CopyDir(directory, targetSubDir); - } + foreach (string directory in Directory.GetDirectories(sourceDirectory)) + { + string targetSubDir = Path.Combine(targetDirectory, Path.GetFileName(directory)); + Directory.CreateDirectory(targetSubDir); + CopyDir(directory, targetSubDir); } } } diff --git a/src/Nethermind/Nethermind.Overseer.Test/Nethermind.Overseer.Test.csproj b/src/Nethermind/Nethermind.Overseer.Test/Nethermind.Overseer.Test.csproj index b88dcc1ca88..f7f3b45b2f5 100644 --- a/src/Nethermind/Nethermind.Overseer.Test/Nethermind.Overseer.Test.csproj +++ b/src/Nethermind/Nethermind.Overseer.Test/Nethermind.Overseer.Test.csproj @@ -26,13 +26,13 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest diff --git a/src/Nethermind/Nethermind.Overseer.Test/configs/auRaMiner.cfg b/src/Nethermind/Nethermind.Overseer.Test/configs/auRaMiner.json similarity index 100% rename from src/Nethermind/Nethermind.Overseer.Test/configs/auRaMiner.cfg rename to src/Nethermind/Nethermind.Overseer.Test/configs/auRaMiner.json diff --git a/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueMiner.cfg b/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueMiner.json similarity index 100% rename from src/Nethermind/Nethermind.Overseer.Test/configs/cliqueMiner.cfg rename to src/Nethermind/Nethermind.Overseer.Test/configs/cliqueMiner.json diff --git a/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueNode.cfg b/src/Nethermind/Nethermind.Overseer.Test/configs/cliqueNode.json similarity index 100% rename from src/Nethermind/Nethermind.Overseer.Test/configs/cliqueNode.cfg rename to src/Nethermind/Nethermind.Overseer.Test/configs/cliqueNode.json diff --git a/src/Nethermind/Nethermind.Runner.Test/ConfigFilesTests.cs b/src/Nethermind/Nethermind.Runner.Test/ConfigFilesTests.cs index 49a9cbf7d50..e5f1956dde8 100644 --- a/src/Nethermind/Nethermind.Runner.Test/ConfigFilesTests.cs +++ b/src/Nethermind/Nethermind.Runner.Test/ConfigFilesTests.cs @@ -42,7 +42,7 @@ public void Required_config_files_exist(string configWildcard) // maybe leave in test since deprecation has not fully happened? [TestCase("validators", true)] - [TestCase("poacore_validator.cfg", true)] + [TestCase("poacore_validator.json", true)] [TestCase("spaceneth", false)] [TestCase("archive", false)] [TestCase("fast", true)] @@ -149,8 +149,8 @@ public void Cache_state_index(string configWildcard, bool expectedValue) [TestCase("gnosis ^archive", 768000000)] [TestCase("poacore archive", 1024000000)] [TestCase("poacore ^archive", 768000000)] - [TestCase("spaceneth.cfg", 64000000)] - [TestCase("spaceneth_persistent.cfg", 128000000)] + [TestCase("spaceneth.json", 64000000)] + [TestCase("spaceneth_persistent.json", 128000000)] public void Memory_hint_values_are_correct(string configWildcard, long expectedValue) { Test(configWildcard, c => c.MemoryHint, expectedValue); @@ -160,7 +160,7 @@ public void Memory_hint_values_are_correct(string configWildcard, long expectedV public void Metrics_disabled_by_default(string configWildcard) { Test(configWildcard, c => c.Enabled, false); - Test(configWildcard, c => c.NodeName.ToUpperInvariant(), (cf, p) => cf.Replace("_", " ").Replace(".cfg", "").ToUpperInvariant().Replace("POACORE", "POA CORE")); + Test(configWildcard, c => c.NodeName.ToUpperInvariant(), (cf, p) => cf.Replace("_", " ").Replace(".json", "").ToUpperInvariant().Replace("POACORE", "POA CORE")); Test(configWildcard, c => c.IntervalSeconds, 5); Test(configWildcard, c => c.PushGatewayUrl, ""); } @@ -229,12 +229,12 @@ public void Fast_sync_settings_as_expected(string configWildcard, bool downloadB } [TestCase("archive", false)] - [TestCase("mainnet.cfg", true)] - [TestCase("sepolia.cfg", true)] - [TestCase("gnosis.cfg", true)] - [TestCase("chiado.cfg", true)] - [TestCase("energyweb.cfg", false)] - [TestCase("volta.cfg", false)] + [TestCase("mainnet.json", true)] + [TestCase("sepolia.json", true)] + [TestCase("gnosis.json", true)] + [TestCase("chiado.json", true)] + [TestCase("energyweb.json", false)] + [TestCase("volta.json", false)] public void Snap_sync_settings_as_expected(string configWildcard, bool enabled) { Test(configWildcard, c => c.SnapSync, enabled); @@ -251,7 +251,7 @@ public void Stays_on_full_sync(string configWildcard, bool stickToFullSyncAfterF Test(configWildcard, c => c.FastSyncCatchUpHeightDelta, stickToFullSyncAfterFastSync ? 10_000_000_000 : 8192); } - [TestCase("^spaceneth.cfg")] + [TestCase("^spaceneth.json")] public void Diagnostics_mode_is_not_enabled_by_default(string configWildcard) { Test(configWildcard, c => c.DiagnosticMode, DiagnosticMode.None); @@ -294,8 +294,8 @@ public void Stores_receipts(string configWildcard, bool storeReceipts) Test(configWildcard, c => c.StoreReceipts, storeReceipts); } - [TestCase("mainnet_archive.cfg", true)] - [TestCase("mainnet.cfg", true)] + [TestCase("mainnet_archive.json", true)] + [TestCase("mainnet.json", true)] [TestCase("poacore", true)] [TestCase("gnosis", true)] [TestCase("volta", false)] @@ -312,7 +312,7 @@ public void Basic_configs_are_as_expected(string configWildcard, bool isProducti Test(configWildcard, c => c.EnableUnsecuredDevWallet, false); } - Test(configWildcard, c => c.LogFileName, (cf, p) => p.Should().Be(cf.Replace("cfg", "logs.txt"), cf)); + Test(configWildcard, c => c.LogFileName, (cf, p) => p.Should().Be(cf.Replace("json", "logs.txt"), cf)); } [TestCase("*")] @@ -334,11 +334,11 @@ public void Blob_txs_support_is_correct(string configWildcard, BlobsSupportMode [TestCase("mainnet")] - [TestCase("poacore.cfg", new[] { 16, 16, 16, 16 })] - [TestCase("poacore_archive.cfg", new[] { 16, 16, 16, 16 })] - [TestCase("poacore_validator.cfg", null, false)] - [TestCase("gnosis.cfg", new[] { 16, 16, 16 })] - [TestCase("gnosis_archive.cfg", new[] { 16, 16, 16 })] + [TestCase("poacore.json", new[] { 16, 16, 16, 16 })] + [TestCase("poacore_archive.json", new[] { 16, 16, 16, 16 })] + [TestCase("poacore_validator.json", null, false)] + [TestCase("gnosis.json", new[] { 16, 16, 16 })] + [TestCase("gnosis_archive.json", new[] { 16, 16, 16 })] [TestCase("volta")] public void Bloom_configs_are_as_expected(string configWildcard, int[] levels = null, bool index = true) { @@ -416,24 +416,24 @@ public void Memory_hint_is_enough(string configWildcard) protected override IEnumerable Configs { get; } = new HashSet { - "holesky.cfg", - "holesky_archive.cfg", - "mainnet_archive.cfg", - "mainnet.cfg", - "poacore.cfg", - "poacore_archive.cfg", - "gnosis.cfg", - "gnosis_archive.cfg", - "spaceneth.cfg", - "spaceneth_persistent.cfg", - "volta.cfg", - "volta_archive.cfg", - "energyweb.cfg", - "energyweb_archive.cfg", - "sepolia.cfg", - "sepolia_archive.cfg", - "chiado.cfg", - "chiado_archive.cfg", + "holesky.json", + "holesky_archive.json", + "mainnet_archive.json", + "mainnet.json", + "poacore.json", + "poacore_archive.json", + "gnosis.json", + "gnosis_archive.json", + "spaceneth.json", + "spaceneth_persistent.json", + "volta.json", + "volta_archive.json", + "energyweb.json", + "energyweb_archive.json", + "sepolia.json", + "sepolia_archive.json", + "chiado.json", + "chiado_archive.json", }; public IEnumerable AllIndexesOf(string str, string searchString) diff --git a/src/Nethermind/Nethermind.Runner.Test/EthereumRunnerTests.cs b/src/Nethermind/Nethermind.Runner.Test/EthereumRunnerTests.cs index 2b2c1afbcf9..8a46d508bef 100644 --- a/src/Nethermind/Nethermind.Runner.Test/EthereumRunnerTests.cs +++ b/src/Nethermind/Nethermind.Runner.Test/EthereumRunnerTests.cs @@ -11,20 +11,15 @@ using System.Threading; using System.Threading.Tasks; using Nethermind.Api; -using Nethermind.Blockchain.Synchronization; using Nethermind.Config; using Nethermind.Core.Test.IO; -using Nethermind.Db.Rocks.Config; -using Nethermind.EthStats; +using Nethermind.Hive; using Nethermind.JsonRpc; using Nethermind.JsonRpc.Modules; -using Nethermind.KeyStore.Config; using Nethermind.Logging; using Nethermind.Network.Config; using Nethermind.Runner.Ethereum; -using Nethermind.Db.Blooms; using Nethermind.Runner.Ethereum.Api; -using Nethermind.TxPool; using NUnit.Framework; namespace Nethermind.Runner.Test; @@ -98,21 +93,8 @@ public async Task Smoke_cancel((string file, ConfigProvider configProvider) test private static async Task SmokeTest(ConfigProvider configProvider, int testIndex, int basePort, bool cancel = false) { - Type type1 = typeof(ITxPoolConfig); - Type type2 = typeof(INetworkConfig); - Type type3 = typeof(IKeyStoreConfig); - Type type4 = typeof(IDbConfig); - Type type7 = typeof(IEthStatsConfig); - Type type8 = typeof(ISyncConfig); - Type type9 = typeof(IBloomConfig); - - Console.WriteLine(type1.Name); - Console.WriteLine(type2.Name); - Console.WriteLine(type3.Name); - Console.WriteLine(type4.Name); - Console.WriteLine(type7.Name); - Console.WriteLine(type8.Name); - Console.WriteLine(type9.Name); + // An ugly hack to keep unused types + Console.WriteLine(typeof(IHiveConfig)); var tempPath = TempPath.GetTempDirectory(); Directory.CreateDirectory(tempPath.Path); diff --git a/src/Nethermind/Nethermind.Runner.Test/Nethermind.Runner.Test.csproj b/src/Nethermind/Nethermind.Runner.Test/Nethermind.Runner.Test.csproj index 3feaac88b64..1a486552b62 100644 --- a/src/Nethermind/Nethermind.Runner.Test/Nethermind.Runner.Test.csproj +++ b/src/Nethermind/Nethermind.Runner.Test/Nethermind.Runner.Test.csproj @@ -52,7 +52,7 @@ Chains\gnosis.json - + PreserveNewest diff --git a/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs b/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs index 9d17ac28787..04c7827288a 100644 --- a/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs +++ b/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs @@ -11,84 +11,83 @@ using Nethermind.Config; using Nethermind.Consensus; using Nethermind.Core; -using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Hive; using Nethermind.Logging; using Nethermind.Serialization.Json; using Nethermind.Specs.ChainSpecStyle; -namespace Nethermind.Runner.Ethereum.Api +namespace Nethermind.Runner.Ethereum.Api; + +public class ApiBuilder { - public class ApiBuilder + private readonly IConfigProvider _configProvider; + private readonly IJsonSerializer _jsonSerializer; + private readonly ILogManager _logManager; + private readonly Nethermind.Logging.ILogger _logger; + private readonly IInitConfig _initConfig; + + public ApiBuilder(IConfigProvider configProvider, ILogManager logManager) { - private readonly IConfigProvider _configProvider; - private readonly IJsonSerializer _jsonSerializer; - private readonly ILogManager _logManager; - private readonly Nethermind.Logging.ILogger _logger; - private readonly IInitConfig _initConfig; + _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); + _logger = _logManager.GetClassLogger(); + _configProvider = configProvider ?? throw new ArgumentNullException(nameof(configProvider)); + _initConfig = configProvider.GetConfig(); + _jsonSerializer = new EthereumJsonSerializer(); + } - public ApiBuilder(IConfigProvider configProvider, ILogManager logManager) + public INethermindApi Create(params IConsensusPlugin[] consensusPlugins) => + Create((IEnumerable)consensusPlugins); + + public INethermindApi Create(IEnumerable consensusPlugins) + { + ChainSpec chainSpec = LoadChainSpec(_jsonSerializer); + bool wasCreated = Interlocked.CompareExchange(ref _apiCreated, 1, 0) == 1; + if (wasCreated) { - _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); - _logger = _logManager.GetClassLogger(); - _configProvider = configProvider ?? throw new ArgumentNullException(nameof(configProvider)); - _initConfig = configProvider.GetConfig(); - _jsonSerializer = new EthereumJsonSerializer(); + throw new NotSupportedException("Creation of multiple APIs not supported."); } - public INethermindApi Create(params IConsensusPlugin[] consensusPlugins) => - Create((IEnumerable)consensusPlugins); + string engine = chainSpec.SealEngineType; + IConsensusPlugin? enginePlugin = consensusPlugins.FirstOrDefault(p => p.SealEngineType == engine); - public INethermindApi Create(IEnumerable consensusPlugins) - { - ChainSpec chainSpec = LoadChainSpec(_jsonSerializer); - bool wasCreated = Interlocked.CompareExchange(ref _apiCreated, 1, 0) == 1; - if (wasCreated) - { - throw new NotSupportedException("Creation of multiple APIs not supported."); - } - - string engine = chainSpec.SealEngineType; - IConsensusPlugin? enginePlugin = consensusPlugins.FirstOrDefault(p => p.SealEngineType == engine); - - INethermindApi nethermindApi = - enginePlugin?.CreateApi(_configProvider, _jsonSerializer, _logManager, chainSpec) ?? - new NethermindApi(_configProvider, _jsonSerializer, _logManager, chainSpec); - nethermindApi.SealEngineType = engine; - nethermindApi.SpecProvider = new ChainSpecBasedSpecProvider(chainSpec, _logManager); - nethermindApi.GasLimitCalculator = new FollowOtherMiners(nethermindApi.SpecProvider); - - SetLoggerVariables(chainSpec); - - return nethermindApi; - } + INethermindApi nethermindApi = + enginePlugin?.CreateApi(_configProvider, _jsonSerializer, _logManager, chainSpec) ?? + new NethermindApi(_configProvider, _jsonSerializer, _logManager, chainSpec); + nethermindApi.SealEngineType = engine; + nethermindApi.SpecProvider = new ChainSpecBasedSpecProvider(chainSpec, _logManager); + nethermindApi.GasLimitCalculator = new FollowOtherMiners(nethermindApi.SpecProvider); - private int _apiCreated; + SetLoggerVariables(chainSpec); - private ChainSpec LoadChainSpec(IJsonSerializer ethereumJsonSerializer) - { - bool hiveEnabled = Environment.GetEnvironmentVariable("NETHERMIND_HIVE_ENABLED")?.ToLowerInvariant() == "true"; - bool hiveChainSpecExists = File.Exists(_initConfig.HiveChainSpecPath); + return nethermindApi; + } - string chainSpecFile; - if (hiveEnabled && hiveChainSpecExists) - chainSpecFile = _initConfig.HiveChainSpecPath; - else - chainSpecFile = _initConfig.ChainSpecPath; + private int _apiCreated; - if (_logger.IsDebug) _logger.Debug($"Loading chain spec from {chainSpecFile}"); + private ChainSpec LoadChainSpec(IJsonSerializer ethereumJsonSerializer) + { + IHiveConfig hiveConfig = _configProvider.GetConfig(); + bool hiveChainSpecExists = File.Exists(_initConfig.HiveChainSpecPath); - ThisNodeInfo.AddInfo("Chainspec :", $"{chainSpecFile}"); + string chainSpecFile; + if (hiveConfig.Enabled && hiveChainSpecExists) + chainSpecFile = _initConfig.HiveChainSpecPath; + else + chainSpecFile = _initConfig.ChainSpecPath; - IChainSpecLoader loader = new ChainSpecLoader(ethereumJsonSerializer); - ChainSpec chainSpec = loader.LoadEmbeddedOrFromFile(chainSpecFile, _logger); - return chainSpec; - } + if (_logger.IsDebug) _logger.Debug($"Loading chain spec from {chainSpecFile}"); - private void SetLoggerVariables(ChainSpec chainSpec) - { - _logManager.SetGlobalVariable("chain", chainSpec.Name); - _logManager.SetGlobalVariable("chainId", chainSpec.ChainId); - _logManager.SetGlobalVariable("engine", chainSpec.SealEngineType); - } + ThisNodeInfo.AddInfo("Chainspec :", $"{chainSpecFile}"); + + IChainSpecLoader loader = new ChainSpecLoader(ethereumJsonSerializer); + ChainSpec chainSpec = loader.LoadEmbeddedOrFromFile(chainSpecFile, _logger); + return chainSpec; + } + + private void SetLoggerVariables(ChainSpec chainSpec) + { + _logManager.SetGlobalVariable("chain", chainSpec.Name); + _logManager.SetGlobalVariable("chainId", chainSpec.ChainId); + _logManager.SetGlobalVariable("engine", chainSpec.SealEngineType); } } diff --git a/src/Nethermind/Nethermind.Runner/Ethereum/EthereumRunner.cs b/src/Nethermind/Nethermind.Runner/Ethereum/EthereumRunner.cs index 5e79b03e1cb..7e896261fee 100644 --- a/src/Nethermind/Nethermind.Runner/Ethereum/EthereumRunner.cs +++ b/src/Nethermind/Nethermind.Runner/Ethereum/EthereumRunner.cs @@ -13,102 +13,100 @@ using Nethermind.Init.Steps; using Nethermind.Logging; -namespace Nethermind.Runner.Ethereum +namespace Nethermind.Runner.Ethereum; + +public class EthereumRunner(INethermindApi api) { - public class EthereumRunner + private readonly INethermindApi _api = api; + private readonly ILogger _logger = api.LogManager.GetClassLogger(); + + public async Task Start(CancellationToken cancellationToken) + { + if (_logger.IsDebug) _logger.Debug("Starting Ethereum runner"); + + EthereumStepsLoader stepsLoader = new(GetStepsAssemblies(_api)); + EthereumStepsManager stepsManager = new(stepsLoader, _api, _api.LogManager); + + await stepsManager.InitializeAll(cancellationToken); + + string infoScreen = ThisNodeInfo.BuildNodeInfoScreen(); + + if (_logger.IsInfo) _logger.Info(infoScreen); + } + + private IEnumerable GetStepsAssemblies(INethermindApi api) { - private readonly INethermindApi _api; + yield return typeof(IStep).Assembly; + yield return GetType().Assembly; - private readonly ILogger _logger; + IEnumerable enabledInitializationPlugins = + _api.Plugins.OfType().Where(p => p.ShouldRunSteps(api)); - public EthereumRunner(INethermindApi api) + foreach (IInitializationPlugin initializationPlugin in enabledInitializationPlugins) { - _api = api; - _logger = api.LogManager.GetClassLogger(); + yield return initializationPlugin.GetType().Assembly; } + } - public async Task Start(CancellationToken cancellationToken) + public async Task StopAsync() + { + Stop(() => _api.SessionMonitor?.Stop(), "Stopping session monitor"); + Stop(() => _api.SyncModeSelector?.Stop(), "Stopping session sync mode selector"); + Task discoveryStopTask = Stop(() => _api.DiscoveryApp?.StopAsync(), "Stopping discovery app"); + Task blockProducerTask = Stop(() => _api.BlockProducerRunner?.StopAsync(), "Stopping block producer"); + Task syncPeerPoolTask = Stop(() => _api.SyncPeerPool?.StopAsync(), "Stopping sync peer pool"); + Task peerPoolTask = Stop(() => _api.PeerPool?.StopAsync(), "Stopping peer pool"); + Task peerManagerTask = Stop(() => _api.PeerManager?.StopAsync(), "Stopping peer manager"); + Task synchronizerTask = Stop(() => _api.Synchronizer?.StopAsync(), "Stopping synchronizer"); + Task blockchainProcessorTask = Stop(() => _api.BlockchainProcessor?.StopAsync(), "Stopping blockchain processor"); + Task rlpxPeerTask = Stop(() => _api.RlpxPeer?.Shutdown(), "Stopping RLPx peer"); + await Task.WhenAll(discoveryStopTask, rlpxPeerTask, peerManagerTask, synchronizerTask, syncPeerPoolTask, peerPoolTask, blockchainProcessorTask, blockProducerTask); + + foreach (INethermindPlugin plugin in _api.Plugins) { - if (_logger.IsDebug) _logger.Debug("Initializing Ethereum"); - - EthereumStepsLoader stepsLoader = new EthereumStepsLoader(GetStepsAssemblies(_api)); - EthereumStepsManager stepsManager = new EthereumStepsManager(stepsLoader, _api, _api.LogManager); - await stepsManager.InitializeAll(cancellationToken); - - string infoScreen = ThisNodeInfo.BuildNodeInfoScreen(); - if (_logger.IsInfo) _logger.Info(infoScreen); + await Stop(async () => await plugin.DisposeAsync(), $"Disposing plugin {plugin.Name}"); } - private IEnumerable GetStepsAssemblies(INethermindApi api) + while (_api.DisposeStack.Count != 0) { - yield return typeof(IStep).Assembly; - yield return GetType().Assembly; - IEnumerable enabledInitializationPlugins = - _api.Plugins.OfType().Where(p => p.ShouldRunSteps(api)); - - foreach (IInitializationPlugin initializationPlugin in enabledInitializationPlugins) - { - yield return initializationPlugin.GetType().Assembly; - } + IAsyncDisposable disposable = _api.DisposeStack.Pop(); + await Stop(async () => await disposable.DisposeAsync(), $"Disposing {disposable}"); } - public async Task StopAsync() + Stop(() => _api.DbProvider?.Dispose(), "Closing DBs"); + + if (_logger.IsInfo) { - Stop(() => _api.SessionMonitor?.Stop(), "Stopping session monitor"); - Stop(() => _api.SyncModeSelector?.Stop(), "Stopping session sync mode selector"); - Task discoveryStopTask = Stop(() => _api.DiscoveryApp?.StopAsync(), "Stopping discovery app"); - Task blockProducerTask = Stop(() => _api.BlockProducerRunner?.StopAsync(), "Stopping block producer"); - Task syncPeerPoolTask = Stop(() => _api.SyncPeerPool?.StopAsync(), "Stopping sync peer pool"); - Task peerPoolTask = Stop(() => _api.PeerPool?.StopAsync(), "Stopping peer pool"); - Task peerManagerTask = Stop(() => _api.PeerManager?.StopAsync(), "Stopping peer manager"); - Task synchronizerTask = Stop(() => _api.Synchronizer?.StopAsync(), "Stopping synchronizer"); - Task blockchainProcessorTask = Stop(() => _api.BlockchainProcessor?.StopAsync(), "Stopping blockchain processor"); - Task rlpxPeerTask = Stop(() => _api.RlpxPeer?.Shutdown(), "Stopping rlpx peer"); - await Task.WhenAll(discoveryStopTask, rlpxPeerTask, peerManagerTask, synchronizerTask, syncPeerPoolTask, peerPoolTask, blockchainProcessorTask, blockProducerTask); - - foreach (INethermindPlugin plugin in _api.Plugins) - { - await Stop(async () => await plugin.DisposeAsync(), $"Disposing plugin {plugin.Name}"); - } - - while (_api.DisposeStack.Count != 0) - { - IAsyncDisposable disposable = _api.DisposeStack.Pop(); - await Stop(async () => await disposable.DisposeAsync(), $"Disposing {disposable}"); - } - - Stop(() => _api.DbProvider?.Dispose(), "Closing DBs"); - - if (_logger.IsInfo) _logger.Info("All DBs closed."); - - if (_logger.IsInfo) _logger.Info("Ethereum shutdown complete... please wait for all components to close"); + _logger.Info("All DBs closed"); + _logger.Info("Ethereum runner stopped"); } + } - private void Stop(Action stopAction, string description) + private void Stop(Action stopAction, string description) + { + try { - try - { - if (_logger.IsInfo) _logger.Info($"{description}..."); - stopAction(); - } - catch (Exception e) - { - if (_logger.IsError) _logger.Error($"{description} shutdown error.", e); - } + if (_logger.IsInfo) _logger.Info(description); + + stopAction(); } + catch (Exception e) + { + if (_logger.IsError) _logger.Error($"{description} shutdown error.", e); + } + } - private Task Stop(Func stopAction, string description) + private Task Stop(Func stopAction, string description) + { + try + { + if (_logger.IsInfo) _logger.Info(description); + return stopAction() ?? Task.CompletedTask; + } + catch (Exception e) { - try - { - if (_logger.IsInfo) _logger.Info($"{description}..."); - return stopAction() ?? Task.CompletedTask; - } - catch (Exception e) - { - if (_logger.IsError) _logger.Error($"{description} shutdown error.", e); - return Task.CompletedTask; - } + if (_logger.IsError) _logger.Error($"{description} shutdown error.", e); + return Task.CompletedTask; } } } diff --git a/src/Nethermind/Nethermind.Runner/Logging/NLogConfigurator.cs b/src/Nethermind/Nethermind.Runner/Logging/NLogConfigurator.cs index 400118352e6..7a2ceca06a5 100644 --- a/src/Nethermind/Nethermind.Runner/Logging/NLogConfigurator.cs +++ b/src/Nethermind/Nethermind.Runner/Logging/NLogConfigurator.cs @@ -3,95 +3,92 @@ using System; using System.Linq; -using McMaster.Extensions.CommandLineUtils; using Nethermind.Core.Collections; using NLog; using NLog.Config; using NLog.Targets; using NLog.Targets.Seq; -namespace Nethermind.Runner.Logging +namespace Nethermind.Runner.Logging; + +public static class NLogConfigurator { - public static class NLogConfigurator + public static void ConfigureSeqBufferTarget( + string url = "http://localhost:5341", + string apiKey = "", + string minLevel = "Off") { - public static void ConfigureSeqBufferTarget( - string url = "http://localhost:5341", - string apiKey = "", - string minLevel = "Off") + LoggingConfiguration loggingConfiguration = LogManager.Configuration; + if (loggingConfiguration is not null) { - LoggingConfiguration loggingConfiguration = LogManager.Configuration; - if (loggingConfiguration is not null) + if (loggingConfiguration.AllTargets is not null) { - if (loggingConfiguration.AllTargets is not null) + foreach (SeqTarget target in loggingConfiguration.AllTargets.OfType()) { - foreach (SeqTarget target in loggingConfiguration.AllTargets.OfType()) + target.ApiKey = apiKey; + target.ServerUrl = url; + foreach (LoggingRule? rule in loggingConfiguration.LoggingRules) { - target.ApiKey = apiKey; - target.ServerUrl = url; - foreach (LoggingRule? rule in loggingConfiguration.LoggingRules) + foreach (Target? ruleTarget in rule.Targets) { - foreach (Target? ruleTarget in rule.Targets) + if (ruleTarget.Name == "seq" && rule.LoggerNamePattern == "*") { - if (ruleTarget.Name == "seq" && rule.LoggerNamePattern == "*") - { - rule.EnableLoggingForLevels(LogLevel.FromString(minLevel), LogLevel.Fatal); - } + rule.EnableLoggingForLevels(LogLevel.FromString(minLevel), LogLevel.Fatal); } } } } - - // // // re-initialize single target - loggingConfiguration.AllTargets?.OfType().ForEach(t => t.Dispose()); - LogManager.ReconfigExistingLoggers(); } - } - public static void ClearSeqTarget() - { - LoggingConfiguration loggingConfiguration = LogManager.Configuration; - loggingConfiguration?.RemoveTarget("seq"); + // // // re-initialize single target + loggingConfiguration.AllTargets?.OfType().ForEach(t => t.Dispose()); + LogManager.ReconfigExistingLoggers(); } + } - public static void ConfigureLogLevels(CommandOption logLevelOverride) + public static void ClearSeqTarget() + { + LoggingConfiguration loggingConfiguration = LogManager.Configuration; + loggingConfiguration?.RemoveTarget("seq"); + } + + public static void ConfigureLogLevels(string logLevel) + { + LogLevel nLogLevel = logLevel.ToUpperInvariant() switch { - string logLevel = logLevelOverride.Value(); - LogLevel nLogLevel = logLevel.ToUpperInvariant() switch - { - "OFF" => LogLevel.Off, - "ERROR" => LogLevel.Error, - "WARN" => LogLevel.Warn, - "INFO" => LogLevel.Info, - "DEBUG" => LogLevel.Debug, - "TRACE" => LogLevel.Trace, - _ => LogLevel.Info - }; + "OFF" => LogLevel.Off, + "ERROR" => LogLevel.Error, + "WARN" => LogLevel.Warn, + "INFO" => LogLevel.Info, + "DEBUG" => LogLevel.Debug, + "TRACE" => LogLevel.Trace, + _ => LogLevel.Info + }; - Console.WriteLine($"Enabling log level override: {logLevel.ToUpperInvariant()}"); + //Console.WriteLine($"Enabling log level override: {logLevel.ToUpperInvariant()}"); - // There are some rules for which we don't want to override the log level - // but instead preserve the original config defined in the 'NLog.config' file - string[] ignoredRuleNames = - { - "JsonWebAPI*", - "JsonWebAPI.Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService", - }; - foreach (LoggingRule rule in LogManager.Configuration.LoggingRules) - { - if (ignoredRuleNames.Contains(rule.LoggerNamePattern)) { continue; } + // There are some rules for which we don't want to override the log level + // but instead preserve the original config defined in the 'NLog.config' file + string[] ignoredRuleNames = + { + "JsonWebAPI*", + "JsonWebAPI.Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService", + }; + foreach (LoggingRule rule in LogManager.Configuration.LoggingRules) + { + if (ignoredRuleNames.Contains(rule.LoggerNamePattern)) { continue; } - foreach (var ruleTarget in rule.Targets) + foreach (var ruleTarget in rule.Targets) + { + if (ruleTarget.Name != "seq") { - if (ruleTarget.Name != "seq") - { - Console.WriteLine($"{ruleTarget.Name} TEST"); - rule.DisableLoggingForLevels(LogLevel.Trace, nLogLevel); - rule.EnableLoggingForLevels(nLogLevel, LogLevel.Off); - } + //Console.WriteLine($"{ruleTarget.Name} TEST"); + rule.DisableLoggingForLevels(LogLevel.Trace, nLogLevel); + rule.EnableLoggingForLevels(nLogLevel, LogLevel.Off); } } - - LogManager.ReconfigExistingLoggers(); } + + LogManager.ReconfigExistingLoggers(); } } diff --git a/src/Nethermind/Nethermind.Runner/NLog.config b/src/Nethermind/Nethermind.Runner/NLog.config index b9e3d620738..11993b49c13 100644 --- a/src/Nethermind/Nethermind.Runner/NLog.config +++ b/src/Nethermind/Nethermind.Runner/NLog.config @@ -53,10 +53,10 @@ - + - + diff --git a/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj b/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj index d439a869bd9..2cc6fa3b095 100644 --- a/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj +++ b/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj @@ -25,10 +25,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + @@ -72,7 +72,7 @@ PreserveNewest - + PreserveNewest PreserveNewest true @@ -83,7 +83,6 @@ true - Always diff --git a/src/Nethermind/Nethermind.Runner/Program.cs b/src/Nethermind/Nethermind.Runner/Program.cs index ed04e3ef6bf..ffc60b4971d 100644 --- a/src/Nethermind/Nethermind.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Runner/Program.cs @@ -1,23 +1,23 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; using System.Collections.Generic; -using System.Diagnostics; +using System.CommandLine; +using System.CommandLine.Help; +using System.CommandLine.Invocation; +using System.CommandLine.Parsing; using System.IO; using System.IO.Abstractions; using System.Linq; using System.Reflection; -using System.Runtime.InteropServices; -using System.Runtime.Loader; -using System.Text; +using System.Runtime; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; #if !DEBUG using DotNetty.Common; #endif -using McMaster.Extensions.CommandLineUtils; using Nethermind.Api; using Nethermind.Api.Extensions; using Nethermind.Config; @@ -32,6 +32,7 @@ using Nethermind.KeyStore.Config; using Nethermind.Logging; using Nethermind.Logging.NLog; +using Nethermind.Runner; using Nethermind.Runner.Ethereum; using Nethermind.Runner.Ethereum.Api; using Nethermind.Runner.Logging; @@ -41,557 +42,502 @@ using NLog; using NLog.Config; using ILogger = Nethermind.Logging.ILogger; +using NullLogger = Nethermind.Logging.NullLogger; -namespace Nethermind.Runner; +Console.Title = ProductInfo.Name; +// Increase regex cache size as more added in log coloring matches +Regex.CacheSize = 128; +#if !DEBUG +ResourceLeakDetector.Level = ResourceLeakDetector.DetectionLevel.Disabled; +#endif -public static partial class Program +ManualResetEventSlim exit = new(true); +ILogger logger = new(SimpleConsoleLogger.Instance); +ProcessExitSource? processExitSource = default; +var unhandledError = "A critical error has occurred"; + +AppDomain.CurrentDomain.UnhandledException += (sender, e) => { - private const string FailureString = "Failure"; - private const string DefaultConfigsDirectory = "configs"; - private const string DefaultConfigFile = "configs/mainnet.cfg"; + ILogger criticalLogger = GetCriticalLogger(); - private static ILogger _logger = new(SimpleConsoleLogger.Instance); + if (e.ExceptionObject is Exception ex) + criticalLogger.Error($"{unhandledError}.", ex); + else + criticalLogger.Error($"{unhandledError}: {e.ExceptionObject}"); +}; - private static readonly ProcessExitSource _processExitSource = new(); - private static readonly ManualResetEventSlim _appClosed = new(true); +try +{ + return await ConfigureAsync(args); +} +catch (Exception ex) +{ + ILogger criticalLogger = GetCriticalLogger(); - public static void Main(string[] args) - { - // Increase regex cache size as more added in log coloring matches - Regex.CacheSize = 128; -#if !DEBUG - ResourceLeakDetector.Level = ResourceLeakDetector.DetectionLevel.Disabled; -#endif - AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) => - { - ILogger logger = GetCriticalLogger(); - if (eventArgs.ExceptionObject is Exception e) - { - logger.Error(FailureString, e); - } - else - { - logger.Error(FailureString + eventArgs.ExceptionObject); - } - }; + ex = ex is AggregateException aex ? aex.InnerException : ex; - try - { - Run(args); - } - catch (AggregateException e) - { - ILogger logger = GetCriticalLogger(); - logger.Error(FailureString, e.InnerException); - } - catch (Exception e) - { - ILogger logger = GetCriticalLogger(); - logger.Error(FailureString, e); - } - finally - { - NLogManager.Shutdown(); - } - } + criticalLogger.Error($"{unhandledError}.", ex); + + return ex is IExceptionWithExitCode exc ? exc.ExitCode : ExitCodes.GeneralError; +} +finally +{ + NLogManager.Shutdown(); +} - private static ILogger GetCriticalLogger() +async Task ConfigureAsync(string[] args) +{ + CliConfiguration cli = ConfigureCli(); + ParseResult parseResult = cli.Parse(args); + // Suppress logs if run with `--help` or `--version` + bool silent = parseResult.CommandResult.Children + .Any(c => c is OptionResult { Option: HelpOption or VersionOption }); + + ConsoleHelpers.EnableConsoleColorOutput(); + + ConfigureLogger(parseResult); + + if (!silent) { - try - { - return new NLogManager("logs.txt").GetClassLogger(); - } - catch - { - if (_logger.IsWarn) _logger.Warn("Critical file logging could not be instantiated! Sticking to console logging till config is loaded."); - return _logger; - } + logger.Info("Nethermind is starting up"); + logger.Info($"Version: {ProductInfo.Version}"); } - private static void Run(string[] args) + AppDomain.CurrentDomain.ProcessExit += (_, _) => { - _logger.Info("Nethermind starting initialization."); - _logger.Info($"Client version: {ProductInfo.ClientId}"); - - AppDomain.CurrentDomain.ProcessExit += CurrentDomainOnProcessExit; - AssemblyLoadContext.Default.ResolvingUnmanagedDll += OnResolvingUnmanagedDll; - - GlobalDiagnosticsContext.Set("version", ProductInfo.Version); - CommandLineApplication app = new() { Name = "Nethermind.Runner" }; - _ = app.HelpOption("-?|-h|--help"); - _ = app.VersionOption("-v|--version", () => ProductInfo.Version, GetProductInfo); - - ConsoleHelpers.EnableConsoleColorOutput(); - - CommandOption dataDir = app.Option("-dd|--datadir ", "Data directory", CommandOptionType.SingleValue); - CommandOption configFile = app.Option("-c|--config ", "Config file path", CommandOptionType.SingleValue); - CommandOption dbBasePath = app.Option("-d|--baseDbPath ", "Base db path", CommandOptionType.SingleValue); - CommandOption logLevelOverride = app.Option("-l|--log ", "Log level override. Possible values: OFF|TRACE|DEBUG|INFO|WARN|ERROR", CommandOptionType.SingleValue); - CommandOption configsDirectory = app.Option("-cd|--configsDirectory ", "Configs directory", CommandOptionType.SingleValue); - CommandOption loggerConfigSource = app.Option("-lcs|--loggerConfigSource ", "Path to the NLog config file", CommandOptionType.SingleValue); - _ = app.Option("-pd|--pluginsDirectory ", "plugins directory", CommandOptionType.SingleValue); - - IFileSystem fileSystem = new FileSystem(); - - string pluginsDirectoryPath = LoadPluginsDirectory(args); - PluginLoader pluginLoader = new(pluginsDirectoryPath, fileSystem, - typeof(AuRaPlugin), - typeof(CliquePlugin), - typeof(EthashPlugin), - typeof(NethDevPlugin), - typeof(HivePlugin), - typeof(UPnPPlugin) - ); - - // leaving here as an example of adding Debug plugin - // IPluginLoader mevLoader = SinglePluginLoader.Instance; - // CompositePluginLoader pluginLoader = new (pluginLoader, mevLoader); - pluginLoader.Load(SimpleConsoleLogManager.Instance); - TypeDiscovery.Initialize(typeof(INethermindPlugin)); - - BuildOptionsFromConfigFiles(app); - - app.OnExecute(async () => - { - IConfigProvider configProvider = BuildConfigProvider(app, loggerConfigSource, logLevelOverride, configsDirectory, configFile); - IInitConfig initConfig = configProvider.GetConfig(); - IKeyStoreConfig keyStoreConfig = configProvider.GetConfig(); - ISnapshotConfig snapshotConfig = configProvider.GetConfig(); - IPluginConfig pluginConfig = configProvider.GetConfig(); + processExitSource?.Exit(ExitCodes.SigTerm); + exit.Wait(); + }; + GlobalDiagnosticsContext.Set("version", ProductInfo.Version); + + PluginLoader pluginLoader = new( + parseResult.GetValue(BasicOptions.PluginsDirectory) ?? "plugins", + new FileSystem(), + silent ? NullLogger.Instance : logger, + typeof(AuRaPlugin), + typeof(CliquePlugin), + typeof(EthashPlugin), + typeof(NethDevPlugin), + typeof(HivePlugin), + typeof(UPnPPlugin) + ); + pluginLoader.Load(); + + // leaving here as an example of adding Debug plugin + // IPluginLoader mevLoader = SinglePluginLoader.Instance; + // CompositePluginLoader pluginLoader = new (pluginLoader, mevLoader); + + TypeDiscovery.Initialize(typeof(INethermindPlugin)); + + AddConfigurationOptions(cli.RootCommand); + + cli.RootCommand.SetAction((result, token) => RunAsync(result, pluginLoader, token)); + + try + { + return await cli.InvokeAsync(args); + } + finally + { + exit.Wait(); + } +} - pluginLoader.OrderPlugins(pluginConfig); - Console.Title = initConfig.LogFileName; - Console.CancelKeyPress += ConsoleOnCancelKeyPress; +async Task RunAsync(ParseResult parseResult, PluginLoader pluginLoader, CancellationToken cancellationToken) +{ + processExitSource = new(cancellationToken); - SetFinalDataDirectory(dataDir.HasValue() ? dataDir.Value() : null, initConfig, keyStoreConfig, snapshotConfig); - NLogManager logManager = new(initConfig.LogFileName, initConfig.LogDirectory, initConfig.LogRules); + IConfigProvider configProvider = CreateConfigProvider(parseResult); + IInitConfig initConfig = configProvider.GetConfig(); + IKeyStoreConfig keyStoreConfig = configProvider.GetConfig(); + ISnapshotConfig snapshotConfig = configProvider.GetConfig(); + IPluginConfig pluginConfig = configProvider.GetConfig(); - _logger = logManager.GetClassLogger(); - ConfigureSeqLogger(configProvider); - SetFinalDbPath(dbBasePath.HasValue() ? dbBasePath.Value() : null, initConfig); - LogMemoryConfiguration(); + pluginLoader.OrderPlugins(pluginConfig); - EthereumJsonSerializer serializer = new(); - if (_logger.IsDebug) _logger.Debug($"Nethermind config:{Environment.NewLine}{serializer.Serialize(initConfig, true)}{Environment.NewLine}"); - if (_logger.IsInfo) _logger.Info($"RocksDb Version: {DbOnTheRocks.GetRocksDbVersion()}"); + ResolveDataDirectory(parseResult.GetValue(BasicOptions.DataDirectory), + initConfig, keyStoreConfig, snapshotConfig); - ApiBuilder apiBuilder = new(configProvider, logManager); + NLogManager logManager = new(initConfig.LogFileName, initConfig.LogDirectory, initConfig.LogRules); - IList plugins = new List(); - foreach (Type pluginType in pluginLoader.PluginTypes) - { - try - { - if (Activator.CreateInstance(pluginType) is INethermindPlugin plugin) - { - plugins.Add(plugin); - } - } - catch (Exception e) - { - if (_logger.IsError) _logger.Error($"Failed to create plugin {pluginType.FullName}", e); - } - } + logger = logManager.GetClassLogger(); - INethermindApi nethermindApi = apiBuilder.Create(plugins.OfType()); - ((List)nethermindApi.Plugins).AddRange(plugins); - nethermindApi.ProcessExit = _processExitSource; + ConfigureSeqLogger(configProvider); + ResolveDatabaseDirectory(parseResult.GetValue(BasicOptions.DatabasePath), initConfig); - _appClosed.Reset(); - EthereumRunner ethereumRunner = new(nethermindApi); - try - { - await ethereumRunner.Start(_processExitSource.Token); + logger.Info("Configuration complete"); - await _processExitSource.ExitTask; - } - catch (TaskCanceledException) - { - if (_logger.IsTrace) _logger.Trace("Runner Task was canceled"); - } - catch (OperationCanceledException) - { - if (_logger.IsTrace) _logger.Trace("Runner operation was canceled"); - } - catch (Exception e) - { - if (_logger.IsError) _logger.Error("Error during ethereum runner start", e); - _processExitSource.Exit(e is IExceptionWithExitCode withExit ? withExit.ExitCode : ExitCodes.GeneralError); - } + EthereumJsonSerializer serializer = new(); - _logger.Info("Closing, please wait until all functions are stopped properly..."); - await ethereumRunner.StopAsync(); - _logger.Info("All done, goodbye!"); - _appClosed.Set(); - }); + if (logger.IsDebug) + { + logger.Debug($"Nethermind configuration:\n{serializer.Serialize(initConfig, true)}"); - try - { - Environment.ExitCode = app.Execute(args); - } - catch (UnrecognizedCommandParsingException e) - { - string[] matches = e.NearestMatches.Take(3).ToArray(); - string suggestion = matches.Length switch - { - 0 => "", - 1 => $" Did you mean {matches[0]}", - _ => $" Did you mean one of: {string.Join(", ", matches)}" - }; - _logger.Error($"{e.Message}.{suggestion}"); - Environment.ExitCode = ExitCodes.UnrecognizedOption; - } - catch (CommandParsingException e) - { - Regex regex = GetUnexpectedConfigValueRegex(); - Match match = regex.Match(e.Message); - if (match.Success) - { - string option = match.Groups["Name"].Value; - CommandOption? optionInfo = app.GetOptions().FirstOrDefault(o => o.ShortName == option || o.LongName == option); - switch (optionInfo?.OptionType) - { - case CommandOptionType.SingleValue or CommandOptionType.SingleOrNoValue: - _logger.Error($"Duplicated option '{option}'"); - Environment.ExitCode = ExitCodes.DuplicatedOption; - return; - case CommandOptionType.NoValue: - _logger.Error($"Value {match.Groups["Value"].Value} passed for value-less option '{option}'"); - Environment.ExitCode = ExitCodes.ForbiddenOptionValue; - return; - } - } + logger.Debug($"Server GC: {GCSettings.IsServerGC}"); + logger.Debug($"GC latency mode: {GCSettings.LatencyMode}"); + logger.Debug($"LOH compaction mode: {GCSettings.LargeObjectHeapCompactionMode}"); + } - _logger.Error($"{e.Message}"); - } - catch (Exception e) + if (logger.IsInfo) logger.Info($"RocksDB: v{DbOnTheRocks.GetRocksDbVersion()}"); + + ApiBuilder apiBuilder = new(configProvider, logManager); + IList plugins = []; + + foreach (Type pluginType in pluginLoader.PluginTypes) + { + try { - if (e is IExceptionWithExitCode withExit) - { - Environment.ExitCode = withExit.ExitCode; - } - else - { - Environment.ExitCode = ExitCodes.GeneralError; - } - throw; + if (Activator.CreateInstance(pluginType) is INethermindPlugin plugin) + plugins.Add(plugin); } - finally + catch (Exception ex) { - _appClosed.Wait(); + if (logger.IsError) logger.Error($"Failed to create plugin {pluginType.FullName}", ex); } } - [GeneratedRegex("^Unexpected value '(?.+)' for option '(?.+)'", RegexOptions.Singleline)] - private static partial Regex GetUnexpectedConfigValueRegex(); + INethermindApi nethermindApi = apiBuilder.Create(plugins.OfType()); + ((List)nethermindApi.Plugins).AddRange(plugins); + nethermindApi.ProcessExit = processExitSource; - private static IntPtr OnResolvingUnmanagedDll(Assembly _, string nativeLibraryName) + EthereumRunner ethereumRunner = new(nethermindApi); + try { - var alternativePath = nativeLibraryName switch - { - "libdl" => "libdl.so.2", - _ => null - }; + await ethereumRunner.Start(processExitSource.Token); + await processExitSource.ExitTask; + } + catch (OperationCanceledException) + { + if (logger.IsTrace) logger.Trace("Nethermind operation was canceled."); + } + catch (Exception ex) + { + if (logger.IsError) logger.Error(unhandledError, ex); - return alternativePath is null ? IntPtr.Zero : NativeLibrary.Load(alternativePath); + processExitSource.Exit(ex is IExceptionWithExitCode withExit ? withExit.ExitCode : ExitCodes.GeneralError); } - private static void BuildOptionsFromConfigFiles(CommandLineApplication app) + logger.Info("Nethermind is shutting down... Please wait until all activities are stopped."); + + await ethereumRunner.StopAsync(); + + logger.Info("Nethermind is shut down"); + + exit.Set(); + + return processExitSource.ExitCode; +} + +void AddConfigurationOptions(CliCommand command) +{ + IEnumerable configTypes = TypeDiscovery + .FindNethermindBasedTypes(typeof(IConfig)) + .Where(ct => ct.IsInterface); + + foreach (Type configType in + configTypes.Where(ct => !ct.IsAssignableTo(typeof(INoCategoryConfig))).OrderBy(c => c.Name)) { - Type configurationType = typeof(IConfig); - IEnumerable configTypes = TypeDiscovery.FindNethermindBasedTypes(configurationType) - .Where(ct => ct.IsInterface); + if (configType is null) + continue; - foreach (Type configType in configTypes.Where(ct => !ct.IsAssignableTo(typeof(INoCategoryConfig))).OrderBy(c => c.Name)) - { - if (configType is null) - { - continue; - } + ConfigCategoryAttribute? typeLevel = configType.GetCustomAttribute(); - ConfigCategoryAttribute? typeLevel = configType.GetCustomAttribute(); + if (typeLevel is not null && (typeLevel.DisabledForCli || typeLevel.HiddenFromDocs)) + continue; - if (typeLevel is not null && (typeLevel?.DisabledForCli ?? true)) - { - continue; - } + foreach (PropertyInfo propertyInfo in + configType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name)) + { + ConfigItemAttribute? configItemAttribute = propertyInfo.GetCustomAttribute(); - foreach (PropertyInfo propertyInfo in configType - .GetProperties(BindingFlags.Public | BindingFlags.Instance) - .OrderBy(p => p.Name)) + if (configItemAttribute?.DisabledForCli == false || configItemAttribute?.HiddenFromDocs == false) { - ConfigItemAttribute? configItemAttribute = propertyInfo.GetCustomAttribute(); - if (!(configItemAttribute?.DisabledForCli ?? false)) - { - _ = app.Option($"--{ConfigExtensions.GetCategoryName(configType)}.{propertyInfo.Name}", $"{(configItemAttribute is null ? "" : configItemAttribute.Description + $" (DEFAULT: {configItemAttribute.DefaultValue})" ?? "")}", CommandOptionType.SingleValue); - } - if (configItemAttribute?.IsPortOption == true) + command.Add(new CliOption( + $"--{ConfigExtensions.GetCategoryName(configType)}.{propertyInfo.Name}", + $"--{ConfigExtensions.GetCategoryName(configType)}-{propertyInfo.Name}".ToLowerInvariant()) { - ConfigExtensions.AddPortOptionName(configType, propertyInfo.Name); - } + Description = configItemAttribute?.Description, + HelpName = "value" + }); } + + if (configItemAttribute?.IsPortOption == true) + ConfigExtensions.AddPortOptionName(configType, propertyInfo.Name); } + } +} - // Create Help Text for environment variables - Type noCategoryConfig = configTypes.FirstOrDefault(ct => ct.IsAssignableTo(typeof(INoCategoryConfig))); - if (noCategoryConfig is not null) +CliConfiguration ConfigureCli() +{ + CliRootCommand rootCommand = + [ + BasicOptions.Configuration, + BasicOptions.ConfigurationDirectory, + BasicOptions.DatabasePath, + BasicOptions.DataDirectory, + BasicOptions.LoggerConfigurationSource, + BasicOptions.LogLevel, + BasicOptions.PluginsDirectory + ]; + + var versionOption = (VersionOption)rootCommand.Children.SingleOrDefault(c => c is VersionOption); + + if (versionOption is not null) + { + versionOption.Action = new AnonymousCliAction(r => { - StringBuilder sb = new(); - sb.AppendLine(); - sb.AppendLine("Configurable Environment Variables:"); - foreach (PropertyInfo propertyInfo in noCategoryConfig.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name)) - { - ConfigItemAttribute? configItemAttribute = propertyInfo.GetCustomAttribute(); - if (configItemAttribute is not null && !(string.IsNullOrEmpty(configItemAttribute?.EnvironmentVariable))) - { - sb.AppendLine($"{configItemAttribute.EnvironmentVariable} - {(string.IsNullOrEmpty(configItemAttribute.Description) ? "" : configItemAttribute.Description)} (DEFAULT: {configItemAttribute.DefaultValue})"); - } - } + Console.WriteLine($""" + Version: {ProductInfo.Version} + Commit: {ProductInfo.Commit} + Build date: {ProductInfo.BuildTimestamp:u} + Runtime: {ProductInfo.Runtime} + Platform: {ProductInfo.OS} {ProductInfo.OSArchitecture} + """); - app.ExtendedHelpText = sb.ToString(); - } + return ExitCodes.Ok; + }); } - private static string LoadPluginsDirectory(string[] args) + return new(rootCommand) { - string shortCommand = "-pd"; - string longCommand = "--pluginsDirectory"; + EnableDefaultExceptionHandler = false, + ProcessTerminationTimeout = Timeout.InfiniteTimeSpan + }; +} - string[] GetPluginArgs() - { - for (int i = 0; i < args.Length; i++) - { - string arg = args[i]; - if (arg == shortCommand || arg == longCommand) - { - return i == args.Length - 1 ? new[] { arg } : new[] { arg, args[i + 1] }; - } - } +void ConfigureLogger(ParseResult parseResult) +{ + string nLogConfig = Path.GetFullPath( + parseResult.GetValue(BasicOptions.LoggerConfigurationSource) + ?? "NLog.config".GetApplicationResourcePath()); - return Array.Empty(); - } + try + { + LogManager.Configuration = new XmlLoggingConfiguration(nLogConfig); + } + catch (Exception ex) + { + logger.Error($"Failed to load logging configuration file.", ex); + return; + } - CommandLineApplication pluginsApp = new() { Name = "Nethermind.Runner.Plugins" }; - CommandOption pluginsAppDirectory = pluginsApp.Option($"{shortCommand}|{longCommand} ", "plugins directory", CommandOptionType.SingleValue); - string pluginDirectory = "plugins"; - pluginsApp.OnExecute(() => - { - if (pluginsAppDirectory.HasValue()) - { - pluginDirectory = pluginsAppDirectory.Value(); - } + using NLogManager logManager = new("nethermind.log"); - return 0; - }); - pluginsApp.Execute(GetPluginArgs()); - return pluginDirectory; + logger = logManager.GetClassLogger(); + + string logLevel = parseResult.GetValue(BasicOptions.LogLevel) ?? "info"; + + // TODO: dynamically switch log levels from CLI + if (logLevel is not null) + NLogConfigurator.ConfigureLogLevels(logLevel); +} + +void ConfigureSeqLogger(IConfigProvider configProvider) +{ + ISeqConfig seqConfig = configProvider.GetConfig(); + + if (!seqConfig.MinLevel.Equals("Off", StringComparison.Ordinal)) + { + if (logger.IsInfo) + logger.Info($"Seq logging is enabled on {seqConfig.ServerUrl} with level of {seqConfig.MinLevel}"); + + NLogConfigurator.ConfigureSeqBufferTarget(seqConfig.ServerUrl, seqConfig.ApiKey, seqConfig.MinLevel); } + else + { + // Clear it up; otherwise, internally it will keep requesting localhost as `all` target includes this. + NLogConfigurator.ClearSeqTarget(); + } +} - private static IConfigProvider BuildConfigProvider( - CommandLineApplication app, - CommandOption loggerConfigSource, - CommandOption logLevelOverride, - CommandOption configsDirectory, - CommandOption configFile) +IConfigProvider CreateConfigProvider(ParseResult parseResult) +{ + ConfigProvider configProvider = new(); + Dictionary configArgs = []; + + foreach (SymbolResult child in parseResult.RootCommandResult.Children) { - if (loggerConfigSource.HasValue()) + if (child is OptionResult result) { - string nLogPath = loggerConfigSource.Value(); - _logger.Info($"Loading NLog configuration file from {nLogPath}."); + var value = result.GetValueOrDefault(); - try - { - LogManager.Configuration = new XmlLoggingConfiguration(nLogPath); - } - catch (Exception e) - { - _logger.Info($"Failed to load NLog configuration from {nLogPath}. {e}"); - } + if (value is not null) + configArgs.Add(result.Option.Name.TrimStart('-'), value); } - else - { - _logger.Info($"Loading standard NLog.config file from {"NLog.config".GetApplicationResourcePath()}."); - long startTime = Stopwatch.GetTimestamp(); - LogManager.Configuration = new XmlLoggingConfiguration("NLog.config".GetApplicationResourcePath()); + } - _logger.Info($"NLog.config loaded in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms."); - } + IConfigSource argsSource = new ArgsConfigSource(configArgs); + configProvider.AddSource(argsSource); + configProvider.AddSource(new EnvConfigSource()); - // TODO: dynamically switch log levels from CLI! - if (logLevelOverride.HasValue()) - { - NLogConfigurator.ConfigureLogLevels(logLevelOverride); - } + string configFile = parseResult.GetValue(BasicOptions.Configuration) + ?? Environment.GetEnvironmentVariable("NETHERMIND_CONFIG") + ?? "mainnet"; + + // If configFile is not a path, handle it + if (string.IsNullOrEmpty(Path.GetDirectoryName(configFile))) + { + string configsDir = parseResult.GetValue(BasicOptions.ConfigurationDirectory) + ?? "configs".GetApplicationResourcePath(); + + configFile = Path.Join(configsDir, configFile); - ConfigProvider configProvider = new(); - Dictionary configArgs = new(); - foreach (CommandOption commandOption in app.Options) + // If the configFile doesn't have an extension, try with supported file extensions + if (!Path.HasExtension(configFile)) { - if (commandOption.HasValue()) + string? fallback; + + foreach (var ext in new[] { ".json", ".cfg" }) { - configArgs.Add(commandOption.LongName, commandOption.Value()); + fallback = $"{configFile}{ext}"; + + if (File.Exists(fallback)) + { + configFile = fallback; + break; + } } } + } + else + { + configFile = configFile.GetApplicationResourcePath(); + } - IConfigSource argsSource = new ArgsConfigSource(configArgs); - configProvider.AddSource(argsSource); - configProvider.AddSource(new EnvConfigSource()); + // Resolve the full path for logging purposes + configFile = Path.GetFullPath(configFile); - string configDir = configsDirectory.HasValue() ? configsDirectory.Value() : DefaultConfigsDirectory; - string configFilePath = configFile.HasValue() ? configFile.Value() : DefaultConfigFile; - string? configPathVariable = Environment.GetEnvironmentVariable("NETHERMIND_CONFIG"); - if (!string.IsNullOrWhiteSpace(configPathVariable)) - { - configFilePath = configPathVariable; - } + if (!File.Exists(configFile)) + throw new FileNotFoundException("Configuration file not found.", configFile); - if (!PathUtils.IsExplicitlyRelative(configFilePath)) - { - configFilePath = configDir == DefaultConfigsDirectory - ? configFilePath.GetApplicationResourcePath() - : Path.Combine(configDir, string.Concat(configFilePath)); - } + logger.Info($"Loading configuration from {configFile}"); - if (!Path.HasExtension(configFilePath) && !configFilePath.Contains(Path.DirectorySeparatorChar)) - { - string redirectedConfigPath = Path.Combine(configDir, string.Concat(configFilePath, ".cfg")); - configFilePath = redirectedConfigPath; - if (!File.Exists(configFilePath)) - { - throw new InvalidOperationException($"Configuration: {configFilePath} was not found."); - } - } + configProvider.AddSource(new JsonConfigSource(configFile)); + configProvider.Initialize(); - if (!Path.HasExtension(configFilePath)) - { - configFilePath = string.Concat(configFilePath, ".cfg"); - } + var incorrectSettings = configProvider.FindIncorrectSettings(); - // Fallback to "{executingDirectory}/configs/{configFile}" if "configs" catalog was not specified. - if (!File.Exists(configFilePath)) - { - string configName = Path.GetFileName(configFilePath); - string? configDirectory = Path.GetDirectoryName(configFilePath); - string redirectedConfigPath = Path.Combine(configDirectory ?? string.Empty, configDir, configName); - configFilePath = redirectedConfigPath; - if (!File.Exists(configFilePath)) - { - throw new InvalidOperationException($"Configuration: {configFilePath} was not found."); - } - } + if (incorrectSettings.Errors.Any()) + logger.Warn($"Invalid configuration settings:\n{incorrectSettings.ErrorMsg}"); - _logger.Info($"Reading config file from {configFilePath}"); - configProvider.AddSource(new JsonConfigSource(configFilePath)); - configProvider.Initialize(); - var incorrectSettings = configProvider.FindIncorrectSettings(); - if (incorrectSettings.Errors.Count > 0) - { - _logger.Warn($"Incorrect config settings found:{Environment.NewLine}{incorrectSettings.ErrorMsg}"); - } + return configProvider; +} - _logger.Info("Configuration initialized."); - return configProvider; +ILogger GetCriticalLogger() +{ + try + { + return new NLogManager("nethermind.log").GetClassLogger(); } - - private static void CurrentDomainOnProcessExit(object? sender, EventArgs e) + catch { - _processExitSource.Exit(ExitCodes.SigTerm); - _appClosed.Wait(); + if (logger.IsWarn) logger.Warn("File logging failed. Using console logging."); + + return logger; } +} - private static void LogMemoryConfiguration() +void ResolveDatabaseDirectory(string? path, IInitConfig initConfig) +{ + if (string.IsNullOrWhiteSpace(path)) { - if (_logger.IsDebug) - _logger.Debug($"Server GC : {System.Runtime.GCSettings.IsServerGC}"); - if (_logger.IsDebug) - _logger.Debug($"GC latency mode : {System.Runtime.GCSettings.LatencyMode}"); - if (_logger.IsDebug) - _logger.Debug($"LOH compaction mode : {System.Runtime.GCSettings.LargeObjectHeapCompactionMode}"); + initConfig.BaseDbPath ??= string.Empty.GetApplicationResourcePath("db"); } + else + { + string dbPath = initConfig.BaseDbPath.GetApplicationResourcePath(path); - private static void SetFinalDbPath(string? baseDbPath, IInitConfig initConfig) + if (logger.IsDebug) logger.Debug($"{nameof(initConfig.BaseDbPath)}: {Path.GetFullPath(dbPath)}"); + + initConfig.BaseDbPath = dbPath; + } +} + +void ResolveDataDirectory(string? path, IInitConfig initConfig, IKeyStoreConfig keyStoreConfig, ISnapshotConfig snapshotConfig) +{ + if (string.IsNullOrWhiteSpace(path)) { - if (!string.IsNullOrWhiteSpace(baseDbPath)) - { - string newDbPath = initConfig.BaseDbPath.GetApplicationResourcePath(baseDbPath); - if (_logger.IsDebug) _logger.Debug($"Adding prefix to baseDbPath, new value: {newDbPath}, old value: {initConfig.BaseDbPath}"); - initConfig.BaseDbPath = newDbPath; - } - else + initConfig.BaseDbPath ??= string.Empty.GetApplicationResourcePath("db"); + keyStoreConfig.KeyStoreDirectory ??= string.Empty.GetApplicationResourcePath("keystore"); + initConfig.LogDirectory ??= string.Empty.GetApplicationResourcePath("logs"); + } + else + { + string newDbPath = initConfig.BaseDbPath.GetApplicationResourcePath(path); + string newKeyStorePath = keyStoreConfig.KeyStoreDirectory.GetApplicationResourcePath(path); + string newLogDirectory = initConfig.LogDirectory.GetApplicationResourcePath(path); + string newSnapshotPath = snapshotConfig.SnapshotDirectory.GetApplicationResourcePath(path); + + if (logger.IsInfo) { - initConfig.BaseDbPath ??= string.Empty.GetApplicationResourcePath("db"); + logger.Info($"{nameof(initConfig.BaseDbPath)}: {Path.GetFullPath(newDbPath)}"); + logger.Info($"{nameof(keyStoreConfig.KeyStoreDirectory)}: {Path.GetFullPath(newKeyStorePath)}"); + logger.Info($"{nameof(initConfig.LogDirectory)}: {Path.GetFullPath(newLogDirectory)}"); + + if (snapshotConfig.Enabled) + logger.Info($"{nameof(snapshotConfig.SnapshotDirectory)}: {Path.GetFullPath(newSnapshotPath)}"); } + + initConfig.BaseDbPath = newDbPath; + keyStoreConfig.KeyStoreDirectory = newKeyStorePath; + initConfig.LogDirectory = newLogDirectory; + snapshotConfig.SnapshotDirectory = newSnapshotPath; } +} - private static void SetFinalDataDirectory(string? dataDir, IInitConfig initConfig, IKeyStoreConfig keyStoreConfig, ISnapshotConfig snapshotConfig) - { - if (!string.IsNullOrWhiteSpace(dataDir)) +static class BasicOptions +{ + public static CliOption Configuration { get; } = + new("--config", "-c") { - string newDbPath = initConfig.BaseDbPath.GetApplicationResourcePath(dataDir); - string newKeyStorePath = keyStoreConfig.KeyStoreDirectory.GetApplicationResourcePath(dataDir); - string newLogDirectory = initConfig.LogDirectory.GetApplicationResourcePath(dataDir); - string newSnapshotPath = snapshotConfig.SnapshotDirectory.GetApplicationResourcePath(dataDir); - - if (_logger.IsInfo) - { - _logger.Info($"Setting BaseDbPath to: {newDbPath}, from: {initConfig.BaseDbPath}"); - _logger.Info($"Setting KeyStoreDirectory to: {newKeyStorePath}, from: {keyStoreConfig.KeyStoreDirectory}"); - _logger.Info($"Setting LogDirectory to: {newLogDirectory}, from: {initConfig.LogDirectory}"); - if (snapshotConfig.Enabled) - { - _logger.Info($"Setting SnapshotPath to: {newSnapshotPath}"); - } - } + Description = "The path to the configuration file or the file name (also without extension) of any of the configuration files in the configuration files directory.", + HelpName = "network or file name" + }; - initConfig.BaseDbPath = newDbPath; - keyStoreConfig.KeyStoreDirectory = newKeyStorePath; - initConfig.LogDirectory = newLogDirectory; - snapshotConfig.SnapshotDirectory = newSnapshotPath; - } - else + public static CliOption ConfigurationDirectory { get; } = + new("--configs-dir", "--configsDirectory", "-cd") { - initConfig.BaseDbPath ??= string.Empty.GetApplicationResourcePath("db"); - keyStoreConfig.KeyStoreDirectory ??= string.Empty.GetApplicationResourcePath("keystore"); - initConfig.LogDirectory ??= string.Empty.GetApplicationResourcePath("logs"); - } - } + Description = "The path to the configuration files directory.", + HelpName = "path" + }; - private static void ConsoleOnCancelKeyPress(object? sender, ConsoleCancelEventArgs e) + public static CliOption DatabasePath { get; } = new("--db-dir", "--baseDbPath", "-d") { - _processExitSource.Exit(ExitCodes.SigInt); - e.Cancel = true; - } + Description = "The path to the Nethermind database directory.", + HelpName = "path" + }; - private static void ConfigureSeqLogger(IConfigProvider configProvider) + public static CliOption DataDirectory { get; } = new("--data-dir", "--datadir", "-dd") { - ISeqConfig seqConfig = configProvider.GetConfig(); - if (seqConfig.MinLevel != "Off") - { - if (_logger.IsInfo) - _logger.Info($"Seq Logging enabled on host: {seqConfig.ServerUrl} with level: {seqConfig.MinLevel}"); - NLogConfigurator.ConfigureSeqBufferTarget(seqConfig.ServerUrl, seqConfig.ApiKey, seqConfig.MinLevel); - } - else + Description = "The path to the Nethermind data directory.", + HelpName = "path" + }; + + public static CliOption LoggerConfigurationSource { get; } = + new("--logger-config", "--loggerConfigSource", "-lcs") { - // Clear it up, otherwise internally it will keep requesting to localhost as `all` target include this. - NLogConfigurator.ClearSeqTarget(); - } - } + Description = "The path to the logging configuration file.", + HelpName = "path" + }; - private static string GetProductInfo() + public static CliOption LogLevel { get; } = new("--log", "-l") { - var info = new StringBuilder(); - - info - .Append("Version: ").AppendLine(ProductInfo.Version) - .Append("Commit: ").AppendLine(ProductInfo.Commit) - .Append("Build Date: ").AppendLine(ProductInfo.BuildTimestamp.ToString("u")) - .Append("OS: ") - .Append(ProductInfo.OS) - .Append(' ') - .AppendLine(ProductInfo.OSArchitecture) - .Append("Runtime: ").AppendLine(ProductInfo.Runtime); - - return info.ToString(); - } + Description = "Log level (severity). Allowed values: off, trace, debug, info, warn, error.", + HelpName = "level" + }; + + public static CliOption PluginsDirectory { get; } = + new("--plugins-dir", "--pluginsDirectory", "-pd") + { + Description = "The path to the Nethermind plugins directory.", + HelpName = "path" + }; +} + +class AnonymousCliAction(Func action) : SynchronousCliAction +{ + private readonly Func _action = action ?? throw new ArgumentNullException(nameof(action)); + + /// + public override int Invoke(ParseResult parseResult) => _action(parseResult); } diff --git a/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json b/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json index 4d6bd1e6c04..7c43a7b79a7 100644 --- a/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json +++ b/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json @@ -1,204 +1,120 @@ { "profiles": { - "Chiado": { + "Base Mainnet": { "commandName": "Project", - "commandLineArgs": "-c chiado -dd .data", + "commandLineArgs": "-c base-mainnet --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Chiado (archive)": { + "Base Sepolia": { "commandName": "Project", - "commandLineArgs": "-c chiado_archive -dd .data", + "commandLineArgs": "-c base-sepolia --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "EIP-4844 local trace": { + "Chiado": { "commandName": "Project", - "commandLineArgs": "-c eip4844_local -dd .data --log TRACE", + "commandLineArgs": "-c chiado --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Energy Web": { "commandName": "Project", - "commandLineArgs": "-c energyweb -dd .data --JsonRpc.Enabled true", + "commandLineArgs": "-c energyweb --data-dir .data --jsonrpc-enabled true", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Gnosis": { "commandName": "Project", - "commandLineArgs": "-c gnosis -dd .data", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Gnosis (archive)": { - "commandName": "Project", - "commandLineArgs": "-c gnosis_archive -dd .data", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Goerli": { - "commandName": "Project", - "commandLineArgs": "-c goerli -dd .data", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Goerli (archive)": { - "commandName": "Project", - "commandLineArgs": "-c goerli_archive -dd .data", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "OP Mainnet": { - "commandName": "Project", - "commandLineArgs": "-c op-mainnet -dd %NETHERMIND_DATA_DIR%", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "OP Sepolia": { - "commandName": "Project", - "commandLineArgs": "-c op-sepolia -dd %NETHERMIND_DATA_DIR%", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Base Mainnet": { - "commandName": "Project", - "commandLineArgs": "-c base-mainnet -dd %NETHERMIND_DATA_DIR%", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Base Sepolia": { - "commandName": "Project", - "commandLineArgs": "-c base-sepolia -dd %NETHERMIND_DATA_DIR%", + "commandLineArgs": "-c gnosis --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Hive": { "commandName": "Project", - "commandLineArgs": "-c hive -dd .data --Init.DiagnosticMode MemDb", + "commandLineArgs": "-c hive --data-dir .data --init-diagnosticmode MemDb", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Holesky": { "commandName": "Project", - "commandLineArgs": "-c holesky -dd .data", + "commandLineArgs": "-c holesky --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Holesky (archive)": { + "JOC Mainnet": { "commandName": "Project", - "commandLineArgs": "-c holesky_archive -dd .data", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Mainnet": { - "commandName": "Project", - "commandLineArgs": "-c mainnet -dd %NETHERMIND_DATA_DIR%", + "commandLineArgs": "-c joc-mainnet -dd .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Mainnet (archive)": { + "JOC Testnet": { "commandName": "Project", - "commandLineArgs": "-c mainnet_archive -dd .data", + "commandLineArgs": "-c joc-testnet --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "POA Core": { + "Mainnet": { "commandName": "Project", - "commandLineArgs": "-c poacore -dd .data --JsonRpc.Enabled true", + "commandLineArgs": "-c mainnet --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Sepolia": { + "OP Mainnet": { "commandName": "Project", - "commandLineArgs": "-c sepolia -dd .data", + "commandLineArgs": "-c op-mainnet --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Sepolia (archive)": { + "OP Sepolia": { "commandName": "Project", - "commandLineArgs": "-c sepolia_archive -dd .data", + "commandLineArgs": "-c op-sepolia --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Spaceneth": { + "Sepolia": { "commandName": "Project", - "commandLineArgs": "-c spaceneth -dd .data", + "commandLineArgs": "-c sepolia --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Taiko Hekla": { "commandName": "Project", - "commandLineArgs": "-c taiko-hekla -dd .data", + "commandLineArgs": "-c taiko-hekla --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Taiko Mainnet": { "commandName": "Project", - "commandLineArgs": "-c taiko-mainnet -dd .data", + "commandLineArgs": "-c taiko-mainnet --data-dir .data", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Volta": { "commandName": "Project", - "commandLineArgs": "-c volta -dd .data --JsonRpc.Enabled true", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Joc-Mainnet": { - "commandName": "Project", - "commandLineArgs": "-c joc-mainnet -dd .data", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Joc-Testnet": { - "commandName": "Project", - "commandLineArgs": "-c joc-testnet -dd .data", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Joc-Mainnet (archive)": { - "commandName": "Project", - "commandLineArgs": "-c joc-mainnet_archive -dd .data", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Joc-Testnet (archive)": { - "commandName": "Project", - "commandLineArgs": "-c joc-testnet_archive -dd .data", + "commandLineArgs": "-c volta --data-dir .data --jsonrpc-enabled true", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Docker": { "commandName": "Docker", - "commandLineArgs": "-c sepolia -dd /data --JsonRpc.EngineHost 0.0.0.0 --JsonRpc.EnginePort 8551 --JsonRpc.Host 0.0.0.0" + "commandLineArgs": "-c holesky --data-dir .data /data --jsonrpc-enginehost 0.0.0.0 --jsonrpc-engineport 8551 --jsonrpc-host 0.0.0.0" } } } diff --git a/src/Nethermind/Nethermind.Runner/configs/AuraTest.cfg b/src/Nethermind/Nethermind.Runner/configs/AuraTest.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/AuraTest.cfg rename to src/Nethermind/Nethermind.Runner/configs/AuraTest.json diff --git a/src/Nethermind/Nethermind.Runner/configs/base-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/base-mainnet.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/base-mainnet.cfg rename to src/Nethermind/Nethermind.Runner/configs/base-mainnet.json diff --git a/src/Nethermind/Nethermind.Runner/configs/base-mainnet_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/base-mainnet_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/base-mainnet_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/base-mainnet_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/base-sepolia.cfg b/src/Nethermind/Nethermind.Runner/configs/base-sepolia.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/base-sepolia.cfg rename to src/Nethermind/Nethermind.Runner/configs/base-sepolia.json diff --git a/src/Nethermind/Nethermind.Runner/configs/base-sepolia_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/base-sepolia_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/base-sepolia_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/base-sepolia_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/chiado.cfg b/src/Nethermind/Nethermind.Runner/configs/chiado.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/chiado.cfg rename to src/Nethermind/Nethermind.Runner/configs/chiado.json diff --git a/src/Nethermind/Nethermind.Runner/configs/chiado_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/chiado_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/chiado_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/chiado_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg b/src/Nethermind/Nethermind.Runner/configs/energyweb.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/energyweb.cfg rename to src/Nethermind/Nethermind.Runner/configs/energyweb.json diff --git a/src/Nethermind/Nethermind.Runner/configs/energyweb_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/energyweb_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/energyweb_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/energyweb_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/exosama.cfg b/src/Nethermind/Nethermind.Runner/configs/exosama.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/exosama.cfg rename to src/Nethermind/Nethermind.Runner/configs/exosama.json diff --git a/src/Nethermind/Nethermind.Runner/configs/exosama_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/exosama_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/exosama_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/exosama_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg b/src/Nethermind/Nethermind.Runner/configs/gnosis.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/gnosis.cfg rename to src/Nethermind/Nethermind.Runner/configs/gnosis.json diff --git a/src/Nethermind/Nethermind.Runner/configs/gnosis_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/gnosis_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/gnosis_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/gnosis_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/hive.cfg b/src/Nethermind/Nethermind.Runner/configs/hive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/hive.cfg rename to src/Nethermind/Nethermind.Runner/configs/hive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/holesky.cfg b/src/Nethermind/Nethermind.Runner/configs/holesky.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/holesky.cfg rename to src/Nethermind/Nethermind.Runner/configs/holesky.json diff --git a/src/Nethermind/Nethermind.Runner/configs/holesky_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/holesky_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/holesky_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/holesky_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg rename to src/Nethermind/Nethermind.Runner/configs/joc-mainnet.json diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/joc-mainnet_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/joc-mainnet_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-testnet.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg rename to src/Nethermind/Nethermind.Runner/configs/joc-testnet.json diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-testnet_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-testnet_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/joc-testnet_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/joc-testnet_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/linea-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/linea-mainnet.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/linea-mainnet.cfg rename to src/Nethermind/Nethermind.Runner/configs/linea-mainnet.json diff --git a/src/Nethermind/Nethermind.Runner/configs/linea-mainnet_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/linea-mainnet_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/linea-mainnet_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/linea-mainnet_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/linea-sepolia.cfg b/src/Nethermind/Nethermind.Runner/configs/linea-sepolia.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/linea-sepolia.cfg rename to src/Nethermind/Nethermind.Runner/configs/linea-sepolia.json diff --git a/src/Nethermind/Nethermind.Runner/configs/linea-sepolia_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/linea-sepolia_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/linea-sepolia_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/linea-sepolia_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/mainnet.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/mainnet.cfg rename to src/Nethermind/Nethermind.Runner/configs/mainnet.json diff --git a/src/Nethermind/Nethermind.Runner/configs/mainnet_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/mainnet_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/mainnet_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/mainnet_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/none.cfg b/src/Nethermind/Nethermind.Runner/configs/none.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/none.cfg rename to src/Nethermind/Nethermind.Runner/configs/none.json diff --git a/src/Nethermind/Nethermind.Runner/configs/op-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/op-mainnet.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/op-mainnet.cfg rename to src/Nethermind/Nethermind.Runner/configs/op-mainnet.json diff --git a/src/Nethermind/Nethermind.Runner/configs/op-mainnet_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/op-mainnet_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/op-mainnet_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/op-mainnet_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/op-sepolia.cfg b/src/Nethermind/Nethermind.Runner/configs/op-sepolia.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/op-sepolia.cfg rename to src/Nethermind/Nethermind.Runner/configs/op-sepolia.json diff --git a/src/Nethermind/Nethermind.Runner/configs/op-sepolia_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/op-sepolia_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/op-sepolia_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/op-sepolia_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/poacore.cfg b/src/Nethermind/Nethermind.Runner/configs/poacore.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/poacore.cfg rename to src/Nethermind/Nethermind.Runner/configs/poacore.json diff --git a/src/Nethermind/Nethermind.Runner/configs/poacore_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/poacore_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/poacore_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/poacore_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/poacore_validator.cfg b/src/Nethermind/Nethermind.Runner/configs/poacore_validator.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/poacore_validator.cfg rename to src/Nethermind/Nethermind.Runner/configs/poacore_validator.json diff --git a/src/Nethermind/Nethermind.Runner/configs/sepolia.cfg b/src/Nethermind/Nethermind.Runner/configs/sepolia.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/sepolia.cfg rename to src/Nethermind/Nethermind.Runner/configs/sepolia.json diff --git a/src/Nethermind/Nethermind.Runner/configs/sepolia_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/sepolia_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/sepolia_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/sepolia_archive.json diff --git a/src/Nethermind/Nethermind.Runner/configs/spaceneth.cfg b/src/Nethermind/Nethermind.Runner/configs/spaceneth.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/spaceneth.cfg rename to src/Nethermind/Nethermind.Runner/configs/spaceneth.json diff --git a/src/Nethermind/Nethermind.Runner/configs/spaceneth_persistent.cfg b/src/Nethermind/Nethermind.Runner/configs/spaceneth_persistent.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/spaceneth_persistent.cfg rename to src/Nethermind/Nethermind.Runner/configs/spaceneth_persistent.json diff --git a/src/Nethermind/Nethermind.Runner/configs/taiko-hekla.cfg b/src/Nethermind/Nethermind.Runner/configs/taiko-hekla.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/taiko-hekla.cfg rename to src/Nethermind/Nethermind.Runner/configs/taiko-hekla.json diff --git a/src/Nethermind/Nethermind.Runner/configs/taiko-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/taiko-mainnet.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/taiko-mainnet.cfg rename to src/Nethermind/Nethermind.Runner/configs/taiko-mainnet.json diff --git a/src/Nethermind/Nethermind.Runner/configs/volta.cfg b/src/Nethermind/Nethermind.Runner/configs/volta.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/volta.cfg rename to src/Nethermind/Nethermind.Runner/configs/volta.json diff --git a/src/Nethermind/Nethermind.Runner/configs/volta_archive.cfg b/src/Nethermind/Nethermind.Runner/configs/volta_archive.json similarity index 100% rename from src/Nethermind/Nethermind.Runner/configs/volta_archive.cfg rename to src/Nethermind/Nethermind.Runner/configs/volta_archive.json diff --git a/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj b/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj index fd96da1e534..7a0db5f7549 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj +++ b/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/Nethermind/Nethermind.Test.Runner/Program.cs b/src/Nethermind/Nethermind.Test.Runner/Program.cs index ccb60229cb5..6161803bcea 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Test.Runner/Program.cs @@ -2,96 +2,118 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.CommandLine; using System.IO; +using System.Threading; using System.Threading.Tasks; -using CommandLine; using Ethereum.Test.Base; using Ethereum.Test.Base.Interfaces; -namespace Nethermind.Test.Runner +namespace Nethermind.Test.Runner; + +internal class Program { - internal class Program + public class Options { - public class Options - { - [Option('i', "input", Required = false, HelpText = "Set the state test input file or directory. Either 'input' or 'stdin' is required")] - public string Input { get; set; } + public static CliOption Input { get; } = + new("--input", "-i") { Description = "Set the state test input file or directory. Either 'input' or 'stdin' is required." }; - [Option('f', "filter", Required = false, HelpText = "Set the test name that you want to run. Could also be a regular expression")] - public string Filter { get; set; } + public static CliOption Filter { get; } = + new("--filter", "-f") { Description = "Set the test name that you want to run. Could also be a regular expression." }; - [Option('b', "blockTest", Required = false, HelpText = "Set test as blockTest. if not, it will be by default assumed a state test.")] - public bool BlockTest { get; set; } + public static CliOption BlockTest { get; } = + new("--blockTest", "-b") { Description = "Set test as blockTest. if not, it will be by default assumed a state test." }; - [Option('t', "trace", Required = false, HelpText = "Set to always trace (by default traces are only generated for failing tests). [Only for State Test]")] - public bool TraceAlways { get; set; } + public static CliOption TraceAlways { get; } = + new("--trace", "-t") { Description = "Set to always trace (by default traces are only generated for failing tests). [Only for State Test]" }; - [Option('n', "neverTrace", Required = false, HelpText = "Set to never trace (by default traces are only generated for failing tests). [Only for State Test]")] - public bool TraceNever { get; set; } + public static CliOption TraceNever { get; } = + new("--neverTrace", "-n") { Description = "Set to never trace (by default traces are only generated for failing tests). [Only for State Test]" }; - [Option('m', "memory", Required = false, HelpText = "Exclude memory trace. [Only for State Test]")] - public bool ExcludeMemory { get; set; } + public static CliOption ExcludeMemory { get; } = + new("--memory", "-m") { Description = "Exclude memory trace. [Only for State Test]" }; - [Option('s', "stack", Required = false, HelpText = "Exclude stack trace. [Only for State Test]")] - public bool ExcludeStack { get; set; } + public static CliOption ExcludeStack { get; } = + new("--stack", "-s") { Description = "Exclude stack trace. [Only for State Test]" }; - [Option('w', "wait", Required = false, HelpText = "Wait for input after the test run.")] - public bool Wait { get; set; } + public static CliOption Wait { get; } = + new("--wait", "-w") { Description = "Wait for input after the test run." }; - [Option('x', "stdin", Required = false, HelpText = "If stdin is used, the state runner will read inputs (filenames) from stdin, and continue executing until empty line is read.")] - public bool Stdin { get; set; } - } + public static CliOption Stdin { get; } = + new("--stdin", "-x") { Description = "If stdin is used, the state runner will read inputs (filenames) from stdin, and continue executing until empty line is read." }; + } - public static async Task Main(params string[] args) - { - ParserResult result = Parser.Default.ParseArguments(args); - if (result is Parsed options) - await Run(options.Value); - } + public static async Task Main(params string[] args) + { + CliRootCommand rootCommand = + [ + Options.Input, + Options.Filter, + Options.BlockTest, + Options.TraceAlways, + Options.TraceNever, + Options.ExcludeMemory, + Options.ExcludeStack, + Options.Wait, + Options.Stdin + ]; + rootCommand.SetAction(Run); + + CliConfiguration configuration = new(rootCommand); + + return await configuration.InvokeAsync(args); + } - private static async Task Run(Options options) - { - WhenTrace whenTrace = WhenTrace.WhenFailing; - if (options.TraceNever) - whenTrace = WhenTrace.Never; - - if (options.TraceAlways) - whenTrace = WhenTrace.Always; - - string input = options.Input; - if (options.Stdin) - input = Console.ReadLine(); - - while (!string.IsNullOrWhiteSpace(input)) - { - if (options.BlockTest) - await RunBlockTest(input, source => new BlockchainTestsRunner(source, options.Filter)); - else - RunStateTest(input, source => new StateTestsRunner(source, whenTrace, !options.ExcludeMemory, !options.ExcludeStack, options.Filter)); - if (!options.Stdin) - break; - - input = Console.ReadLine(); - } - - if (options.Wait) - Console.ReadLine(); - } + private static async Task Run(ParseResult parseResult, CancellationToken cancellationToken) + { + WhenTrace whenTrace = WhenTrace.WhenFailing; - private static async Task RunBlockTest(string path, Func testRunnerBuilder) - { - ITestSourceLoader source = Path.HasExtension(path) - ? new TestsSourceLoader(new LoadBlockchainTestFileStrategy(), path) - : new TestsSourceLoader(new LoadBlockchainTestsStrategy(), path); - await testRunnerBuilder(source).RunTestsAsync(); - } + if (parseResult.GetValue(Options.TraceNever)) + whenTrace = WhenTrace.Never; + + if (parseResult.GetValue(Options.TraceAlways)) + whenTrace = WhenTrace.Always; + + string input = parseResult.GetValue(Options.Input); - private static void RunStateTest(string path, Func testRunnerBuilder) + if (parseResult.GetValue(Options.Stdin)) + input = Console.ReadLine(); + + while (!string.IsNullOrWhiteSpace(input)) { - ITestSourceLoader source = Path.HasExtension(path) - ? new TestsSourceLoader(new LoadGeneralStateTestFileStrategy(), path) - : new TestsSourceLoader(new LoadGeneralStateTestsStrategy(), path); - testRunnerBuilder(source).RunTests(); + if (parseResult.GetValue(Options.BlockTest)) + await RunBlockTest(input, source => new BlockchainTestsRunner(source, parseResult.GetValue(Options.Filter))); + else + RunStateTest(input, source => new StateTestsRunner(source, whenTrace, + !parseResult.GetValue(Options.ExcludeMemory), + !parseResult.GetValue(Options.ExcludeStack), + parseResult.GetValue(Options.Filter))); + + if (!parseResult.GetValue(Options.Stdin)) + break; + + input = Console.ReadLine(); } + + if (parseResult.GetValue(Options.Wait)) + Console.ReadLine(); + + return 0; + } + + private static async Task RunBlockTest(string path, Func testRunnerBuilder) + { + ITestSourceLoader source = Path.HasExtension(path) + ? new TestsSourceLoader(new LoadBlockchainTestFileStrategy(), path) + : new TestsSourceLoader(new LoadBlockchainTestsStrategy(), path); + await testRunnerBuilder(source).RunTestsAsync(); + } + + private static void RunStateTest(string path, Func testRunnerBuilder) + { + ITestSourceLoader source = Path.HasExtension(path) + ? new TestsSourceLoader(new LoadGeneralStateTestFileStrategy(), path) + : new TestsSourceLoader(new LoadGeneralStateTestsStrategy(), path); + testRunnerBuilder(source).RunTests(); } } diff --git a/src/Nethermind/nuget.config b/src/Nethermind/nuget.config index 84c8ccf735e..401f058f2ab 100644 --- a/src/Nethermind/nuget.config +++ b/src/Nethermind/nuget.config @@ -3,14 +3,19 @@ + - + diff --git a/tools/DocGen/ConfigGenerator.cs b/tools/DocGen/ConfigGenerator.cs index 3edaabcd26a..aa39b045c3d 100644 --- a/tools/DocGen/ConfigGenerator.cs +++ b/tools/DocGen/ConfigGenerator.cs @@ -107,6 +107,7 @@ private static void WriteMarkdown(StreamWriter file, Type configType) ``` + --{{moduleName.ToLowerInvariant()}}-{{prop.Name.ToLowerInvariant()}} --{{moduleName}}.{{prop.Name}} ``` diff --git a/tools/DocGen/DocGen.csproj b/tools/DocGen/DocGen.csproj index 1890c043d16..bf97292bb15 100644 --- a/tools/DocGen/DocGen.csproj +++ b/tools/DocGen/DocGen.csproj @@ -8,7 +8,8 @@ - + + diff --git a/tools/DocGen/Program.cs b/tools/DocGen/Program.cs index 80a62ef36ca..d4d70e110ac 100644 --- a/tools/DocGen/Program.cs +++ b/tools/DocGen/Program.cs @@ -1,66 +1,65 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.ComponentModel; +using System.CommandLine; using Nethermind.DocGen; using Spectre.Console; -using Spectre.Console.Cli; -var app = new CommandApp(); - -app.Run(args); - -public sealed class AppCommand : Command +CliOption configOption = new("--config") { Description = "Generate configuration options docs" }; +CliOption dbSizeOption = new("--dbsize") { Description = "Generate DB sizes" }; +CliOption dbSizeSourceOption = new("--dbsize-src") { - public override int Execute(CommandContext context, AppSettings settings) - { - if (settings.DocsPath is null) - { - AnsiConsole.MarkupLine("[red]The path to the docs is not specified[/]"); - return 1; - } - - if (!Directory.Exists(settings.DocsPath)) - { - AnsiConsole.MarkupLine("[red]No docs not found at the path specified[/]"); - return 1; - } - - if (settings.GenerateConfig) - ConfigGenerator.Generate(settings.DocsPath); - - if (settings.GenerateDBSize) - DBSizeGenerator.Generate(settings.DocsPath, settings.DBSizeSourcePath); - - if (settings.GenerateJsonRpc) - JsonRpcGenerator.Generate(settings.DocsPath); + Description = "The path to the directory with DB size files", + HelpName = "path" +}; +CliArgument docsDirArg = new("docs-dir") +{ + Description = "The path to the docs directory", + HelpName = "path" +}; +CliOption jsonRpcOption = new("--jsonrpc") { Description = "Generate JSON-RPC API docs" }; +CliOption metricsOption = new("--metrics") { Description = "Generate metrics options docs" }; - if (settings.GenerateMetrics) - MetricsGenerator.Generate(settings.DocsPath); +dbSizeOption.Validators.Add(optionResult => +{ + if (optionResult.Parent?.GetValue(dbSizeSourceOption) is null) + optionResult.AddError($"{dbSizeSourceOption.Name} must be specified when {dbSizeOption.Name} is set"); +}); + +CliRootCommand rootCommand = +[ + configOption, + dbSizeOption, + dbSizeSourceOption, + docsDirArg, + jsonRpcOption, + metricsOption +]; +rootCommand.SetAction(parseResult => +{ + var docsPath = parseResult.GetValue(docsDirArg)!; - return 0; + if (!Directory.Exists(docsPath)) + { + AnsiConsole.MarkupLine("[red]The specified docs directory not found[/]"); + return 1; } -} -public sealed class AppSettings : CommandSettings -{ - [Description("Path to the directory with DB size files")] - [CommandOption("--dbsize-src")] - public string? DBSizeSourcePath { get; init; } + if (parseResult.GetValue(configOption)) + ConfigGenerator.Generate(docsPath); + + if (parseResult.GetValue(dbSizeOption)) + DBSizeGenerator.Generate(docsPath, parseResult.GetValue(dbSizeSourceOption)); - [Description("Path to the docs")] - [CommandArgument(0, "[docspath]")] - public string? DocsPath { get; init; } + if (parseResult.GetValue(jsonRpcOption)) + JsonRpcGenerator.Generate(docsPath); - [CommandOption("--config")] - public bool GenerateConfig { get; init; } + if (parseResult.GetValue(metricsOption)) + MetricsGenerator.Generate(docsPath); - [CommandOption("--dbsize")] - public bool GenerateDBSize { get; init; } + return 0; +}); - [CommandOption("--jsonrpc")] - public bool GenerateJsonRpc { get; init; } +CliConfiguration cli = new(rootCommand); - [CommandOption("--metrics")] - public bool GenerateMetrics { get; init; } -} +return cli.Invoke(args); diff --git a/tools/DocGen/Properties/launchSettings.json b/tools/DocGen/Properties/launchSettings.json index 14e64bfc589..090d2b5ec38 100644 --- a/tools/DocGen/Properties/launchSettings.json +++ b/tools/DocGen/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "DocGen": { "commandName": "Project", - "commandLineArgs": "/path/to/docs --config --dbsize --jsonrpc --metrics" + "commandLineArgs": "/path/to/docs --config --jsonrpc --metrics" } } } diff --git a/tools/DocGen/nuget.config b/tools/DocGen/nuget.config deleted file mode 100644 index 765346e5343..00000000000 --- a/tools/DocGen/nuget.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/tools/HiveCompare/HiveCompare/HiveCompare.csproj b/tools/HiveCompare/HiveCompare/HiveCompare.csproj index aa57c232794..61ac6a5d41c 100644 --- a/tools/HiveCompare/HiveCompare/HiveCompare.csproj +++ b/tools/HiveCompare/HiveCompare/HiveCompare.csproj @@ -8,7 +8,7 @@ - + diff --git a/tools/HiveCompare/HiveCompare/Program.cs b/tools/HiveCompare/HiveCompare/Program.cs index 2107db69bd1..5da4acd2f65 100644 --- a/tools/HiveCompare/HiveCompare/Program.cs +++ b/tools/HiveCompare/HiveCompare/Program.cs @@ -1,5 +1,5 @@ using HiveCompare.Models; -using McMaster.Extensions.CommandLineUtils; +using System.CommandLine; using System.Diagnostics.CodeAnalysis; using System.Text.Json; @@ -13,53 +13,40 @@ internal class Program private static void Main(string[] args) { - CommandLineApplication cli = CreateCommandLineInterface(); - try + CliOption firstFileOption = new("--first-file", "-f") { - cli.Execute(args); - } - catch (CommandParsingException) + Description = "The first file to be used for comparison", + Required = true, + HelpName = "path" + }; + CliOption secondFileOption = new("--second-file", "-s") { - cli.ShowHelp(); - } - } - - static CommandLineApplication CreateCommandLineInterface() - { - CommandLineApplication cli = new() { Name = "HiveCompare" }; - cli.HelpOption("-?|-h|--help"); - CommandOption firstFileOption = cli.Option("-f|--first-file", "first file to be used for comparison", CommandOptionType.SingleValue); - CommandOption secondFileOption = cli.Option("-s|--second-file", "second file to be used for comparison", CommandOptionType.SingleValue); + Description = "The second file to be used for comparison", + Required = true, + HelpName = "path" + }; + CliRootCommand rootCommand = [firstFileOption, secondFileOption]; - cli.OnExecute(() => + rootCommand.SetAction(parseResult => { - bool HasRequiredOption(CommandOption option) + static bool RequiredFileExists(string? filePath) { - if (option.HasValue() && !string.IsNullOrEmpty(option.Value())) return true; + if (File.Exists(filePath)) return true; - cli.ShowHelp(); + Console.WriteLine($"Could not find file '{filePath}'."); return false; } - bool RequiredFileExists(CommandOption option) - { - if (File.Exists(option.Value())) return true; - - Console.WriteLine($"Could not find file '{option.Value()}'."); - return false; - - } + string? firstFileValue = parseResult.GetValue(firstFileOption); + string? secondFileValue = parseResult.GetValue(secondFileOption); - return HasRequiredOption(firstFileOption) && HasRequiredOption(secondFileOption) - ? RequiredFileExists(firstFileOption) && RequiredFileExists(secondFileOption) - ? ParseTests(firstFileOption.Value()!, secondFileOption.Value()!) - ? 0 - : 4 - : 2 - : 1; + return RequiredFileExists(firstFileValue) && RequiredFileExists(secondFileValue) + ? ParseTests(firstFileValue!, secondFileValue!) ? 0 : 4 + : 2; }); - return cli; + CliConfiguration cli = new(rootCommand); + cli.Invoke(args); } private static bool ParseTests(string firstFile, string secondFile) diff --git a/tools/Nethermind.Tools.Kute/Config.cs b/tools/Nethermind.Tools.Kute/Config.cs index a6467c30312..63cba1a916b 100644 --- a/tools/Nethermind.Tools.Kute/Config.cs +++ b/tools/Nethermind.Tools.Kute/Config.cs @@ -1,135 +1,80 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using CommandLine; using Nethermind.Tools.Kute.MetricsConsumer; +using System.CommandLine; namespace Nethermind.Tools.Kute; -public class Config +public static class Config { - [Option( - shortName: 'i', - longName: "input", + public static CliOption MessagesFilePath { get; } = new("--input", "-i") + { + Description = "Path to a file or directory containing JSON RPC messages", + HelpName = "path", Required = true, - HelpText = "Path to a Folder or a File containing JSON RPC messages" - )] - public string MessagesFilePath { get; } - - [Option( - shortName: 'a', - longName: "address", - Required = false, - Default = "http://localhost:8551", - HelpText = "Address where to send JSON RPC requests" - )] - public string HostAddress { get; } + }; - [Option( - shortName: 's', - longName: "secret", - Required = true, - HelpText = "Path to File with hex encoded secret for JWT authentication" - )] - public string JwtSecretFilePath { get; } + public static CliOption HostAddress { get; } = new("--address", "-a") + { + DefaultValueFactory = r => "http://localhost:8551", + Description = "Address where to send JSON RPC requests", + HelpName = "URL" + }; - [Option( - shortName: 't', - longName: "ttl", - Required = false, - Default = 60, - HelpText = "Authentication time to live (ttl) in seconds" - )] - public int AuthTtl { get; } + public static CliOption JwtSecretFilePath { get; } = new("--secret", "-s") + { + Description = "Path to file with hex-encoded secret for JWT authentication", + HelpName = "value", + Required = true + }; - [Option( - shortName: 'd', - longName: "dry", - Required = false, - Default = false, - HelpText = "Only log into console" - )] - public bool DryRun { get; } + public static CliOption AuthTtl { get; } = new("--ttl", "-t") + { + DefaultValueFactory = r => 60, + Description = "Authentication time to live (TTL), in seconds", + HelpName = "value" + }; - [Option( - shortName: 'p', - longName: "progress", - Required = false, - Default = false, - HelpText = "Show progress" - )] - public bool ShowProgress { get; } + public static CliOption DryRun { get; } = new("--dry", "-d") + { + Description = "Only log into console" + }; - [Option( - shortName: 'o', - longName: "output", - Required = false, - Default = MetricsOutputFormatter.Report, - HelpText = "Strategy to report metrics" - )] - public MetricsOutputFormatter MetricsOutputFormatter { get; } + public static CliOption ShowProgress { get; } = new("--progress", "-p") + { + Description = "Show progress" + }; - [Option( - shortName: 'f', - longName: "filters", - Separator = ',', - Required = false, - Default = new string[] { }, - HelpText = "A comma separated List of regexes of methods to be executed with optional limits" - )] - public IEnumerable MethodFilters { get; } + public static CliOption MetricsOutputFormatter { get; } = new("--output", "-o") + { + DefaultValueFactory = r => MetricsConsumer.MetricsOutputFormatter.Report, + Description = "Strategy to report metrics", + HelpName = "value", + }; - [Option( - shortName: 'r', - longName: "responses", - Required = false, - Default = null, - HelpText = "Path to File to store JSON-RPC responses" - )] - public string? ResponsesTraceFile { get; } + public static CliOption> MethodFilters { get; } = new("--filters", "-f") + { + DefaultValueFactory = r => [], + CustomParser = r => r.Tokens.Count == 1 ? r.Tokens[0].Value.Split(',') : null, + Description = "A comma separated List of regexes of methods to be executed with optional limits", + HelpName = "value", + }; - [Option( - shortName: 'e', - longName: "rps", - Required = false, - Default = 0, - HelpText = "If set to higher than 0, then requests will be send in selected RPS (Requests per seconds) rate. If 0 (or lower) then requests will be sent sequentionally." - )] - public int RequestsPerSecond { get; } + public static CliOption ResponsesTraceFile { get; } = new("--responses", "-r") + { + Description = "Path to file to store JSON-RPC responses", + HelpName = "path" + }; - [Option( - shortName: 'u', - longName: "unwrapBatch", - Required = false, - Default = false, - HelpText = "If true then each batched request will be unwraped to single requests." - )] - public bool UnwrapBatch { get; } + public static CliOption RequestsPerSecond { get; } = new("--rps", "-e") + { + Description = "If set to higher than 0, then requests will be send in selected RPS (Requests per seconds) rate. If 0 (or lower) then requests will be sent sequentially", + HelpName = "value" + }; - public Config( - string messagesFilePath, - string hostAddress, - string jwtSecretFilePath, - int authTtl, - bool dryRun, - bool showProgress, - MetricsOutputFormatter metricsOutputFormatter, - IEnumerable methodFilters, - string? responsesTraceFile, - int requestsPerSecond, - bool unwrapBatch - ) + public static CliOption UnwrapBatch { get; } = new("--unwrapBatch", "-u") { - MessagesFilePath = messagesFilePath; - HostAddress = hostAddress; - JwtSecretFilePath = jwtSecretFilePath; - AuthTtl = authTtl; - DryRun = dryRun; - ShowProgress = showProgress; - MetricsOutputFormatter = metricsOutputFormatter; - MethodFilters = methodFilters; - ResponsesTraceFile = responsesTraceFile; - RequestsPerSecond = requestsPerSecond; - UnwrapBatch = unwrapBatch; - } + Description = "If true then each batched request will be unwraped to single requests" + }; } diff --git a/tools/Nethermind.Tools.Kute/Nethermind.Tools.Kute.csproj b/tools/Nethermind.Tools.Kute/Nethermind.Tools.Kute.csproj index 0e5870bdf32..32fb32af4b3 100644 --- a/tools/Nethermind.Tools.Kute/Nethermind.Tools.Kute.csproj +++ b/tools/Nethermind.Tools.Kute/Nethermind.Tools.Kute.csproj @@ -9,9 +9,9 @@ - + diff --git a/tools/Nethermind.Tools.Kute/Program.cs b/tools/Nethermind.Tools.Kute/Program.cs index 3e5157cfcc9..67b6837d0c6 100644 --- a/tools/Nethermind.Tools.Kute/Program.cs +++ b/tools/Nethermind.Tools.Kute/Program.cs @@ -1,7 +1,6 @@ using App.Metrics.Formatters; using App.Metrics.Formatters.Ascii; using App.Metrics.Formatters.Json; -using CommandLine; using Microsoft.Extensions.DependencyInjection; using Nethermind.Tools.Kute.Auth; using Nethermind.Tools.Kute.FlowManager; @@ -15,30 +14,52 @@ using Nethermind.Tools.Kute.ResponseTracer; using Nethermind.Tools.Kute.SecretProvider; using Nethermind.Tools.Kute.SystemClock; +using System.CommandLine; namespace Nethermind.Tools.Kute; static class Program { - public static async Task Main(string[] args) + public static async Task Main(string[] args) { - await Parser.Default.ParseArguments(args).WithParsedAsync(async config => + CliRootCommand rootCommand = + [ + Config.MessagesFilePath, + Config.HostAddress, + Config.JwtSecretFilePath, + Config.AuthTtl, + Config.DryRun, + Config.ShowProgress, + Config.MetricsOutputFormatter, + Config.MethodFilters, + Config.ResponsesTraceFile, + Config.RequestsPerSecond, + Config.UnwrapBatch + ]; + rootCommand.SetAction((parseResult, cancellationToken) => { - IServiceProvider serviceProvider = BuildServiceProvider(config); + IServiceProvider serviceProvider = BuildServiceProvider(parseResult); Application app = serviceProvider.GetService()!; - await app.Run(); + return app.Run(); }); + + CliConfiguration cli = new(rootCommand); + + return await cli.InvokeAsync(args); } - static IServiceProvider BuildServiceProvider(Config config) + private static IServiceProvider BuildServiceProvider(ParseResult parseResult) { + bool dryRun = parseResult.GetValue(Config.DryRun); + bool unwrapBatch = parseResult.GetValue(Config.UnwrapBatch); + string? responsesTraceFile = parseResult.GetValue(Config.ResponsesTraceFile); IServiceCollection collection = new ServiceCollection(); collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(); - collection.AddSingleton(new FileSecretProvider(config.JwtSecretFilePath)); + collection.AddSingleton(new FileSecretProvider(parseResult.GetValue(Config.JwtSecretFilePath)!)); collection.AddSingleton(provider => new TtlAuth( new JwtAuth( @@ -46,42 +67,39 @@ static IServiceProvider BuildServiceProvider(Config config) provider.GetRequiredService() ), provider.GetRequiredService(), - config.AuthTtl + parseResult.GetValue(Config.AuthTtl) ) ); - collection.AddSingleton>(new FileMessageProvider(config.MessagesFilePath)); + collection.AddSingleton>(new FileMessageProvider(parseResult.GetValue(Config.MessagesFilePath)!)); collection.AddSingleton>(serviceProvider => { var messageProvider = serviceProvider.GetRequiredService>(); var jsonMessageProvider = new JsonRpcMessageProvider(messageProvider); - return config.UnwrapBatch - ? new UnwrapBatchJsonRpcMessageProvider(jsonMessageProvider) - : jsonMessageProvider; + return unwrapBatch ? new UnwrapBatchJsonRpcMessageProvider(jsonMessageProvider) : jsonMessageProvider; }); - collection.AddSingleton( - config.DryRun - ? new NullJsonRpcValidator() - : new ComposedJsonRpcValidator(new List - { - new NonErrorJsonRpcValidator(), new NewPayloadJsonRpcValidator(), - }) + collection.AddSingleton(dryRun + ? new NullJsonRpcValidator() + : new ComposedJsonRpcValidator(new List + { + new NonErrorJsonRpcValidator(), new NewPayloadJsonRpcValidator(), + }) ); collection.AddSingleton( new ComposedJsonRpcMethodFilter( - config.MethodFilters + parseResult.GetValue(Config.MethodFilters)! .Select(pattern => new PatternJsonRpcMethodFilter(pattern) as IJsonRpcMethodFilter) .ToList() ) ); collection.AddSingleton(provider => { - if (!config.DryRun) + if (!dryRun) { return new HttpJsonRpcSubmitter( provider.GetRequiredService(), provider.GetRequiredService(), - config.HostAddress + parseResult.GetValue(Config.HostAddress)! ); } @@ -92,13 +110,13 @@ static IServiceProvider BuildServiceProvider(Config config) return new NullJsonRpcSubmitter(); }); collection.AddSingleton( - config is { DryRun: false, ResponsesTraceFile: not null } - ? new FileResponseTracer(config.ResponsesTraceFile) + !dryRun && responsesTraceFile is not null + ? new FileResponseTracer(responsesTraceFile) : new NullResponseTracer() ); collection.AddSingleton(provider => { - if (config.ShowProgress) + if (parseResult.GetValue(Config.ShowProgress)) { // NOTE: // Terrible, terrible hack since it forces a double enumeration: @@ -107,7 +125,7 @@ static IServiceProvider BuildServiceProvider(Config config) // We can reduce the cost by not parsing each message on the first enumeration // only when we're not unwrapping batches. If we are, we need to parse. // This optimization relies on implementation details. - IMessageProvider messagesProvider = config.UnwrapBatch + IMessageProvider messagesProvider = unwrapBatch ? provider.GetRequiredService>() : provider.GetRequiredService>(); var totalMessages = messagesProvider.Messages.ToEnumerable().Count(); @@ -118,14 +136,15 @@ static IServiceProvider BuildServiceProvider(Config config) }); collection.AddSingleton(); collection.AddSingleton( - config.MetricsOutputFormatter switch + parseResult.GetValue(Config.MetricsOutputFormatter) switch { MetricsOutputFormatter.Report => new MetricsTextOutputFormatter(), MetricsOutputFormatter.Json => new MetricsJsonOutputFormatter(), _ => throw new ArgumentOutOfRangeException(), } ); - collection.AddSingleton(new JsonRpcFlowManager(config.RequestsPerSecond, config.UnwrapBatch)); + collection.AddSingleton(new JsonRpcFlowManager( + parseResult.GetValue(Config.RequestsPerSecond), unwrapBatch)); return collection.BuildServiceProvider(); } diff --git a/tools/SendBlobs/Program.cs b/tools/SendBlobs/Program.cs index 109df4e7247..08f18a197a4 100644 --- a/tools/SendBlobs/Program.cs +++ b/tools/SendBlobs/Program.cs @@ -1,40 +1,16 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using McMaster.Extensions.CommandLineUtils; -using Nethermind.Cli; -using Nethermind.Cli.Console; -using Nethermind.Consensus; -using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Crypto; -using Nethermind.Evm; -using Nethermind.Facade.Proxy.Models; -using Nethermind.Int256; -using Nethermind.Logging; -using Nethermind.Serialization.Json; -using Nethermind.Serialization.Rlp; -using Org.BouncyCastle.Utilities.Encoders; using SendBlobs; +using System.CommandLine; -CommandLineApplication app = new() { Name = "SendBlobs" }; - -SetupCli.SetupExecute(app); -SetupCli.SetupDistributeCommand(app); -SetupCli.SetupReclaimCommand(app); -SetupCli.SetupSendFileCommand(app); - -try -{ - app.Execute(args); -} -catch (CommandParsingException ex) -{ - Console.WriteLine(ex.Message); - app.ShowHelp(); -} - - +CliRootCommand rootCommand = []; +SetupCli.SetupExecute(rootCommand); +SetupCli.SetupDistributeCommand(rootCommand); +SetupCli.SetupReclaimCommand(rootCommand); +SetupCli.SetupSendFileCommand(rootCommand); +CliConfiguration cli = new(rootCommand); +return await cli.InvokeAsync(args); diff --git a/tools/SendBlobs/SendBlobs.csproj b/tools/SendBlobs/SendBlobs.csproj index 93c44067294..df746e10d83 100644 --- a/tools/SendBlobs/SendBlobs.csproj +++ b/tools/SendBlobs/SendBlobs.csproj @@ -12,7 +12,7 @@ - + diff --git a/tools/SendBlobs/SetupCli.cs b/tools/SendBlobs/SetupCli.cs index 43352f1a5ee..c64e0b09770 100644 --- a/tools/SendBlobs/SetupCli.cs +++ b/tools/SendBlobs/SetupCli.cs @@ -1,84 +1,107 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using McMaster.Extensions.CommandLineUtils; using Nethermind.Cli; using Nethermind.Cli.Console; using Nethermind.Consensus; -using Nethermind.Core; using Nethermind.Crypto; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Serialization.Json; +using System.CommandLine; namespace SendBlobs; internal static class SetupCli { - public static void SetupExecute(CommandLineApplication app) + public static void SetupExecute(CliRootCommand command) { - app.HelpOption("--help"); - - CommandOption rpcUrlOption = app.Option("--rpcurl ", "Url of the Json RPC.", CommandOptionType.SingleValue); - CommandOption blobTxOption = app.Option("--bloboptions ", "Options in format '10x1-2', '2x5-5' etc. for the blobs.", CommandOptionType.MultipleValue); - CommandOption privateKeyOption = app.Option("--privatekey ", "The key to use for sending blobs.", CommandOptionType.SingleValue); - CommandOption privateKeyFileOption = app.Option("--keyfile ", "File containing private keys that each blob tx will be send from.", CommandOptionType.SingleValue); - CommandOption receiverOption = app.Option("--receiveraddress ", "Receiver address of the blobs.", CommandOptionType.SingleValue); - CommandOption maxFeePerDataGasOptionObsolete = app.Option("--maxfeeperdatagas ", "(Optional) Set the maximum fee per blob data.", CommandOptionType.SingleValue); - CommandOption maxFeePerBlobGasOption = app.Option("--maxfeeperblobgas ", "(Optional) Set the maximum fee per blob data.", CommandOptionType.SingleValue); - CommandOption feeMultiplierOption = app.Option("--feemultiplier ", "(Optional) A multiplier to use for gas fees.", CommandOptionType.SingleValue); - CommandOption maxPriorityFeeGasOption = app.Option("--maxpriorityfee ", "(Optional) The maximum priority fee for each transaction.", CommandOptionType.SingleValue); - CommandOption waitOption = app.Option("--wait", "(Optional) Wait for tx inclusion.", CommandOptionType.NoValue); - - app.OnExecuteAsync(async cancellationToken => + CliOption rpcUrlOption = new("--rpcurl") + { + Description = "The URL of the JSON RPC server", + HelpName = "URL", + Required = true + }; + CliOption blobTxOption = new("--bloboptions") + { + Description = "Options in format '10x1-2', '2x5-5' etc. for the blobs", + HelpName = "options" + }; + CliOption privateKeyOption = new("--privatekey") + { + Description = "The key to use for sending blobs", + HelpName = "key" + }; + CliOption privateKeyFileOption = new("--keyfile") + { + Description = "File containing private keys that each blob tx will be send from", + HelpName = "path" + }; + CliOption receiverOption = new("--receiveraddress") + { + Description = "Receiver address of the blobs", + HelpName = "address", + Required = true + }; + CliOption maxFeePerDataGasOptionObsolete = new("--maxfeeperdatagas") + { + Description = "Set the maximum fee per blob data", + HelpName = "fee" + }; + CliOption maxFeePerBlobGasOption = new("--maxfeeperblobgas") + { + Description = "Set the maximum fee per blob data", + HelpName = "fee" + }; + CliOption feeMultiplierOption = new("--feemultiplier") + { + DefaultValueFactory = r => 1UL, + Description = "A multiplier to use for gas fees", + HelpName = "value" + }; + CliOption maxPriorityFeeGasOption = new("--maxpriorityfee") + { + Description = "The maximum priority fee for each transaction", + HelpName = "fee" + }; + CliOption waitOption = new("--wait") { Description = "Wait for tx inclusion" }; + + command.Add(rpcUrlOption); + command.Add(blobTxOption); + command.Add(privateKeyOption); + command.Add(privateKeyFileOption); + command.Add(receiverOption); + command.Add(maxFeePerDataGasOptionObsolete); + command.Add(maxFeePerBlobGasOption); + command.Add(feeMultiplierOption); + command.Add(maxPriorityFeeGasOption); + command.Add(waitOption); + command.SetAction((parseResult, cancellationToken) => { - string rpcUrl = rpcUrlOption.Value()!; - (int count, int blobCount, string @break)[] blobTxCounts = ParseTxOptions(blobTxOption.Value()); - PrivateKey[] privateKeys; - if (privateKeyFileOption.HasValue()) - privateKeys = File.ReadAllLines(privateKeyFileOption.Value()!).Select(k => new PrivateKey(k)).ToArray(); - else if (privateKeyOption.HasValue()) - privateKeys = [new PrivateKey(privateKeyOption.Value()!)]; + string? privateKeyFileValue = parseResult.GetValue(privateKeyFileOption); + string? privateKeyValue = parseResult.GetValue(privateKeyOption); + + if (privateKeyFileValue is not null) + privateKeys = File.ReadAllLines(privateKeyFileValue).Select(k => new PrivateKey(k)).ToArray(); + else if (privateKeyValue is not null) + privateKeys = [new PrivateKey(privateKeyValue)]; else { Console.WriteLine("Missing private key argument."); - app.ShowHelp(); - return; - } - - string receiver = receiverOption.Value()!; - - UInt256? maxFeePerBlobGas = null; - if (maxFeePerBlobGasOption.HasValue()) - { - ulong.TryParse(maxFeePerBlobGasOption.Value(), out ulong shortMaxFeePerBlobGas); - maxFeePerBlobGas = shortMaxFeePerBlobGas; - } - else if (maxFeePerDataGasOptionObsolete.HasValue()) - { - ulong.TryParse(maxFeePerDataGasOptionObsolete.Value(), out ulong shortMaxFeePerBlobGas); - maxFeePerBlobGas = shortMaxFeePerBlobGas; + return Task.CompletedTask; } - ulong feeMultiplier = 1; - if (feeMultiplierOption.HasValue()) - ulong.TryParse(feeMultiplierOption.Value(), out feeMultiplier); - - UInt256 maxPriorityFeeGasArgs = 0; - if (maxPriorityFeeGasOption.HasValue()) UInt256.TryParse(maxPriorityFeeGasOption.Value()!, out maxPriorityFeeGasArgs); - - bool wait = waitOption.HasValue(); + BlobSender sender = new(parseResult.GetValue(rpcUrlOption)!, SimpleConsoleLogManager.Instance); - BlobSender sender = new(rpcUrl, SimpleConsoleLogManager.Instance); - await sender.SendRandomBlobs( - blobTxCounts, + return sender.SendRandomBlobs( + ParseTxOptions(parseResult.GetValue(blobTxOption)), privateKeys, - receiver, - maxFeePerBlobGas, - feeMultiplier, - maxPriorityFeeGasArgs, - wait); + parseResult.GetValue(receiverOption)!, + parseResult.GetValue(maxFeePerBlobGasOption) ?? parseResult.GetValue(maxFeePerDataGasOptionObsolete), + parseResult.GetValue(feeMultiplierOption), + parseResult.GetValue(maxPriorityFeeGasOption), + parseResult.GetValue(waitOption)); }); } @@ -97,7 +120,6 @@ private static (int count, int blobCount, string @break)[] ParseTxOptions(string nextComma = SplitToNext(chars[offSet..], ','); ReadOnlySpan @break = SplitToNext(nextComma, '-', true); - ReadOnlySpan rest = nextComma[..(nextComma.Length - (@break.Length == 0 ? 0 : @break.Length + 1))]; ReadOnlySpan count = SplitToNext(rest, 'x'); ReadOnlySpan txCount = SplitToNext(rest, 'x', true); @@ -121,70 +143,127 @@ private static ReadOnlySpan SplitToNext(ReadOnlySpan line, char sepa return returnRemainder ? line[(i + 1)..] : line[..i]; } - public static void SetupDistributeCommand(CommandLineApplication app) + public static void SetupDistributeCommand(CliCommand root) { - app.Command("distribute", (command) => + CliCommand command = new("distribute") { - command.Description = "Distribute funds from an address to a number of new addresses."; - command.HelpOption("--help"); - - CommandOption rpcUrlOption = command.Option("--rpcurl ", "Url of the Json RPC.", CommandOptionType.SingleValue, c => c.IsRequired()); - CommandOption privateKeyOption = command.Option("--privatekey ", "The private key to distribute funds from.", CommandOptionType.SingleValue, c => c.IsRequired()); - CommandOption keyNumberOption = command.Option("--number ", "The number of new addresses/keys to make.", CommandOptionType.SingleValue); - CommandOption keyFileOption = command.Option("--keyfile ", "File where the newly generated keys are written.", CommandOptionType.SingleValue); - CommandOption maxPriorityFeeGasOption = command.Option("--maxpriorityfee ", "(Optional) The maximum priority fee for each transaction.", CommandOptionType.SingleValue); - CommandOption maxFeeOption = command.Option("--maxfee ", "(Optional) The maxFeePerGas fee paid for each transaction.", CommandOptionType.SingleValue); - - command.OnExecute(async () => - { - uint keysToMake = keyNumberOption.HasValue() ? uint.Parse(keyNumberOption.Value()!) : 0; - PrivateKey privateKey = new(privateKeyOption.Value()!); - - ILogger logger = SimpleConsoleLogManager.Instance.GetClassLogger(); - INodeManager nodeManager = InitNodeManager(rpcUrlOption.Value()!, logger); - - string? chainIdString = await nodeManager.Post("eth_chainId") ?? "1"; - ulong chainId = HexConvert.ToUInt64(chainIdString); - - Signer signer = new Signer(chainId, privateKey, SimpleConsoleLogManager.Instance); - UInt256 maxFee = maxFeeOption.HasValue() ? UInt256.Parse(maxFeeOption.Value()!) : 0; - UInt256 maxPriorityFee = maxPriorityFeeGasOption.HasValue() ? UInt256.Parse(maxPriorityFeeGasOption.Value()!) : 0; - - FundsDistributor distributor = new FundsDistributor(nodeManager, chainId, keyFileOption.Value(), SimpleConsoleLogManager.Instance); - IEnumerable hashes = await distributor.DitributeFunds(signer, keysToMake, maxFee, maxPriorityFee); - }); + Description = "Distribute funds from an address to a number of new addresses" + }; + CliOption rpcUrlOption = new("--rpcurl") + { + Description = "The URL of the JSON RPC server", + HelpName = "URL", + Required = true + }; + CliOption privateKeyOption = new("--privatekey") + { + Description = "The private key to distribute funds from", + HelpName = "key", + Required = true, + }; + CliOption keyNumberOption = new("--number") + { + Description = "The number of new addresses/keys to make", + HelpName = "value" + }; + CliOption keyFileOption = new("--keyfile") + { + Description = "File where the newly generated keys are written", + HelpName = "path" + }; + CliOption maxPriorityFeeGasOption = new("--maxpriorityfee") + { + Description = "The maximum priority fee for each transaction", + HelpName = "fee" + }; + CliOption maxFeeOption = new("--maxfee") + { + Description = "The maxFeePerGas fee paid for each transaction", + HelpName = "fee" + }; + + command.Add(rpcUrlOption); + command.Add(privateKeyOption); + command.Add(keyNumberOption); + command.Add(keyFileOption); + command.Add(maxPriorityFeeGasOption); + command.Add(maxFeeOption); + command.SetAction(async (parseResult, cancellationToken) => + { + INodeManager nodeManager = InitNodeManager( + parseResult.GetValue(rpcUrlOption)!, SimpleConsoleLogManager.Instance.GetClassLogger()); + + string? chainIdString = await nodeManager.Post("eth_chainId") ?? "1"; + ulong chainId = HexConvert.ToUInt64(chainIdString); + + Signer signer = new(chainId, new PrivateKey(parseResult.GetValue(privateKeyOption)!), + SimpleConsoleLogManager.Instance); + + FundsDistributor distributor = new FundsDistributor( + nodeManager, chainId, parseResult.GetValue(keyFileOption), SimpleConsoleLogManager.Instance); + IEnumerable hashes = await distributor.DitributeFunds( + signer, + parseResult.GetValue(keyNumberOption), + parseResult.GetValue(maxFeeOption), + parseResult.GetValue(maxPriorityFeeGasOption)); }); + + root.Add(command); } - public static void SetupReclaimCommand(CommandLineApplication app) + public static void SetupReclaimCommand(CliCommand root) { - app.Command("reclaim", (command) => + CliCommand command = new("reclaim") { - command.Description = "Reclaim funds distributed from the 'distribute' command."; - command.HelpOption("--help"); - - CommandOption rpcUrlOption = command.Option("--rpcurl ", "Url of the Json RPC.", CommandOptionType.SingleValue, c => c.IsRequired()); - CommandOption receiverOption = command.Option("--receiveraddress ", "The address to send the funds to.", CommandOptionType.SingleValue, c => c.IsRequired()); - CommandOption keyFileOption = command.Option("--keyfile ", "File of the private keys to reclaim from.", CommandOptionType.SingleValue); - CommandOption maxPriorityFeeGasOption = command.Option("--maxpriorityfee ", "(Optional) The maximum priority fee for each transaction.", CommandOptionType.SingleValue); - CommandOption maxFeeOption = command.Option("--maxfee ", "(Optional) The maxFeePerGas paid for each transaction.", CommandOptionType.SingleValue); - - command.OnExecute(async () => - { - INodeManager nodeManager = InitNodeManager(rpcUrlOption.Value()!, SimpleConsoleLogManager.Instance.GetClassLogger()); - - string? chainIdString = await nodeManager.Post("eth_chainId") ?? "1"; - ulong chainId = HexConvert.ToUInt64(chainIdString); - - Address beneficiary = new Address(receiverOption.Value()!); + Description = "Reclaim funds distributed from the 'distribute' command" + }; + CliOption rpcUrlOption = new("--rpcurl") + { + Description = "The URL of the JSON RPC server", + HelpName = "URL", + Required = true + }; + CliOption receiverOption = new("--receiveraddress") + { + Description = "The address to send the funds to", + HelpName = "address", + Required = true, + }; + CliOption keyFileOption = new("--keyfile") + { + Description = "File of the private keys to reclaim from", + HelpName = "path" + }; + CliOption maxPriorityFeeGasOption = new("--maxpriorityfee") + { + Description = "The maximum priority fee for each transaction", + HelpName = "fee" + }; + CliOption maxFeeOption = new("--maxfee") + { + Description = "The maxFeePerGas fee paid for each transaction", + HelpName = "fee" + }; + + command.Add(rpcUrlOption); + command.Add(keyFileOption); + command.Add(maxPriorityFeeGasOption); + command.Add(maxFeeOption); + command.SetAction(async (parseResult, cancellationToken) => + { + INodeManager nodeManager = InitNodeManager(parseResult.GetValue(rpcUrlOption)!, SimpleConsoleLogManager.Instance.GetClassLogger()); - UInt256 maxFee = maxFeeOption.HasValue() ? UInt256.Parse(maxFeeOption.Value()!) : 0; - UInt256 maxPriorityFee = maxPriorityFeeGasOption.HasValue() ? UInt256.Parse(maxPriorityFeeGasOption.Value()!) : 0; + string? chainIdString = await nodeManager.Post("eth_chainId") ?? "1"; + ulong chainId = HexConvert.ToUInt64(chainIdString); - FundsDistributor distributor = new FundsDistributor(nodeManager, chainId, keyFileOption.Value(), SimpleConsoleLogManager.Instance); - IEnumerable hashes = await distributor.ReclaimFunds(beneficiary, maxFee, maxPriorityFee); - }); + FundsDistributor distributor = new(nodeManager, chainId, parseResult.GetValue(keyFileOption), SimpleConsoleLogManager.Instance); + IEnumerable hashes = await distributor.ReclaimFunds( + new(parseResult.GetValue(receiverOption)!), + parseResult.GetValue(maxFeeOption), + parseResult.GetValue(maxPriorityFeeGasOption)); }); + + root.Add(command); } public static INodeManager InitNodeManager(string rpcUrl, ILogger logger) @@ -198,71 +277,79 @@ public static INodeManager InitNodeManager(string rpcUrl, ILogger logger) return nodeManager; } - public static void SetupSendFileCommand(CommandLineApplication app) + public static void SetupSendFileCommand(CliCommand root) { - app.Command("send", (command) => + CliCommand command = new("send") { - command.Description = "Sends a file"; - command.HelpOption("--help"); - - CommandOption fileOption = command.Option("--file ", "File to send as is.", CommandOptionType.SingleValue, c => c.IsRequired()); - CommandOption rpcUrlOption = command.Option("--rpcurl ", "Url of the Json RPC.", CommandOptionType.SingleValue, c => c.IsRequired()); - CommandOption privateKeyOption = command.Option("--privatekey ", "The key to use for sending blobs.", CommandOptionType.SingleValue); - CommandOption receiverOption = command.Option("--receiveraddress ", "Receiver address of the blobs.", CommandOptionType.SingleValue, c => c.IsRequired()); - CommandOption maxFeePerBlobGasOption = command.Option("--maxfeeperblobgas ", "(Optional) Set the maximum fee per blob data.", CommandOptionType.SingleValue); - CommandOption feeMultiplierOption = command.Option("--feemultiplier ", "(Optional) A multiplier to use for gas fees.", CommandOptionType.SingleValue); - CommandOption maxPriorityFeeGasOption = command.Option("--maxpriorityfee ", "(Optional) The maximum priority fee for each transaction.", CommandOptionType.SingleValue); - CommandOption waitOption = app.Option("--wait", "(Optional) Wait for tx inclusion.", CommandOptionType.NoValue); - - command.OnExecuteAsync(async cancellationToken => - { - string rpcUrl = rpcUrlOption.Value()!; - - PrivateKey privateKey; - - if (privateKeyOption.HasValue()) - privateKey = new PrivateKey(privateKeyOption.Value()!); - else - { - Console.WriteLine("Missing private key argument."); - app.ShowHelp(); - return; - } - - string receiver = receiverOption.Value()!; - - UInt256 maxFeePerBlobGas = 1000; - if (maxFeePerBlobGasOption.HasValue()) - { - ulong.TryParse(maxFeePerBlobGasOption.Value(), out ulong shortMaxFeePerBlobGas); - maxFeePerBlobGas = shortMaxFeePerBlobGas; - } - - ulong feeMultiplier = 1; - if (feeMultiplierOption.HasValue()) - ulong.TryParse(feeMultiplierOption.Value(), out feeMultiplier); - - UInt256? maxPriorityFeeGas = null; - if (maxPriorityFeeGasOption.HasValue() && UInt256.TryParse(maxPriorityFeeGasOption.Value()!, out UInt256 maxPriorityFeeGasParsed)) - { - maxPriorityFeeGas = maxPriorityFeeGasParsed; - } - - bool wait = waitOption.HasValue(); - - byte[] data = File.ReadAllBytes(fileOption.Value()!); - - BlobSender sender = new(rpcUrl, SimpleConsoleLogManager.Instance); - await sender.SendData( - data, - privateKey, - receiver, - maxFeePerBlobGas, - feeMultiplier, - maxPriorityFeeGas, - wait); - }); + Description = "Sends a file" + }; + CliOption fileOption = new("--file") + { + Description = "File to send as is", + HelpName = "path", + Required = true + }; + CliOption rpcUrlOption = new("--rpcurl") + { + Description = "The URL of the JSON RPC server", + HelpName = "URL", + Required = true + }; + CliOption privateKeyOption = new("--privatekey") + { + Description = "The key to use for sending blobs", + HelpName = "key", + Required = true, + }; + CliOption receiverOption = new("--receiveraddress") + { + Description = "Receiver address of the blobs", + HelpName = "address", + Required = true, + }; + CliOption maxFeePerBlobGasOption = new("--maxfeeperblobgas") + { + DefaultValueFactory = r => 1000, + Description = "Set the maximum fee per blob data", + HelpName = "fee" + }; + CliOption feeMultiplierOption = new("--feemultiplier") + { + DefaultValueFactory = r => 1UL, + Description = "A multiplier to use for gas fees", + HelpName = "value" + }; + CliOption maxPriorityFeeGasOption = new("--maxpriorityfee") + { + Description = "The maximum priority fee for each transaction", + HelpName = "fee" + }; + CliOption waitOption = new("--wait") { Description = "Wait for tx inclusion" }; + + command.Add(fileOption); + command.Add(rpcUrlOption); + command.Add(privateKeyOption); + command.Add(receiverOption); + command.Add(maxFeePerBlobGasOption); + command.Add(feeMultiplierOption); + command.Add(maxPriorityFeeGasOption); + command.Add(waitOption); + command.SetAction((parseResult, cancellationToken) => + { + PrivateKey privateKey = new(parseResult.GetValue(privateKeyOption)!); + byte[] data = File.ReadAllBytes(parseResult.GetValue(fileOption)!); + BlobSender sender = new(parseResult.GetValue(rpcUrlOption)!, SimpleConsoleLogManager.Instance); + + return sender.SendData( + data, + privateKey, + parseResult.GetValue(receiverOption)!, + parseResult.GetValue(maxFeePerBlobGasOption), + parseResult.GetValue(feeMultiplierOption), + parseResult.GetValue(maxPriorityFeeGasOption), + parseResult.GetValue(waitOption)); }); - } + root.Add(command); + } } diff --git a/tools/nuget.config b/tools/nuget.config new file mode 100644 index 00000000000..401f058f2ab --- /dev/null +++ b/tools/nuget.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + +