diff --git a/src/client/LibplanetConsole.Clients.Executable/EntryCommands/StartCommand.cs b/src/client/LibplanetConsole.Clients.Executable/EntryCommands/StartCommand.cs index 6ff0c227..814e075c 100644 --- a/src/client/LibplanetConsole.Clients.Executable/EntryCommands/StartCommand.cs +++ b/src/client/LibplanetConsole.Clients.Executable/EntryCommands/StartCommand.cs @@ -1,5 +1,6 @@ using System.ComponentModel; using JSSoft.Commands; +using LibplanetConsole.DataAnnotations; using LibplanetConsole.Frameworks; using LibplanetConsole.Settings; @@ -11,7 +12,9 @@ internal sealed class StartCommand : CommandAsyncBase private static readonly ApplicationSettingsCollection _settingsCollection = new(); [CommandPropertyRequired] - public string SettingsPath { get; set; } = string.Empty; + [CommandSummary("The path of the repository.")] + [Path(Type = PathType.Directory, ExistsType = PathExistsType.Exist)] + public string RepositoryPath { get; set; } = string.Empty; [CommandProperty("parent")] [CommandSummary("Reserved option used by libplanet-console.")] @@ -26,8 +29,9 @@ protected override async Task OnExecuteAsync(CancellationToken cancellationToken { try { + var settingsPath = Path.Combine(RepositoryPath, Repository.SettingsFileName); var components = _settingsCollection.ToArray(); - var applicationSettings = Load(SettingsPath) with + var applicationSettings = Load(settingsPath) with { ParentProcessId = ParentProcessId, NoREPL = NoREPL, diff --git a/src/console/LibplanetConsole.Consoles/NodeRepositoryProcess.cs b/src/console/LibplanetConsole.Consoles/NodeRepositoryProcess.cs index 1a8b65af..43fe924f 100644 --- a/src/console/LibplanetConsole.Consoles/NodeRepositoryProcess.cs +++ b/src/console/LibplanetConsole.Consoles/NodeRepositoryProcess.cs @@ -22,6 +22,5 @@ internal sealed class NodeRepositoryProcess : NodeProcessBase AppEndPoint.ToString(EndPoint), "--genesis-path", GenesisPath, - "--no-genesis", ]; } diff --git a/src/node/LibplanetConsole.Nodes.Executable/Application.cs b/src/node/LibplanetConsole.Nodes.Executable/Application.cs index 6a031b26..cf4c07b4 100644 --- a/src/node/LibplanetConsole.Nodes.Executable/Application.cs +++ b/src/node/LibplanetConsole.Nodes.Executable/Application.cs @@ -50,7 +50,7 @@ protected override async ValueTask OnDisposeAsync() private static bool GetStartupCondition(ApplicationOptions options) { - if (options.SeedEndPoint is not null || options.IsSingleNode is true) + if (options.SeedEndPoint is not null) { return false; } diff --git a/src/node/LibplanetConsole.Nodes.Executable/ApplicationSettings.cs b/src/node/LibplanetConsole.Nodes.Executable/ApplicationSettings.cs index 1aeb30bd..3c34fa4c 100644 --- a/src/node/LibplanetConsole.Nodes.Executable/ApplicationSettings.cs +++ b/src/node/LibplanetConsole.Nodes.Executable/ApplicationSettings.cs @@ -7,6 +7,7 @@ using LibplanetConsole.Common.DataAnnotations; using LibplanetConsole.DataAnnotations; using LibplanetConsole.Frameworks; +using LibplanetConsole.Seeds; namespace LibplanetConsole.Nodes.Executable; @@ -85,17 +86,26 @@ public ApplicationOptions ToOptions(object[] components) return new ApplicationOptions(endPoint, privateKey, genesis) { ParentProcessId = ParentProcessId, - SeedEndPoint = AppEndPoint.ParseOrDefault(SeedEndPoint), + SeedEndPoint = GetSeedEndPoint(), StorePath = GetFullPath(StorePath), LogPath = GetFullPath(LogPath), LibraryLogPath = GetFullPath(LibraryLogPath), NoREPL = NoREPL, - IsSingleNode = IsSingleNode, Components = components, }; static string GetFullPath(string path) => path != string.Empty ? Path.GetFullPath(path) : path; + + AppEndPoint? GetSeedEndPoint() + { + if (SeedEndPoint != string.Empty) + { + return AppEndPoint.Parse(SeedEndPoint); + } + + return IsSingleNode is true ? endPoint : null; + } } private static byte[] CreateGenesis(AppPrivateKey privateKey) diff --git a/src/node/LibplanetConsole.Nodes.Executable/EntryCommands/InitializeCommand.cs b/src/node/LibplanetConsole.Nodes.Executable/EntryCommands/InitializeCommand.cs index 67c06414..89ad13f2 100644 --- a/src/node/LibplanetConsole.Nodes.Executable/EntryCommands/InitializeCommand.cs +++ b/src/node/LibplanetConsole.Nodes.Executable/EntryCommands/InitializeCommand.cs @@ -55,19 +55,18 @@ public InitializeCommand() [CommandProperty] [CommandSummary("The file path of the genesis." + "If omitted, the 'genesis' file is used.")] - [Path(Type = PathType.File, ExistsType = PathExistsType.NotExistOrEmpty, AllowEmpty = true)] + [Path(Type = PathType.File, AllowEmpty = true)] public string GenesisPath { get; set; } = string.Empty; - [CommandPropertySwitch] + [CommandPropertySwitch("single-node")] [CommandSummary("If set, the genesis is not stored at the specified genesis path.")] - [Category("Genesis")] - public bool NoGenesis { get; set; } + public bool IsSingleNode { get; set; } [CommandProperty] [CommandSummary("The private key of the genesis block. " + "if omitted, a random private key is used.\n" + "Mutually exclusive with '--no-genesis' option.")] - [CommandPropertyExclusion(nameof(NoGenesis))] + [CommandPropertyDependency(nameof(IsSingleNode))] [AppPrivateKey] [Category("Genesis")] public string GenesisKey { get; set; } = string.Empty; @@ -76,7 +75,7 @@ public InitializeCommand() [CommandSummary("The timestamp of the genesis block. ex) \"2021-01-01T00:00:00Z\"\n" + "Mutually exclusive with '--no-genesis' option.")] [Category("Genesis")] - [CommandPropertyExclusion(nameof(NoGenesis))] + [CommandPropertyDependency(nameof(IsSingleNode))] public DateTimeOffset DateTimeOffset { get; set; } [CommandPropertySwitch("quiet", 'q')] @@ -100,6 +99,7 @@ protected override void OnExecute() LogPath = logPath, LibraryLogPath = libraryLogPath, GenesisPath = genesisPath, + SeedEndPoint = IsSingleNode is true ? endPoint : null, }; dynamic info = repository.Save(outputPath); using var writer = new ConditionalTextWriter(Out) @@ -107,7 +107,7 @@ protected override void OnExecute() Condition = Quiet is false, }; - if (NoGenesis is false) + if (IsSingleNode is true) { var genesisKey = AppPrivateKey.ParseOrRandom(GenesisKey); var validatorKeys = new AppPublicKey[] { privateKey.PublicKey }; diff --git a/src/node/LibplanetConsole.Nodes.Executable/EntryCommands/StartCommand.cs b/src/node/LibplanetConsole.Nodes.Executable/EntryCommands/StartCommand.cs index 7b55ede3..15769308 100644 --- a/src/node/LibplanetConsole.Nodes.Executable/EntryCommands/StartCommand.cs +++ b/src/node/LibplanetConsole.Nodes.Executable/EntryCommands/StartCommand.cs @@ -12,8 +12,9 @@ internal sealed class StartCommand : CommandAsyncBase private static readonly ApplicationSettingsCollection _settingsCollection = new(); [CommandPropertyRequired] - [Path(Type = PathType.File, ExistsType = PathExistsType.Exist)] - public string SettingsPath { get; set; } = string.Empty; + [CommandSummary("The path of the repository.")] + [Path(Type = PathType.Directory, ExistsType = PathExistsType.Exist)] + public string RepositoryPath { get; set; } = string.Empty; [CommandProperty("parent")] [CommandSummary("Reserved option used by libplanet-console.")] @@ -33,7 +34,7 @@ protected override async Task OnExecuteAsync(CancellationToken cancellationToken { try { - var settingsPath = Path.GetFullPath(SettingsPath); + var settingsPath = Path.Combine(RepositoryPath, Repository.SettingsFileName); var components = _settingsCollection.ToArray(); var applicationSettings = Load(settingsPath) with { diff --git a/src/node/LibplanetConsole.Nodes/ApplicationBase.cs b/src/node/LibplanetConsole.Nodes/ApplicationBase.cs index 1ce86144..1abd3d61 100644 --- a/src/node/LibplanetConsole.Nodes/ApplicationBase.cs +++ b/src/node/LibplanetConsole.Nodes/ApplicationBase.cs @@ -45,7 +45,6 @@ protected ApplicationBase(ApplicationOptions options) SeedEndPoint = options.SeedEndPoint, StorePath = options.StorePath, LogPath = options.LogPath, - IsSingleNode = options.IsSingleNode, ParentProcessId = options.ParentProcessId, }; ApplicationServices = new(_container.GetExportedValues()); @@ -125,10 +124,5 @@ private async Task AutoStartAsync(CancellationToken cancellationToken) _node.SeedEndPoint = seedEndPoint; await _node.StartAsync(cancellationToken); } - else if (_info.IsSingleNode is true) - { - _node.SeedEndPoint = _info.EndPoint; - await _node.StartAsync(cancellationToken); - } } } diff --git a/src/node/LibplanetConsole.Nodes/ApplicationInfo.cs b/src/node/LibplanetConsole.Nodes/ApplicationInfo.cs index d58c120e..78c69bff 100644 --- a/src/node/LibplanetConsole.Nodes/ApplicationInfo.cs +++ b/src/node/LibplanetConsole.Nodes/ApplicationInfo.cs @@ -12,7 +12,5 @@ public readonly record struct ApplicationInfo public required string LogPath { get; init; } - public bool IsSingleNode { get; init; } - public int ParentProcessId { get; init; } } diff --git a/src/node/LibplanetConsole.Nodes/ApplicationOptions.cs b/src/node/LibplanetConsole.Nodes/ApplicationOptions.cs index c326008a..2d552cb2 100644 --- a/src/node/LibplanetConsole.Nodes/ApplicationOptions.cs +++ b/src/node/LibplanetConsole.Nodes/ApplicationOptions.cs @@ -29,7 +29,5 @@ public ApplicationOptions(AppEndPoint endPoint, AppPrivateKey privateKey, byte[] public bool NoREPL { get; init; } - public bool IsSingleNode { get; init; } - public object[] Components { get; init; } = []; } diff --git a/src/node/LibplanetConsole.Nodes/Node.cs b/src/node/LibplanetConsole.Nodes/Node.cs index 595a9ffe..f1e1bd6c 100644 --- a/src/node/LibplanetConsole.Nodes/Node.cs +++ b/src/node/LibplanetConsole.Nodes/Node.cs @@ -49,8 +49,7 @@ private readonly SynchronizationContext _synchronizationContext public Node(IServiceProvider serviceProvider, ApplicationOptions options, ILogger logger) { _serviceProvider = serviceProvider; - _seedEndPoint = options.SeedEndPoint - ?? (options.IsSingleNode is true ? options.EndPoint : null); + _seedEndPoint = options.SeedEndPoint; _privateKey = options.PrivateKey.ToSecureString(); _storePath = options.StorePath; PublicKey = options.PrivateKey.PublicKey; diff --git a/src/node/LibplanetConsole.Nodes/Services/SeedService.cs b/src/node/LibplanetConsole.Nodes/Services/SeedService.cs index 3d7dd61d..a3c7476b 100644 --- a/src/node/LibplanetConsole.Nodes/Services/SeedService.cs +++ b/src/node/LibplanetConsole.Nodes/Services/SeedService.cs @@ -53,7 +53,7 @@ async ValueTask IAsyncDisposable.DisposeAsync() async Task IApplicationService.InitializeAsync( IServiceProvider serviceProvider, CancellationToken cancellationToken) { - if (application.Info.IsSingleNode is true) + if (application.Info.SeedEndPoint == application.Info.EndPoint) { _blocksyncSeed = new Seed(new() {