Skip to content

Commit

Permalink
Merge pull request #78 from s2quake/feature/node-client-content
Browse files Browse the repository at this point in the history
Client/Node ContentBase class
  • Loading branch information
s2quake authored Oct 18, 2024
2 parents b53c300 + 973a17c commit 69465c5
Show file tree
Hide file tree
Showing 16 changed files with 173 additions and 22 deletions.
15 changes: 13 additions & 2 deletions src/client/LibplanetConsole.Client/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal sealed partial class Client : IClient
private GrpcChannel? _channel;
private CancellationTokenSource? _cancellationTokenSource;
private ClientInfo _info;
private IClientContent[]? _contents;

public Client(ILogger<Client> logger, ApplicationOptions options)
{
Expand All @@ -41,6 +42,12 @@ public Client(ILogger<Client> logger, ApplicationOptions options)

public ClientInfo Info => _info;

public IClientContent[] Contents
{
get => _contents ?? throw new InvalidOperationException("Contents is not initialized.");
set => _contents = value;
}

public NodeInfo NodeInfo { get; private set; }

public EndPoint NodeEndPoint
Expand Down Expand Up @@ -104,8 +111,9 @@ public async Task StartAsync(CancellationToken cancellationToken)
Tip = nodeService.Info.Tip,
};
IsRunning = true;
_logger.LogDebug(
"Client is started: {Address} -> {NodeAddress}", Address, NodeInfo.Address);
_logger.LogDebug("Client is started: {Address}->{NodeAddress}", Address, NodeInfo.Address);
await Task.WhenAll(Contents.Select(item => item.StartAsync(cancellationToken)));
_logger.LogDebug("Client Contents are started: {Address}", Address);
Started?.Invoke(this, EventArgs.Empty);
}

Expand All @@ -116,6 +124,9 @@ public async Task StopAsync(CancellationToken cancellationToken)
throw new InvalidOperationException("The client is not running.");
}

await Task.WhenAll(Contents.Select(item => item.StopAsync(cancellationToken)));
_logger.LogDebug("Client Contents are stopped: {Address}", Address);

if (_cancellationTokenSource is not null)
{
await _cancellationTokenSource.CancelAsync();
Expand Down
43 changes: 43 additions & 0 deletions src/client/LibplanetConsole.Client/ClientContentBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace LibplanetConsole.Client;

public abstract class ClientContentBase(string name) : IClientContent, IDisposable
{
private readonly string _name = name;
private bool _isDisposed;

protected ClientContentBase()
: this(string.Empty)
{
}

public string Name => _name != string.Empty ? _name : GetType().Name;

void IDisposable.Dispose()
{
OnDispose(disposing: true);
GC.SuppressFinalize(this);
}

Task IClientContent.StartAsync(CancellationToken cancellationToken)
=> OnStartAsync(cancellationToken);

Task IClientContent.StopAsync(CancellationToken cancellationToken)
=> OnStopAsync(cancellationToken);

protected abstract Task OnStartAsync(CancellationToken cancellationToken);

protected abstract Task OnStopAsync(CancellationToken cancellationToken);

protected virtual void OnDispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
// do nothing
}

_isDisposed = true;
}
}
}
5 changes: 4 additions & 1 deletion src/client/LibplanetConsole.Client/ClientHostedService.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace LibplanetConsole.Client;

internal sealed class ClientHostedService(Client client, ApplicationOptions options)
internal sealed class ClientHostedService(
IServiceProvider serviceProvider, Client client, ApplicationOptions options)
: IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
client.Contents = [.. serviceProvider.GetServices<IClientContent>()];
if (options.NodeEndPoint is not null)
{
client.NodeEndPoint = options.NodeEndPoint;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace LibplanetConsole.Client.Commands;

[CommandSummary("Prints the address of the client.")]
internal sealed class AddressCommand(IClient client) : CommandBase
{
protected override void OnExecute() => Out.WriteLine(client.Address);
Expand Down
10 changes: 10 additions & 0 deletions src/client/LibplanetConsole.Client/IClientContent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace LibplanetConsole.Client;

public interface IClientContent
{
string Name { get; }

Task StartAsync(CancellationToken cancellationToken);

Task StopAsync(CancellationToken cancellationToken);
}
11 changes: 8 additions & 3 deletions src/console/LibplanetConsole.Console/ClientContentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ namespace LibplanetConsole.Console;
public abstract class ClientContentBase(string name) : IClientContent, IDisposable
{
private readonly string _name = name;
private bool disposedValue;
private bool _isDisposed;

protected ClientContentBase()
: this(string.Empty)
{
}

public string Name => _name != string.Empty ? _name : GetType().Name;

Expand All @@ -25,14 +30,14 @@ Task IClientContent.StopAsync(CancellationToken cancellationToken)

protected virtual void OnDispose(bool disposing)
{
if (!disposedValue)
if (!_isDisposed)
{
if (disposing)
{
// do nothing
}

disposedValue = true;
_isDisposed = true;
}
}
}
2 changes: 1 addition & 1 deletion src/console/LibplanetConsole.Console/Node.BlockChain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public async Task<TxId> SendTransactionAsync(

var address = _privateKey.Address;
var nonce = await GetNextNonceAsync(address, cancellationToken);
var genesisHash = _nodeInfo.GenesisHash;
var genesisHash = _info.GenesisHash;
var tx = Transaction.Create(
nonce: nonce,
privateKey: _privateKey,
Expand Down
20 changes: 10 additions & 10 deletions src/console/LibplanetConsole.Console/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ internal sealed partial class Node : INode
private NodeService? _nodeService;
private BlockChainService? _blockChainService;
private GrpcChannel? _channel;
private NodeInfo _nodeInfo;
private NodeInfo _info;
private bool _isDisposed;
private NodeProcess? _process;
private Task _processTask = Task.CompletedTask;
Expand Down Expand Up @@ -58,7 +58,7 @@ public Node(IServiceProvider serviceProvider, NodeOptions nodeOptions)

public EndPoint EndPoint => _nodeOptions.EndPoint;

public NodeInfo Info => _nodeInfo;
public NodeInfo Info => _info;

public INodeContent[] Contents
{
Expand Down Expand Up @@ -104,8 +104,8 @@ public async Task<NodeInfo> GetInfoAsync(CancellationToken cancellationToken)
var request = new GetInfoRequest();
var callOptions = new CallOptions(cancellationToken: cancellationToken);
var response = await _nodeService.GetInfoAsync(request, callOptions);
_nodeInfo = response.NodeInfo;
return _nodeInfo;
_info = response.NodeInfo;
return _info;
}

public async Task AttachAsync(CancellationToken cancellationToken)
Expand Down Expand Up @@ -138,8 +138,8 @@ public async Task AttachAsync(CancellationToken cancellationToken)
_channel = channel;
_nodeService = nodeService;
_blockChainService = blockChainService;
_nodeInfo = await GetInfoAsync(cancellationToken);
IsRunning = _nodeInfo.IsRunning;
_info = await GetInfoAsync(cancellationToken);
IsRunning = _info.IsRunning;
IsAttached = true;
_logger.LogDebug("Node is attached: {Address}", Address);
Attached?.Invoke(this, EventArgs.Empty);
Expand Down Expand Up @@ -199,7 +199,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
};
var callOptions = new CallOptions(cancellationToken: cancellationToken);
var response = await _nodeService.StartAsync(request, callOptions);
_nodeInfo = response.NodeInfo;
_info = response.NodeInfo;
IsRunning = true;
_logger.LogDebug("Node is started: {Address}", Address);
await Task.WhenAll(Contents.Select(item => item.StartAsync(cancellationToken)));
Expand All @@ -226,7 +226,7 @@ public async Task StopAsync(CancellationToken cancellationToken)
var request = new StopRequest();
var callOptions = new CallOptions(cancellationToken: cancellationToken);
await _nodeService.StopAsync(request, callOptions);
_nodeInfo = NodeInfo.Empty;
_info = NodeInfo.Empty;
IsRunning = false;
_logger.LogDebug("Node is stopped: {Address}", Address);
Stopped?.Invoke(this, EventArgs.Empty);
Expand Down Expand Up @@ -296,7 +296,7 @@ public Task StartProcessAsync(AddNewNodeOptions options, CancellationToken cance

private void NodeService_Started(object? sender, NodeEventArgs e)
{
_nodeInfo = e.NodeInfo;
_info = e.NodeInfo;
IsRunning = true;
Started?.Invoke(this, EventArgs.Empty);
}
Expand All @@ -308,7 +308,7 @@ private void NodeService_Stopped(object? sender, EventArgs e)

private void BlockChainService_BlockAppended(object? sender, BlockEventArgs e)
{
_nodeInfo = _nodeInfo with { Tip = e.BlockInfo };
_info = _info with { Tip = e.BlockInfo };
BlockAppended?.Invoke(this, e);
}

Expand Down
11 changes: 8 additions & 3 deletions src/console/LibplanetConsole.Console/NodeContentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ namespace LibplanetConsole.Console;
public abstract class NodeContentBase(string name) : INodeContent, IDisposable
{
private readonly string _name = name;
private bool disposedValue;
private bool _isDisposed;

protected NodeContentBase()
: this(string.Empty)
{
}

public string Name => _name != string.Empty ? _name : GetType().Name;

Expand All @@ -25,14 +30,14 @@ Task INodeContent.StopAsync(CancellationToken cancellationToken)

protected virtual void OnDispose(bool disposing)
{
if (!disposedValue)
if (!_isDisposed)
{
if (disposing)
{
// do nothing
}

disposedValue = true;
_isDisposed = true;
}
}
}
10 changes: 9 additions & 1 deletion src/node/LibplanetConsole.Node.Evidence/Evidence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

namespace LibplanetConsole.Node.Evidence;

internal sealed class Evidence(INode node) : IEvidence, IAsyncDisposable
internal sealed class Evidence(INode node) : IEvidence, INodeContent, IAsyncDisposable
{
private readonly DuplicateVoteViolator _duplicateVotePerpetrator = new(node);

public string Name => nameof(Evidence);

public async Task<EvidenceInfo> AddEvidenceAsync(CancellationToken cancellationToken)
{
var blockChain = node.GetRequiredService<BlockChain>();
Expand Down Expand Up @@ -85,6 +87,12 @@ public async ValueTask DisposeAsync()
await _duplicateVotePerpetrator.DisposeAsync();
}

public Task StartAsync(CancellationToken cancellationToken)
=> Task.CompletedTask;

public Task StopAsync(CancellationToken cancellationToken)
=> Task.CompletedTask;

#if LIBPLANET_DPOS
public async Task UnjailAsync(CancellationToken cancellationToken)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public static class ServiceCollectionExtensions
public static IServiceCollection AddEvidence(this IServiceCollection @this)
{
@this.AddSingleton<Evidence>()
.AddSingleton<INodeContent>(s => s.GetRequiredService<Evidence>())
.AddSingleton<IEvidence>(s => s.GetRequiredService<Evidence>());
@this.AddSingleton<ICommand, EvidenceCommand>();
return @this;
Expand Down
1 change: 1 addition & 0 deletions src/node/LibplanetConsole.Node/Commands/AddressCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace LibplanetConsole.Node.Commands;

[CommandSummary("Prints the address of the node.")]
internal sealed class AddressCommand(INode node) : CommandBase
{
protected override void OnExecute() => Out.WriteLine(node.Address);
Expand Down
10 changes: 10 additions & 0 deletions src/node/LibplanetConsole.Node/INodeContent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace LibplanetConsole.Node;

public interface INodeContent
{
string Name { get; }

Task StartAsync(CancellationToken cancellationToken);

Task StopAsync(CancellationToken cancellationToken);
}
12 changes: 12 additions & 0 deletions src/node/LibplanetConsole.Node/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ private readonly SynchronizationContext _synchronizationContext
private EndPoint? _seedEndPoint;
private Swarm? _swarm;
private Task _startTask = Task.CompletedTask;
private INodeContent[]? _contents;
private bool _isDisposed;

public Node(IServiceProvider serviceProvider, ApplicationOptions options)
Expand Down Expand Up @@ -73,6 +74,12 @@ public Node(IServiceProvider serviceProvider, ApplicationOptions options)

public NodeInfo Info { get; private set; } = NodeInfo.Empty;

public INodeContent[] Contents
{
get => _contents ?? throw new InvalidOperationException("Contents is not initialized.");
set => _contents = value;
}

public Swarm Swarm => _swarm ?? throw new InvalidOperationException();

public BoundPeer[] Peers
Expand Down Expand Up @@ -187,6 +194,8 @@ var swarmTransport
IsRunning = true;
UpdateNodeInfo();
_logger.LogDebug("Node is started: {Address}", Address);
await Task.WhenAll(Contents.Select(item => item.StartAsync(cancellationToken)));
_logger.LogDebug("Node Contents are started: {Address}", Address);
Started?.Invoke(this, EventArgs.Empty);
}

Expand All @@ -198,6 +207,9 @@ public async Task StopAsync(CancellationToken cancellationToken)
throw new InvalidOperationException("Node is not running.");
}

await Task.WhenAll(Contents.Select(item => item.StopAsync(cancellationToken)));
_logger.LogDebug("Node Contents are stopped: {Address}", Address);

if (_swarm is not null)
{
await _swarm.StopAsync(cancellationToken: cancellationToken);
Expand Down
Loading

0 comments on commit 69465c5

Please sign in to comment.