From 9fba89d095ae422866a37132f1f56503b8024fce Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 2 Sep 2024 10:45:15 +0900 Subject: [PATCH 1/8] feat: Add IStoreService --- .../LibplanetServicesExtensions.cs | 2 ++ .../Services/BlockChainService.cs | 34 +++---------------- .../Libplanet.Node/Services/IStoreService.cs | 13 +++++++ .../Libplanet.Node/Services/StoreService.cs | 34 +++++++++++++++++++ 4 files changed, 54 insertions(+), 29 deletions(-) create mode 100644 sdk/node/Libplanet.Node/Services/IStoreService.cs create mode 100644 sdk/node/Libplanet.Node/Services/StoreService.cs 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/Services/BlockChainService.cs b/sdk/node/Libplanet.Node/Services/BlockChainService.cs index 49c4e1c6a49..ca071b20461 100644 --- a/sdk/node/Libplanet.Node/Services/BlockChainService.cs +++ b/sdk/node/Libplanet.Node/Services/BlockChainService.cs @@ -36,7 +36,7 @@ internal sealed class BlockChainService : IBlockChainService, IActionRenderer public BlockChainService( IOptions genesisOptions, - IOptions storeOptions, + IStoreService storeService, PolicyService policyService, IEnumerable actionLoaderProviders, ILogger logger) @@ -45,7 +45,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 +92,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 +134,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/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}"), + }; +} From 45e8a7cee39d006783e3f0122eb4532019d13c1f Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 2 Sep 2024 10:46:01 +0900 Subject: [PATCH 2/8] test: Fix test failures --- .../Services/BlockChainServiceTest.cs | 8 ++++++-- sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs b/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs index dd8461585ed..55a0e817c7b 100644 --- a/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs +++ b/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs @@ -25,12 +25,14 @@ public void Create_Test() var serviceProvider = services.BuildServiceProvider(); var policyService = new PolicyService(); - var logger = new NullLoggerFactory().CreateLogger(); + var loggerFactory = new NullLoggerFactory(); + var logger = loggerFactory.CreateLogger(); var genesisOptions = serviceProvider.GetRequiredService>(); var storeOptions = serviceProvider.GetRequiredService>(); + var storeService = new StoreService(storeOptions); var blockChainService = new BlockChainService( genesisOptions: genesisOptions, - storeOptions: storeOptions, + storeService: storeService, policyService: policyService, actionLoaderProviders: [], logger: logger); @@ -52,6 +54,8 @@ public async Task BlockAppended_TestAsync() services.AddSingleton, SwarmOptionsConfigurator>(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(s => (IStoreService)s.GetRequiredService()); var serviceProvider = services.BuildServiceProvider(); var blockChainService = serviceProvider.GetRequiredService(); var blockChain = blockChainService.BlockChain; diff --git a/sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs b/sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs index f6ad2056dcd..9f2bdb19765 100644 --- a/sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs +++ b/sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs @@ -82,6 +82,8 @@ private static ServiceCollection CreateServices() services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(s => (IStoreService)s.GetRequiredService()); return services; } } From 3735e9fd268bddaec24633a352d2f3a23b2e3086 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Sep 2024 11:42:19 +0900 Subject: [PATCH 3/8] refactor: Refactor swarm service to work better with validator service --- .../Services/BlockChainService.cs | 2 -- .../Libplanet.Node/Services/ISwarmService.cs | 2 ++ .../Libplanet.Node/Services/SwarmService.cs | 31 ++++++------------- .../Services/ValidatorService.cs | 9 +++++- 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/sdk/node/Libplanet.Node/Services/BlockChainService.cs b/sdk/node/Libplanet.Node/Services/BlockChainService.cs index ca071b20461..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; 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/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 } From 0a2d216b0ff696fdb8c441ef69a19b139164a1bc Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Sep 2024 11:42:52 +0900 Subject: [PATCH 4/8] test: Add utilties for test --- .../Libplanet.Node.Tests/BlockChainUtility.cs | 33 +++++++++++++++ .../Libplanet.Node.Tests.csproj | 1 + sdk/node/Libplanet.Node.Tests/TestUtility.cs | 41 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 sdk/node/Libplanet.Node.Tests/BlockChainUtility.cs create mode 100644 sdk/node/Libplanet.Node.Tests/TestUtility.cs 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/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(); + } +} From d272915fa8290cce85839161e0c1fac20b0f3fd5 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Sep 2024 11:43:13 +0900 Subject: [PATCH 5/8] test: Add test for SwarmService --- .../Services/NodeServiceTest.cs | 89 ------------------ .../Libplanet.Node.Tests/Services/PeerTest.cs | 1 - .../Services/SwarmServiceTest.cs | 90 +++++++++++++++++++ 3 files changed, 90 insertions(+), 90 deletions(-) delete mode 100644 sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs create mode 100644 sdk/node/Libplanet.Node.Tests/Services/SwarmServiceTest.cs 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 9f2bdb19765..00000000000 --- a/sdk/node/Libplanet.Node.Tests/Services/NodeServiceTest.cs +++ /dev/null @@ -1,89 +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(); - services.AddSingleton(); - services.AddSingleton(s => (IStoreService)s.GetRequiredService()); - 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/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); + } +} From ec4a1a042e0df1cb80937eb8c3b996d5b65fdf2d Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Sep 2024 11:43:26 +0900 Subject: [PATCH 6/8] test: Add BlockChainServiceTest --- .../Services/BlockChainServiceTest.cs | 64 ++----------------- .../Services/ReadChainServiceTest.cs | 42 ++++++++++++ 2 files changed, 49 insertions(+), 57 deletions(-) create mode 100644 sdk/node/Libplanet.Node.Tests/Services/ReadChainServiceTest.cs diff --git a/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs b/sdk/node/Libplanet.Node.Tests/Services/BlockChainServiceTest.cs index 55a0e817c7b..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,28 +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 loggerFactory = new NullLoggerFactory(); - var logger = loggerFactory.CreateLogger(); - var genesisOptions = serviceProvider.GetRequiredService>(); - var storeOptions = serviceProvider.GetRequiredService>(); - var storeService = new StoreService(storeOptions); - var blockChainService = new BlockChainService( - genesisOptions: genesisOptions, - storeService: storeService, - policyService: policyService, - actionLoaderProviders: [], - logger: logger); + var serviceProvider = TestUtility.CreateServiceProvider(); + var blockChainService = serviceProvider.GetRequiredService(); var blockChain = blockChainService.BlockChain; Assert.Equal(1, blockChain.Count); @@ -44,42 +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(); - services.AddSingleton(); - services.AddSingleton(s => (IStoreService)s.GetRequiredService()); - 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/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)); + } +} From aadc735c2c2b9c8d840ee524122b6e5aeaeeed2d Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Sep 2024 11:43:37 +0900 Subject: [PATCH 7/8] test: Add ValidatorServiceTest --- .../Services/ValidatorServiceTest.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 sdk/node/Libplanet.Node.Tests/Services/ValidatorServiceTest.cs 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); + } +} From 6be41046629c9c9beffd5f41232cdc67c75edcbc Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 5 Sep 2024 15:56:15 +0900 Subject: [PATCH 8/8] test: Add StoreServiceTest --- .../Services/StoreServiceTest.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 sdk/node/Libplanet.Node.Tests/Services/StoreServiceTest.cs 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); + } +}