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/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/DocGen/nuget.config b/tools/nuget.config similarity index 73% rename from tools/DocGen/nuget.config rename to tools/nuget.config index 6e528901b29..401f058f2ab 100644 --- a/tools/DocGen/nuget.config +++ b/tools/nuget.config @@ -4,6 +4,7 @@ + @@ -12,5 +13,9 @@ +