Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IBlockChain, IBlockChainService #63

Merged
merged 3 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.ComponentModel.Composition;
using JSSoft.Terminals;
using LibplanetConsole.Common.Extensions;
using LibplanetConsole.Frameworks;

namespace LibplanetConsole.Clients.Executable.Tracers;

[Export(typeof(IApplicationService))]
[method: ImportingConstructor]
internal sealed class BlockChainEventTracer(IBlockChain blockChain)
: IApplicationService, IDisposable
{
public Task InitializeAsync(
IServiceProvider serviceProvider, CancellationToken cancellationToken)
{
blockChain.BlockAppended += BlockChain_BlockAppended;
return Task.CompletedTask;
}

void IDisposable.Dispose()
{
blockChain.BlockAppended -= BlockChain_BlockAppended;
}

private void BlockChain_BlockAppended(object? sender, BlockEventArgs e)
{
var blockInfo = e.BlockInfo;
var hash = blockInfo.Hash;
var miner = blockInfo.Miner;
var message = $"Block #{blockInfo.Height} '{hash:S}' Appended by '{miner:S}'";
Console.Out.WriteColoredLine(message, TerminalColorType.BrightGreen);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,25 @@
using LibplanetConsole.Common.Extensions;
using LibplanetConsole.Frameworks;

namespace LibplanetConsole.Clients.Executable;
namespace LibplanetConsole.Clients.Executable.Tracers;

[Export(typeof(IApplicationService))]
[method: ImportingConstructor]
internal sealed class ClientEventTracer(IClient client)
: IApplicationService
: IApplicationService, IDisposable
{
public Task InitializeAsync(
IServiceProvider serviceProvider, CancellationToken cancellationToken)
{
client.BlockAppended += Client_BlockAppended;
client.Started += Client_Started;
client.Stopped += Client_Stopped;
return Task.CompletedTask;
}

public ValueTask DisposeAsync()
void IDisposable.Dispose()
{
client.BlockAppended -= Client_BlockAppended;
client.Started -= Client_Started;
client.Stopped -= Client_Stopped;
return ValueTask.CompletedTask;
}

private void Client_BlockAppended(object? sender, BlockEventArgs e)
{
var blockInfo = e.BlockInfo;
var hash = blockInfo.Hash;
var miner = blockInfo.Miner;
var message = $"Block #{blockInfo.Index} '{hash:S}' Appended by '{miner:S}'";
Console.Out.WriteColoredLine(message, TerminalColorType.BrightGreen);
}

private void Client_Started(object? sender, EventArgs e)
Expand Down
1 change: 1 addition & 0 deletions src/client/LibplanetConsole.Clients/ApplicationBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protected ApplicationBase(ApplicationOptions options)
_container.ComposeExportedValue<IServiceProvider>(this);
_container.ComposeExportedValue(_client);
_container.ComposeExportedValue<IClient>(_client);
_container.ComposeExportedValue<IBlockChain>(_client);
_clientServiceContext = _container.GetValue<ClientServiceContext>();
_clientServiceContext.EndPoint = options.EndPoint;
_container.GetValue<IApplicationConfigurations>();
Expand Down
79 changes: 79 additions & 0 deletions src/client/LibplanetConsole.Clients/Client.BlockChain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using Bencodex;
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Crypto;
using Libplanet.Types.Blocks;
using Libplanet.Types.Tx;
using LibplanetConsole.Common;
using LibplanetConsole.Nodes;

namespace LibplanetConsole.Clients;

internal sealed partial class Client : IBlockChain
{
private static readonly Codec _codec = new();

public async Task<AppId> SendTransactionAsync(
IAction[] actions, CancellationToken cancellationToken)
{
var privateKey = AppPrivateKey.FromSecureString(_privateKey);
var address = privateKey.Address;
var nonce = await RemoteBlockChainService.GetNextNonceAsync(address, cancellationToken);
var genesisHash = NodeInfo.GenesisHash;
var tx = Transaction.Create(
nonce: nonce,
privateKey: (PrivateKey)privateKey,
genesisHash: (BlockHash)genesisHash,
actions: [.. actions.Select(item => item.PlainValue)]);
var txData = tx.Serialize();
_logger.Debug("Client sends a transaction: {AppId}", tx.Id);
return await RemoteBlockChainService.SendTransactionAsync(txData, cancellationToken);
}

public Task<AppHash> GetBlockHashAsync(long height, CancellationToken cancellationToken)
=> RemoteBlockChainService.GetBlockHashAsync(height, cancellationToken);

public Task<long> GetNextNonceAsync(AppAddress address, CancellationToken cancellationToken)
=> RemoteBlockChainService.GetNextNonceAsync(address, cancellationToken);

public Task<AppHash> GetTipHashAsync(CancellationToken cancellationToken)
=> RemoteBlockChainService.GetTipHashAsync(cancellationToken);

public async Task<IValue> GetStateAsync(
AppHash blockHash,
AppAddress accountAddress,
AppAddress address,
CancellationToken cancellationToken)
{
var value = await RemoteBlockChainService.GetStateAsync(
blockHash, accountAddress, address, cancellationToken);
return _codec.Decode(value);
}

public async Task<IValue> GetStateByStateRootHashAsync(
AppHash stateRootHash,
AppAddress accountAddress,
AppAddress address,
CancellationToken cancellationToken)
{
var value = await RemoteBlockChainService.GetStateByStateRootHashAsync(
stateRootHash, accountAddress, address, cancellationToken);
return _codec.Decode(value);
}

public async Task<T> GetActionAsync<T>(
AppId txId, int actionIndex, CancellationToken cancellationToken)
where T : IAction
{
var bytes = await RemoteBlockChainService.GetActionAsync(
txId, actionIndex, cancellationToken);
var value = _codec.Decode(bytes);
if (Activator.CreateInstance(typeof(T)) is T action)
{
action.LoadPlainValue(value);
return action;
}

throw new InvalidOperationException("Action not found.");
}
}
29 changes: 6 additions & 23 deletions src/client/LibplanetConsole.Clients/Client.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
using System.Security;
using Libplanet.Action;
using Libplanet.Crypto;
using Libplanet.Types.Blocks;
using Libplanet.Types.Tx;
using LibplanetConsole.Clients.Serializations;
using LibplanetConsole.Clients.Services;
using LibplanetConsole.Common;
using LibplanetConsole.Common.Extensions;
using LibplanetConsole.Nodes.Serializations;
using LibplanetConsole.Nodes;
using LibplanetConsole.Nodes.Services;
using Serilog;

namespace LibplanetConsole.Clients;

internal sealed class Client : IClient, INodeCallback
internal sealed partial class Client : IClient, INodeCallback, IBlockChainCallback
{
private readonly ApplicationBase _application;
private readonly SecureString _privateKey;
Expand Down Expand Up @@ -70,6 +66,9 @@ public AppEndPoint NodeEndPoint

private INodeService RemoteNodeService => _application.GetService<RemoteNodeService>().Service;

private IBlockChainService RemoteBlockChainService
=> _application.GetService<RemoteBlockChainService>().Service;

public override string ToString() => $"[{Address}]";

public bool Verify(object obj, byte[] signature) => PublicKey.Verify(obj, signature);
Expand Down Expand Up @@ -109,22 +108,6 @@ public async Task StopAsync(CancellationToken cancellationToken)
Stopped?.Invoke(this, new(StopReason.None));
}

public async Task<AppId> SendTransactionAsync(
IAction[] actions, CancellationToken cancellationToken)
{
var privateKey = AppPrivateKey.FromSecureString(_privateKey);
var address = privateKey.Address;
var nonce = await RemoteNodeService.GetNextNonceAsync(address, cancellationToken);
var genesisHash = NodeInfo.GenesisHash;
var tx = Transaction.Create(
nonce: nonce,
privateKey: (PrivateKey)privateKey,
genesisHash: (BlockHash)genesisHash,
actions: [.. actions.Select(item => item.PlainValue)]);
_logger.Debug("Client sends a transaction: {AppId}", tx.Id);
return await RemoteNodeService.SendTransactionAsync(tx.Serialize(), cancellationToken);
}

public void InvokeNodeStartedEvent(NodeInfo nodeInfo)
{
NodeInfo = nodeInfo;
Expand Down Expand Up @@ -162,7 +145,7 @@ void INodeCallback.OnStopped()
NodeInfo = NodeInfo.Empty;
}

void INodeCallback.OnBlockAppended(BlockInfo blockInfo)
void IBlockChainCallback.OnBlockAppended(BlockInfo blockInfo)
{
BlockAppended?.Invoke(this, new BlockEventArgs(blockInfo));
}
Expand Down
4 changes: 2 additions & 2 deletions src/client/LibplanetConsole.Clients/Commands/TxCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace LibplanetConsole.Clients.Commands;
[Export(typeof(ICommand))]
[method: ImportingConstructor]
[CommandSummary("Sends a transaction to store simple string.")]
internal sealed class TxCommand(IClient client) : CommandAsyncBase
internal sealed class TxCommand(IClient client, IBlockChain blockChain) : CommandAsyncBase
{
[CommandPropertyRequired]
public string Text { get; set; } = string.Empty;
Expand All @@ -19,7 +19,7 @@ protected override async Task OnExecuteAsync(
{
Value = Text,
};
await client.SendTransactionAsync([action], cancellationToken);
await blockChain.SendTransactionAsync([action], cancellationToken);
await Out.WriteLineAsync($"{client.Address:S}: {Text}");
}
}
33 changes: 33 additions & 0 deletions src/client/LibplanetConsole.Clients/IBlockChain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Bencodex.Types;
using Libplanet.Action;
using LibplanetConsole.Common;

namespace LibplanetConsole.Clients;

public interface IBlockChain
{
event EventHandler<BlockEventArgs>? BlockAppended;

Task<AppId> SendTransactionAsync(IAction[] actions, CancellationToken cancellationToken);

Task<long> GetNextNonceAsync(AppAddress address, CancellationToken cancellationToken);

Task<AppHash> GetTipHashAsync(CancellationToken cancellationToken);

Task<IValue> GetStateAsync(
AppHash blockHash,
AppAddress accountAddress,
AppAddress address,
CancellationToken cancellationToken);

Task<IValue> GetStateByStateRootHashAsync(
AppHash stateRootHash,
AppAddress accountAddress,
AppAddress address,
CancellationToken cancellationToken);

Task<AppHash> GetBlockHashAsync(long height, CancellationToken cancellationToken);

Task<T> GetActionAsync<T>(AppId txId, int actionIndex, CancellationToken cancellationToken)
where T : IAction;
}
4 changes: 1 addition & 3 deletions src/client/LibplanetConsole.Clients/IClient.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using Libplanet.Action;
using LibplanetConsole.Clients.Serializations;
using LibplanetConsole.Common;
using LibplanetConsole.Nodes.Serializations;
using LibplanetConsole.Nodes;

namespace LibplanetConsole.Clients;

public interface IClient : IVerifier
{
event EventHandler<BlockEventArgs>? BlockAppended;

event EventHandler? Started;

event EventHandler<StopEventArgs>? Stopped;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.ComponentModel.Composition;
using LibplanetConsole.Common.Services;
using LibplanetConsole.Nodes.Services;

namespace LibplanetConsole.Clients.Services;

[Export]
[Export(typeof(IRemoteService))]
[method: ImportingConstructor]
internal sealed class RemoteBlockChainService(Client client)
: RemoteService<IBlockChainService, IBlockChainCallback>(client)
{
}
2 changes: 2 additions & 0 deletions src/common/LibplanetConsole.Common/AppAddress.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
using System.Text;
Expand All @@ -8,6 +9,7 @@
namespace LibplanetConsole.Common;

[JsonConverter(typeof(AppAddressJsonConverter))]
[TypeConverter(typeof(AppAddressConverter))]
public readonly record struct AppAddress : IFormattable
{
private readonly Address _address;
Expand Down
8 changes: 7 additions & 1 deletion src/common/LibplanetConsole.Common/AppHash.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
using System.Text.Json.Serialization;
using Libplanet.Common;
using Libplanet.Types.Blocks;
Expand All @@ -26,7 +27,12 @@ public AppHash(ImmutableArray<byte> bytes)

public static explicit operator AppHash(BlockHash blockHash) => new(blockHash);

public static explicit operator BlockHash(AppHash id) => new(id._bytes);
public static explicit operator AppHash(HashDigest<SHA256> hashDigest)
=> new(hashDigest.ByteArray);

public static explicit operator BlockHash(AppHash hash) => new(hash._bytes);

public static explicit operator HashDigest<SHA256>(AppHash hash) => new(hash._bytes);

public static string ToString(AppHash? blockHash)
=> blockHash?.ToString() ?? string.Empty;
Expand Down
9 changes: 0 additions & 9 deletions src/common/LibplanetConsole.Common/AppPeer.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System.Net;
using System.Text.Json.Serialization;
using Libplanet.Crypto;
using Libplanet.Net;
using LibplanetConsole.Common.Converters;

namespace LibplanetConsole.Common;
Expand All @@ -15,12 +12,6 @@ public readonly struct AppPeer(AppPublicKey publicKey, AppEndPoint endPoint)

public AppAddress Address => PublicKey.Address;

public static explicit operator BoundPeer(AppPeer peer)
=> new((PublicKey)peer.PublicKey, (DnsEndPoint)peer.EndPoint);

public static explicit operator AppPeer(BoundPeer boundPeer)
=> new((AppPublicKey)boundPeer.PublicKey, (AppEndPoint)boundPeer.EndPoint);

public static string ToString(AppPeer? peer)
=> peer?.ToString() ?? string.Empty;

Expand Down
Loading