diff --git a/sdk/node/Libplanet.Node.Extensions/LibplanetServicesExtensions.cs b/sdk/node/Libplanet.Node.Extensions/LibplanetServicesExtensions.cs index ae618b55222..f5210f179f4 100644 --- a/sdk/node/Libplanet.Node.Extensions/LibplanetServicesExtensions.cs +++ b/sdk/node/Libplanet.Node.Extensions/LibplanetServicesExtensions.cs @@ -41,6 +41,8 @@ public static ILibplanetNodeBuilder AddLibplanetNode( services.AddSingleton, SoloOptionsConfigurator>(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(s => (IStoreService)s.GetRequiredService()); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/sdk/node/Libplanet.Node.Tests/BlockChainUtility.cs b/sdk/node/Libplanet.Node.Tests/BlockChainUtility.cs new file mode 100644 index 00000000000..2ca17d2a828 --- /dev/null +++ b/sdk/node/Libplanet.Node.Tests/BlockChainUtility.cs @@ -0,0 +1,33 @@ +using Libplanet.Blockchain; +using Libplanet.Crypto; +using Libplanet.Types.Blocks; + +namespace Libplanet.Node.Tests; + +internal static class BlockChainUtility +{ + public static Task AppendBlockAsync(BlockChain blockChain) + => AppendBlockAsync(blockChain, new PrivateKey()); + + public static async Task AppendBlockAsync(BlockChain blockChain, PrivateKey privateKey) + { + var tip = blockChain.Tip; + var height = tip.Index + 1; + var block = blockChain.ProposeBlock( + privateKey, + blockChain.GetBlockCommit(tip.Hash)); + blockChain.Append( + block, + blockChain.GetBlockCommit(tip.Hash), + validate: false); + + while (blockChain.Tip.Index < height) + { + await Task.Delay(100); + } + + await Task.Delay(1000); + + return block; + } +} diff --git a/sdk/node/Libplanet.Node.Tests/Libplanet.Node.Tests.csproj b/sdk/node/Libplanet.Node.Tests/Libplanet.Node.Tests.csproj index 80862730f50..7f219739917 100644 --- a/sdk/node/Libplanet.Node.Tests/Libplanet.Node.Tests.csproj +++ b/sdk/node/Libplanet.Node.Tests/Libplanet.Node.Tests.csproj @@ -21,6 +21,7 @@ + diff --git a/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs b/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs index dd8461585ed..662821647cd 100644 --- a/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs +++ b/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs @@ -1,11 +1,7 @@ -using Libplanet.Blockchain; -using Libplanet.Crypto; -using Libplanet.Node.Options; +using Libplanet.Node.Extensions; using Libplanet.Node.Services; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; namespace Libplanet.Node.Tests.Services; @@ -14,26 +10,8 @@ public class BlockChainServiceTest [Fact] public void Create_Test() { - var services = new ServiceCollection(); - services.AddLogging(configure => configure.AddProvider(NullLoggerProvider.Instance)); - services.AddOptions(); - services.AddSingleton, GenesisOptionsConfigurator>(); - services.AddOptions(); - services.AddSingleton, StoreOptionsConfigurator>(); - services.AddOptions(); - services.AddSingleton, SwarmOptionsConfigurator>(); - - var serviceProvider = services.BuildServiceProvider(); - var policyService = new PolicyService(); - var logger = new NullLoggerFactory().CreateLogger(); - var genesisOptions = serviceProvider.GetRequiredService>(); - var storeOptions = serviceProvider.GetRequiredService>(); - var blockChainService = new BlockChainService( - genesisOptions: genesisOptions, - storeOptions: storeOptions, - policyService: policyService, - actionLoaderProviders: [], - logger: logger); + var serviceProvider = TestUtility.CreateServiceProvider(); + var blockChainService = serviceProvider.GetRequiredService(); var blockChain = blockChainService.BlockChain; Assert.Equal(1, blockChain.Count); @@ -42,40 +20,16 @@ public void Create_Test() [Fact] public async Task BlockAppended_TestAsync() { - var services = new ServiceCollection(); - services.AddLogging(configure => configure.AddProvider(NullLoggerProvider.Instance)); - services.AddOptions(); - services.AddSingleton, GenesisOptionsConfigurator>(); - services.AddOptions(); - services.AddSingleton, StoreOptionsConfigurator>(); - services.AddOptions(); - services.AddSingleton, SwarmOptionsConfigurator>(); - services.AddSingleton(); - services.AddSingleton(); - var serviceProvider = services.BuildServiceProvider(); - var blockChainService = serviceProvider.GetRequiredService(); + var serviceProvider = TestUtility.CreateServiceProvider(); + var blockChainService = serviceProvider.GetRequiredService(); var blockChain = blockChainService.BlockChain; var args = await Assert.RaisesAsync( handler => blockChainService.BlockAppended += handler, handler => blockChainService.BlockAppended -= handler, - async () => await AppendBlockAsync(new PrivateKey(), blockChain)); + async () => await BlockChainUtility.AppendBlockAsync(blockChain)); Assert.Equal(args.Arguments.Block, blockChain.Tip); Assert.Equal(2, blockChain.Count); } - - private static async Task AppendBlockAsync(PrivateKey privateKey, BlockChain blockChain) - { - var tip = blockChain.Tip; - var block = blockChain.ProposeBlock( - privateKey, - blockChain.GetBlockCommit(tip.Hash)); - blockChain.Append( - block, - blockChain.GetBlockCommit(tip.Hash), - validate: false); - - await Task.Delay(1000); - } } diff --git a/sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs b/sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs deleted file mode 100644 index f6ad2056dcd..00000000000 --- a/sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Libplanet.Node.Options; -using Libplanet.Node.Services; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; - -namespace Libplanet.Node.Tests.Services; - -public class NodeServiceTest -{ - [Fact] - public void Create_Test() - { - var services = CreateServices(); - var serviceProvider = services.BuildServiceProvider(); - var nodeService = serviceProvider.GetRequiredService(); - - Assert.False(nodeService.IsRunning); - } - - [Fact] - public async Task Start_TestAsync() - { - var services = CreateServices(); - var serviceProvider = services.BuildServiceProvider(); - var nodeService = serviceProvider.GetRequiredService(); - - await Assert.RaisesAnyAsync( - handler => nodeService.Started += handler, - handler => nodeService.Started -= handler, - async () => await nodeService.StartAsync(default)); - Assert.True(nodeService.IsRunning); - } - - [Fact] - public async Task Start_ThrowTestAsync() - { - var services = CreateServices(); - var serviceProvider = services.BuildServiceProvider(); - var nodeService = serviceProvider.GetRequiredService(); - await nodeService.StartAsync(default); - await Assert.ThrowsAsync( - async () => await nodeService.StartAsync(default)); - } - - [Fact] - public async Task Stop_TestAsync() - { - var services = CreateServices(); - var serviceProvider = services.BuildServiceProvider(); - var nodeService = serviceProvider.GetRequiredService(); - await nodeService.StartAsync(default); - - await Assert.RaisesAnyAsync( - handler => nodeService.Stopped += handler, - handler => nodeService.Stopped -= handler, - async () => await nodeService.StopAsync(default)); - Assert.False(nodeService.IsRunning); - } - - [Fact] - public async Task Stop_ThrowTestAsync() - { - var services = CreateServices(); - var serviceProvider = services.BuildServiceProvider(); - var nodeService = serviceProvider.GetRequiredService(); - await Assert.ThrowsAsync( - async () => await nodeService.StopAsync(default)); - } - - private static ServiceCollection CreateServices() - { - var services = new ServiceCollection(); - services.AddLogging(configure => configure.AddProvider(NullLoggerProvider.Instance)); - services.AddOptions(); - services.AddSingleton, GenesisOptionsConfigurator>(); - services.AddOptions(); - services.AddSingleton, StoreOptionsConfigurator>(); - services.AddOptions(); - services.AddSingleton, SwarmOptionsConfigurator>(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - return services; - } -} diff --git a/sdk/node/Libplanet.Node.Tests/Services/PeerTest.cs b/sdk/node/Libplanet.Node.Tests/Services/PeerTest.cs index 34d6a67bf3b..de515b4afe2 100644 --- a/sdk/node/Libplanet.Node.Tests/Services/PeerTest.cs +++ b/sdk/node/Libplanet.Node.Tests/Services/PeerTest.cs @@ -1,7 +1,6 @@ using Libplanet.Net; using Libplanet.Net.Messages; using Libplanet.Net.Transports; -using Libplanet.Node.Options; using Libplanet.Node.Services; using Moq; diff --git a/sdk/node/Libplanet.Node.Tests/Services/ReadChainServiceTest.cs b/sdk/node/Libplanet.Node.Tests/Services/ReadChainServiceTest.cs new file mode 100644 index 00000000000..706c70d8272 --- /dev/null +++ b/sdk/node/Libplanet.Node.Tests/Services/ReadChainServiceTest.cs @@ -0,0 +1,42 @@ +using Libplanet.Node.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Libplanet.Node.Tests.Services; + +public class ReadChainServiceTest +{ + [Fact] + public void Tip_Test() + { + var serviceProvider = TestUtility.CreateServiceProvider(); + var readChainService = serviceProvider.GetRequiredService(); + var expectedBlock = readChainService.GetBlock(0); + + Assert.Equal(expectedBlock, readChainService.Tip); + } + + [Fact] + public async Task GetBlock_WithHash_TestAsync() + { + var serviceProvider = TestUtility.CreateServiceProvider(); + var blockChainService = serviceProvider.GetRequiredService(); + var blockChain = blockChainService.BlockChain; + var readChainService = serviceProvider.GetRequiredService(); + var expectedBlock = await BlockChainUtility.AppendBlockAsync(blockChain); + + Assert.Equal(expectedBlock, readChainService.GetBlock(expectedBlock.Hash)); + } + + [Fact] + public async Task GetBlock_WithHeight_TestAsync() + { + var serviceProvider = TestUtility.CreateServiceProvider(); + var blockChainService = serviceProvider.GetRequiredService(); + var blockChain = blockChainService.BlockChain; + var readChainService = serviceProvider.GetRequiredService(); + var height = blockChain.Count; + var expectedBlock = await BlockChainUtility.AppendBlockAsync(blockChain); + + Assert.Equal(expectedBlock, readChainService.GetBlock(height)); + } +} diff --git a/sdk/node/Libplanet.Node.Tests/Services/StoreServiceTest.cs b/sdk/node/Libplanet.Node.Tests/Services/StoreServiceTest.cs new file mode 100644 index 00000000000..af19962d8a4 --- /dev/null +++ b/sdk/node/Libplanet.Node.Tests/Services/StoreServiceTest.cs @@ -0,0 +1,38 @@ +using Libplanet.Node.Options; +using Libplanet.Node.Services; +using Libplanet.Store; +using Libplanet.Store.Trie; +using Microsoft.Extensions.DependencyInjection; + +namespace Libplanet.Node.Tests.Services; + +public class StoreServiceTest +{ + [Fact] + public void RocksDB_Test() + { + var settings = new Dictionary + { + [$"{StoreOptions.Position}:{nameof(StoreOptions.Type)}"] = $"{StoreType.RocksDB}", + }; + var serviceProvider = TestUtility.CreateServiceProvider(settings); + var storeService = serviceProvider.GetRequiredService(); + + Assert.IsType(storeService.Store); + Assert.IsType(storeService.KeyValueStore); + } + + [Fact] + public void InMemory_Test() + { + var settings = new Dictionary + { + [$"{StoreOptions.Position}:{nameof(StoreOptions.Type)}"] = $"{StoreType.InMemory}", + }; + var serviceProvider = TestUtility.CreateServiceProvider(settings); + var storeService = serviceProvider.GetRequiredService(); + + Assert.IsType(storeService.Store); + Assert.IsType(storeService.KeyValueStore); + } +} diff --git a/sdk/node/Libplanet.Node.Tests/Services/SwarmServiceTest.cs b/sdk/node/Libplanet.Node.Tests/Services/SwarmServiceTest.cs new file mode 100644 index 00000000000..4817a9fc0bd --- /dev/null +++ b/sdk/node/Libplanet.Node.Tests/Services/SwarmServiceTest.cs @@ -0,0 +1,90 @@ +using Libplanet.Node.Options; +using Libplanet.Node.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Libplanet.Node.Tests.Services; + +public class SwarmServiceTest +{ + [Fact] + public void Enable_Test() + { + var settings = new Dictionary + { + [$"{SwarmOptions.Position}:{nameof(SwarmOptions.IsEnabled)}"] = "true", + }; + var serviceProvider = TestUtility.CreateServiceProvider(settings); + var swarmService = serviceProvider.GetRequiredService(); + Assert.NotNull(swarmService); + Assert.False(swarmService.IsRunning); + } + + [Fact] + public void Disable_ThrowTest() + { + var serviceProvider = TestUtility.CreateServiceProvider(); + Assert.ThrowsAny(serviceProvider.GetRequiredService); + } + + [Fact] + public async Task Start_TestAsync() + { + var serviceProvider = CreateServiceProviderWithSwarm(); + var swarmService = serviceProvider.GetRequiredService(); + var swarmServiceHost = serviceProvider.GetRequiredService(); + + await Assert.RaisesAnyAsync( + handler => swarmService.Started += handler, + handler => swarmService.Started -= handler, + async () => await swarmServiceHost.StartAsync(default)); + Assert.True(swarmService.IsRunning); + } + + [Fact] + public async Task Start_ThrowTestAsync() + { + var serviceProvider = CreateServiceProviderWithSwarm(); + var swarmService = serviceProvider.GetRequiredService(); + var swarmServiceHost = serviceProvider.GetRequiredService(); + + await swarmServiceHost.StartAsync(default); + await Assert.ThrowsAsync( + async () => await swarmServiceHost.StartAsync(default)); + } + + [Fact] + public async Task Stop_TestAsync() + { + var serviceProvider = CreateServiceProviderWithSwarm(); + var swarmService = serviceProvider.GetRequiredService(); + var swarmServiceHost = serviceProvider.GetRequiredService(); + await swarmServiceHost.StartAsync(default); + + await Assert.RaisesAnyAsync( + handler => swarmService.Stopped += handler, + handler => swarmService.Stopped -= handler, + async () => await swarmServiceHost.StopAsync(default)); + Assert.False(swarmService.IsRunning); + } + + [Fact] + public async Task Stop_ThrowTestAsync() + { + var serviceProvider = CreateServiceProviderWithSwarm(); + var swarmService = serviceProvider.GetRequiredService(); + var swarmServiceHost = serviceProvider.GetRequiredService(); + + await Assert.ThrowsAsync( + async () => await swarmServiceHost.StopAsync(default)); + } + + private static IServiceProvider CreateServiceProviderWithSwarm() + { + var settings = new Dictionary + { + [$"{SwarmOptions.Position}:{nameof(SwarmOptions.IsEnabled)}"] = "true", + }; + + return TestUtility.CreateServiceProvider(settings); + } +} diff --git a/sdk/node/Libplanet.Node.Tests/Services/ValidatorServiceTest.cs b/sdk/node/Libplanet.Node.Tests/Services/ValidatorServiceTest.cs new file mode 100644 index 00000000000..a81b2e4df5d --- /dev/null +++ b/sdk/node/Libplanet.Node.Tests/Services/ValidatorServiceTest.cs @@ -0,0 +1,42 @@ +using Libplanet.Node.Options; +using Libplanet.Node.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Libplanet.Node.Tests.Services; + +public class ValidatorServiceTest +{ + [Fact] + public void Enable_Test() + { + var settings = new Dictionary + { + [$"{SwarmOptions.Position}:{nameof(SwarmOptions.IsEnabled)}"] = "true", + [$"{ValidatorOptions.Position}:{nameof(ValidatorOptions.IsEnabled)}"] = "true", + }; + var serviceProvider = TestUtility.CreateServiceProvider(settings); + var validatorService = serviceProvider.GetRequiredService(); + Assert.NotNull(validatorService); + } + + [Fact] + public void Enable_WithoutSwarm_ThrowTest() + { + var settings = new Dictionary + { + [$"{ValidatorOptions.Position}:{nameof(ValidatorOptions.IsEnabled)}"] = "true", + }; + var serviceProvider = TestUtility.CreateServiceProvider(settings); + Assert.Throws( + serviceProvider.GetRequiredService); + } + + [Fact] + public void Disable_ThrowTest() + { + var serviceProvider = TestUtility.CreateServiceProvider(); + Assert.ThrowsAny( + serviceProvider.GetRequiredService); + } +} diff --git a/sdk/node/Libplanet.Node.Tests/TestUtility.cs b/sdk/node/Libplanet.Node.Tests/TestUtility.cs new file mode 100644 index 00000000000..c9227ce7aab --- /dev/null +++ b/sdk/node/Libplanet.Node.Tests/TestUtility.cs @@ -0,0 +1,41 @@ +using Libplanet.Node.Extensions; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Libplanet.Node.Tests; + +internal static class TestUtility +{ + public static IServiceProvider CreateServiceProvider() + { + var services = new ServiceCollection(); + var configuration = new ConfigurationBuilder().Build(); + services.AddSingleton(); + services.AddLogging(); + services.AddLibplanetNode(configuration); + return services.BuildServiceProvider(); + } + + public static IServiceProvider CreateServiceProvider(IConfiguration configuration) + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddLogging(); + services.AddLibplanetNode(configuration); + return services.BuildServiceProvider(); + } + + public static IServiceProvider CreateServiceProvider( + IReadOnlyDictionary settings) + { + var configuration = CreateConfiguration(settings); + return CreateServiceProvider(configuration); + } + + public static IConfiguration CreateConfiguration(IReadOnlyDictionary settings) + { + return new ConfigurationBuilder().AddInMemoryCollection(settings).Build(); + } +} diff --git a/sdk/node/Libplanet.Node/Services/BlockChainService.cs b/sdk/node/Libplanet.Node/Services/BlockChainService.cs index 49c4e1c6a49..9a6bd7fb362 100644 --- a/sdk/node/Libplanet.Node/Services/BlockChainService.cs +++ b/sdk/node/Libplanet.Node/Services/BlockChainService.cs @@ -14,9 +14,7 @@ using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Node.Options; -using Libplanet.RocksDBStore; using Libplanet.Store; -using Libplanet.Store.Trie; using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; using Libplanet.Types.Tx; @@ -36,7 +34,7 @@ internal sealed class BlockChainService : IBlockChainService, IActionRenderer public BlockChainService( IOptions genesisOptions, - IOptions storeOptions, + IStoreService storeService, PolicyService policyService, IEnumerable actionLoaderProviders, ILogger logger) @@ -45,7 +43,8 @@ public BlockChainService( _logger = logger; _blockChain = CreateBlockChain( genesisOptions: genesisOptions.Value, - storeOptions: storeOptions.Value, + store: storeService.Store, + stateStore: storeService.StateStore, stagePolicy: policyService.StagePolicy, renderers: [this], actionLoaders: [.. actionLoaderProviders.Select(item => item.GetActionLoader())]); @@ -91,12 +90,12 @@ void Action(object? state) private static BlockChain CreateBlockChain( GenesisOptions genesisOptions, - StoreOptions storeOptions, + IStore store, + IStateStore stateStore, IStagePolicy stagePolicy, IRenderer[] renderers, IActionLoader[] actionLoaders) { - var (store, stateStore) = CreateStore(storeOptions); var actionLoader = new AggregateTypedActionLoader(actionLoaders); var actionEvaluator = new ActionEvaluator( policyActionsRegistry: new(), @@ -133,31 +132,6 @@ private static BlockChain CreateBlockChain( renderers: renderers); } - private static (IStore, IStateStore) CreateStore(StoreOptions storeOptions) - { - return storeOptions.Type switch - { - StoreType.RocksDB => CreateDiskStore(), - StoreType.InMemory => CreateMemoryStore(), - _ => throw new NotSupportedException($"Unsupported store type: {storeOptions.Type}"), - }; - - (MemoryStore, TrieStateStore) CreateMemoryStore() - { - var store = new MemoryStore(); - var stateStore = new TrieStateStore(new MemoryKeyValueStore()); - return (store, stateStore); - } - - (RocksDBStore.RocksDBStore, TrieStateStore) CreateDiskStore() - { - var store = new RocksDBStore.RocksDBStore(storeOptions.StoreName); - var keyValueStore = new RocksDBKeyValueStore(storeOptions.StateStoreName); - var stateStore = new TrieStateStore(keyValueStore); - return (store, stateStore); - } - } - private static Block CreateGenesisBlock(GenesisOptions genesisOptions) { if (genesisOptions.GenesisKey != string.Empty) diff --git a/sdk/node/Libplanet.Node/Services/IStoreService.cs b/sdk/node/Libplanet.Node/Services/IStoreService.cs new file mode 100644 index 00000000000..62c60214239 --- /dev/null +++ b/sdk/node/Libplanet.Node/Services/IStoreService.cs @@ -0,0 +1,13 @@ +using Libplanet.Store; +using Libplanet.Store.Trie; + +namespace Libplanet.Node.Services; + +public interface IStoreService +{ + IStore Store { get; } + + IStateStore StateStore { get; } + + IKeyValueStore KeyValueStore { get; } +} diff --git a/sdk/node/Libplanet.Node/Services/ISwarmService.cs b/sdk/node/Libplanet.Node/Services/ISwarmService.cs index 6b47eafa44a..c2e8da534c3 100644 --- a/sdk/node/Libplanet.Node/Services/ISwarmService.cs +++ b/sdk/node/Libplanet.Node/Services/ISwarmService.cs @@ -9,4 +9,6 @@ public interface ISwarmService public event EventHandler? Stopped; Swarm Swarm { get; } + + bool IsRunning { get; } } diff --git a/sdk/node/Libplanet.Node/Services/StoreService.cs b/sdk/node/Libplanet.Node/Services/StoreService.cs new file mode 100644 index 00000000000..34911520179 --- /dev/null +++ b/sdk/node/Libplanet.Node/Services/StoreService.cs @@ -0,0 +1,34 @@ +using Libplanet.Node.Options; +using Libplanet.RocksDBStore; +using Libplanet.Store; +using Libplanet.Store.Trie; +using Microsoft.Extensions.Options; + +namespace Libplanet.Node.Services; + +internal sealed class StoreService(IOptions storeOptions) : IStoreService +{ + private IStateStore? _stateStore; + + public IStore Store { get; } = CreateStore(storeOptions.Value); + + public IKeyValueStore KeyValueStore { get; } = CreateKeyValueStore(storeOptions.Value); + + public IStateStore StateStore => _stateStore ??= new TrieStateStore(KeyValueStore); + + private static IStore CreateStore(StoreOptions storeOptions) + => storeOptions.Type switch + { + StoreType.RocksDB => new RocksDBStore.RocksDBStore(storeOptions.StoreName), + StoreType.InMemory => new MemoryStore(), + _ => throw new NotSupportedException($"Unsupported store type: {storeOptions.Type}"), + }; + + private static IKeyValueStore CreateKeyValueStore(StoreOptions storeOptions) + => storeOptions.Type switch + { + StoreType.RocksDB => new RocksDBKeyValueStore(storeOptions.StateStoreName), + StoreType.InMemory => new MemoryKeyValueStore(), + _ => throw new NotSupportedException($"Unsupported store type: {storeOptions.Type}"), + }; +} diff --git a/sdk/node/Libplanet.Node/Services/SwarmService.cs b/sdk/node/Libplanet.Node/Services/SwarmService.cs index 53930fb6c90..2be379c7625 100644 --- a/sdk/node/Libplanet.Node/Services/SwarmService.cs +++ b/sdk/node/Libplanet.Node/Services/SwarmService.cs @@ -5,7 +5,6 @@ using Libplanet.Net.Consensus; using Libplanet.Net.Transports; using Libplanet.Node.Options; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -13,13 +12,14 @@ namespace Libplanet.Node.Services; internal sealed class SwarmService( - IServiceProvider serviceProvider, + IBlockChainService blockChainService, IOptions options, + IOptions validatorOptions, ILogger logger) : IHostedService, ISwarmService, IAsyncDisposable { - private readonly IServiceProvider _serviceProvider = serviceProvider; private readonly SwarmOptions _options = options.Value; + private readonly ValidatorOptions _validatorOptions = validatorOptions.Value; private readonly ILogger _logger = logger; private Swarm? _swarm; @@ -43,8 +43,6 @@ public async Task StartAsync(CancellationToken cancellationToken) } var seedPrivateKey = new PrivateKey(); - var blockChainService = _serviceProvider.GetRequiredService(); - var validatorOptions = GetValidatorOptions(_serviceProvider); var blockChain = blockChainService.BlockChain; if (_options.BlocksyncSeedPeer == string.Empty) @@ -59,7 +57,7 @@ public async Task StartAsync(CancellationToken cancellationToken) await _blocksyncSeed.StartAsync(cancellationToken); } - if (validatorOptions is not null && validatorOptions.ConsensusSeedPeer == string.Empty) + if (_validatorOptions.ConsensusSeedPeer == string.Empty) { _consensusSeed = new Seed(new() { @@ -67,7 +65,7 @@ public async Task StartAsync(CancellationToken cancellationToken) EndPoint = EndPointUtility.ToString(EndPointUtility.Next()), AppProtocolVersion = _options.AppProtocolVersion, }); - validatorOptions.ConsensusSeedPeer = _consensusSeed.BoundPeer.PeerString; + _validatorOptions.ConsensusSeedPeer = _consensusSeed.BoundPeer.PeerString; await _consensusSeed.StartAsync(cancellationToken); } @@ -89,12 +87,12 @@ public async Task StartAsync(CancellationToken cancellationToken) }, }; - var consensusTransport = validatorOptions is not null + var consensusTransport = _validatorOptions.IsEnabled ? await CreateConsensusTransportAsync( - privateKey, appProtocolVersion, validatorOptions, cancellationToken) + privateKey, appProtocolVersion, _validatorOptions, cancellationToken) : null; - var consensusReactorOption = validatorOptions is not null - ? CreateConsensusReactorOption(privateKey, validatorOptions) + var consensusReactorOption = _validatorOptions.IsEnabled + ? CreateConsensusReactorOption(privateKey, _validatorOptions) : (ConsensusReactorOption?)null; _swarm = new Swarm( @@ -166,17 +164,6 @@ public async ValueTask DisposeAsync() } } - private static ValidatorOptions? GetValidatorOptions(IServiceProvider serviceProvider) - { - var validatorService = serviceProvider.GetService(); - if (validatorService is not null) - { - return serviceProvider.GetRequiredService>().Value; - } - - return null; - } - private static async Task CreateTransport( PrivateKey privateKey, DnsEndPoint endPoint, AppProtocolVersion appProtocolVersion) { diff --git a/sdk/node/Libplanet.Node/Services/ValidatorService.cs b/sdk/node/Libplanet.Node/Services/ValidatorService.cs index 7a59338f68d..b44faa2faa4 100644 --- a/sdk/node/Libplanet.Node/Services/ValidatorService.cs +++ b/sdk/node/Libplanet.Node/Services/ValidatorService.cs @@ -1,5 +1,12 @@ +using Libplanet.Node.Options; +using Microsoft.Extensions.Options; + namespace Libplanet.Node.Services; -internal sealed class ValidatorService : IValidatorService +internal sealed class ValidatorService(IOptions options) : IValidatorService { + // TODO: The consensus reactor settings should be implemented here in order to pass them as parameters to the Swarm constructor. +#pragma warning disable S1144 // Unused private types or members should be removed + private readonly ValidatorOptions _options = options.Value; +#pragma warning restore S1144 // Unused private types or members should be removed }