Skip to content

Commit

Permalink
feat: introduce Lib9c.StateService
Browse files Browse the repository at this point in the history
moved from `planetarium/NineChronicles.Headless`
  • Loading branch information
moreal committed Jul 27, 2023
1 parent a45946a commit 8a118ed
Show file tree
Hide file tree
Showing 33 changed files with 1,492 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .Lib9c.StateService.Shared/Lib9c.StateService.Shared.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
6 changes: 6 additions & 0 deletions .Lib9c.StateService.Shared/RemoteEvaluationRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Lib9c.StateService.Shared;

public class RemoteEvaluationRequest
{
public byte[] PreEvaluationBlock { get; set; }

Check warning on line 5 in .Lib9c.StateService.Shared/RemoteEvaluationRequest.cs

View workflow job for this annotation

GitHub Actions / build-and-test (Release)

Non-nullable property 'PreEvaluationBlock' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 5 in .Lib9c.StateService.Shared/RemoteEvaluationRequest.cs

View workflow job for this annotation

GitHub Actions / build-for-unity

Non-nullable property 'PreEvaluationBlock' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
6 changes: 6 additions & 0 deletions .Lib9c.StateService.Shared/RemoteEvaluationResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Lib9c.StateService.Shared;

public class RemoteEvaluationResponse
{
public byte[][] Evaluations { get; set; }

Check warning on line 5 in .Lib9c.StateService.Shared/RemoteEvaluationResponse.cs

View workflow job for this annotation

GitHub Actions / build-and-test (Release)

Non-nullable property 'Evaluations' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 5 in .Lib9c.StateService.Shared/RemoteEvaluationResponse.cs

View workflow job for this annotation

GitHub Actions / build-for-unity

Non-nullable property 'Evaluations' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
52 changes: 52 additions & 0 deletions .Lib9c.StateService/Controllers/RemoteEvaluationController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Bencodex;
using Bencodex.Types;
using Lib9c.StateService.Shared;
using Libplanet.Action;
using Libplanet.Action.State;
using Libplanet.Extensions.ActionEvaluatorCommonComponents;
using Microsoft.AspNetCore.Mvc;
using Nekoyume.Action;
using Nekoyume.Action.Loader;

namespace Lib9c.StateService.Controllers;

[ApiController]
[Route("/evaluation")]
public class RemoteEvaluationController : ControllerBase
{
private readonly IBlockChainStates _blockChainStates;
private readonly ILogger<RemoteEvaluationController> _logger;
private readonly Codec _codec;

public RemoteEvaluationController(
IBlockChainStates blockChainStates,
ILogger<RemoteEvaluationController> logger,
Codec codec)
{
_blockChainStates = blockChainStates;
_logger = logger;
_codec = codec;
}

[HttpPost]
public ActionResult<RemoteEvaluationResponse> GetEvaluation([FromBody] RemoteEvaluationRequest request)
{
var decoded = _codec.Decode(request.PreEvaluationBlock);
if (decoded is not Dictionary dictionary)
{
return StatusCode(StatusCodes.Status400BadRequest);
}

var preEvaluationBlock = PreEvaluationBlockMarshaller.Unmarshal(dictionary);
var actionEvaluator =
new ActionEvaluator(
context => new RewardGold(),
_blockChainStates,
new NCActionLoader());
return Ok(new RemoteEvaluationResponse
{
Evaluations = actionEvaluator.Evaluate(preEvaluationBlock).Select(ActionEvaluationMarshaller.Serialize)
.ToArray(),
});
}
}
20 changes: 20 additions & 0 deletions .Lib9c.StateService/Lib9c.StateService.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\.Libplanet.Extensions.RemoteBlockChainStates\Libplanet.Extensions.RemoteBlockChainStates.csproj" />
<ProjectReference Include="..\.Libplanet.Extensions.ActionEvaluatorCommonComponents\Libplanet.Extensions.ActionEvaluatorCommonComponents.csproj" />
<ProjectReference Include="..\.Lib9c.StateService.Shared\Lib9c.StateService.Shared.csproj" />
<ProjectReference Include="..\Lib9c\Lib9c.csproj" />
</ItemGroup>

</Project>
40 changes: 40 additions & 0 deletions .Lib9c.StateService/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Bencodex;
using Libplanet.Action.State;
using Libplanet.Extensions.RemoteBlockChainStates;

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddEnvironmentVariables();

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton<Codec>();

builder.Services.AddSingleton<IBlockChainStates, RemoteBlockChainStates>(_ =>
{
const string DefaultEndpoint = "http://localhost:31280/graphql/explorer";
var endpoint = builder.Configuration.GetValue<string>("RemoteBlockChainStatesEndpoint") ?? DefaultEndpoint;
return new RemoteBlockChainStates(new Uri(endpoint));
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
41 changes: 41 additions & 0 deletions .Lib9c.StateService/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:25712",
"sslPort": 44330
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5157",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7140;http://localhost:5157",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
11 changes: 11 additions & 0 deletions .Lib9c.StateService/appsettings-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"type": "object",
"description": "appsettings.json to configure application.",
"properties": {
"RemoteBlockChainStatesEndpoint": {
"type": "string",
"description": "The headless' Libplanet.Explorer GraphQL endpoint. (e.g., http://localhost/graphql/explorer)"
}
},
"required": ["RemoteBlockChainStatesEndpoint"]
}
8 changes: 8 additions & 0 deletions .Lib9c.StateService/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
10 changes: 10 additions & 0 deletions .Lib9c.StateService/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "./appsettings-schema.json",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Collections.Immutable;
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Crypto;

namespace Libplanet.Extensions.ActionEvaluatorCommonComponents.Tests;

public class ActionEvaluationSerializerTest
{
[Fact]
public void Serialization()
{
var addresses = Enumerable.Repeat(0, 4).Select(_ => new PrivateKey().ToAddress()).ToImmutableList();
AccountStateDelta outputStates = (AccountStateDelta)new AccountStateDelta()
.SetState(addresses[0], Null.Value)
.SetState(addresses[1], (Text)"foo")
.SetState(addresses[2], new List((Text)"bar"));

var previousStates = new AccountStateDelta();

var actionEvaluation = new ActionEvaluation(
Null.Value,
new ActionContext(null,
addresses[0],
null,
addresses[1],
0,
0,
false,
previousStates,
new Random(123),
null,
true),
outputStates,
new Libplanet.Action.UnexpectedlyTerminatedActionException("", null, null, null, null, new NullAction(), null));
var serialized = ActionEvaluationMarshaller.Serialize(actionEvaluation);
var deserialized = ActionEvaluationMarshaller.Deserialize(serialized);

Assert.Equal(Null.Value, deserialized.Action);
Assert.Equal(123, deserialized.InputContext.Random.Seed);
Assert.Equal(0, deserialized.InputContext.BlockIndex);
Assert.Equal(0, deserialized.InputContext.BlockProtocolVersion);
Assert.Equal(addresses[0], deserialized.InputContext.Signer);
Assert.Equal(addresses[1], deserialized.InputContext.Miner);
Assert.Equal(Null.Value, deserialized.OutputState.GetState(addresses[0]));
Assert.Equal((Text)"foo", deserialized.OutputState.GetState(addresses[1]));
Assert.Equal(new List((Text)"bar"), deserialized.OutputState.GetState(addresses[2]));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>


<ItemGroup>
<ProjectReference Include="..\.Libplanet.Extensions.ActionEvaluatorCommonComponents\Libplanet.Extensions.ActionEvaluatorCommonComponents.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System.Collections.Immutable;
using System.Numerics;
using Bencodex.Types;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Libplanet.Types.Consensus;
using Libplanet.Action.State;

namespace Libplanet.Extensions.ActionEvaluatorCommonComponents
{
public class AccountDelta : IAccountDelta
{
public AccountDelta()
{
States = ImmutableDictionary<Address, IValue>.Empty;
Fungibles = ImmutableDictionary<(Address, Currency), BigInteger>.Empty;
TotalSupplies = ImmutableDictionary<Currency, BigInteger>.Empty;
ValidatorSet = null;
}

public AccountDelta(
IImmutableDictionary<Address, IValue> statesDelta,
IImmutableDictionary<(Address, Currency), BigInteger> fungiblesDelta,
IImmutableDictionary<Currency, BigInteger> totalSuppliesDelta,
ValidatorSet? validatorSetDelta)
{
States = statesDelta;
Fungibles = fungiblesDelta;
TotalSupplies = totalSuppliesDelta;
ValidatorSet = validatorSetDelta;
}

/// <inheritdoc cref="IAccountDelta.UpdatedAddresses"/>
public IImmutableSet<Address> UpdatedAddresses =>
StateUpdatedAddresses.Union(FungibleUpdatedAddresses);

/// <inheritdoc cref="IAccountDelta.StateUpdatedAddresses"/>
public IImmutableSet<Address> StateUpdatedAddresses =>
States.Keys.ToImmutableHashSet();

/// <inheritdoc cref="IAccountDelta.States"/>
public IImmutableDictionary<Address, IValue> States { get; }

/// <inheritdoc cref="IAccountDelta.FungibleUpdatedAddresses"/>
public IImmutableSet<Address> FungibleUpdatedAddresses =>
Fungibles.Keys.Select(pair => pair.Item1).ToImmutableHashSet();

/// <inheritdoc cref="IAccountDelta.UpdatedFungibleAssets"/>
public IImmutableSet<(Address, Currency)> UpdatedFungibleAssets =>
Fungibles.Keys.ToImmutableHashSet();

/// <inheritdoc cref="IAccountDelta.Fungibles"/>
public IImmutableDictionary<(Address, Currency), BigInteger> Fungibles { get; }

/// <inheritdoc cref="IAccountDelta.UpdatedTotalSupplyCurrencies"/>
public IImmutableSet<Currency> UpdatedTotalSupplyCurrencies =>
TotalSupplies.Keys.ToImmutableHashSet();

/// <inheritdoc cref="IAccountDelta.TotalSupplies"/>
public IImmutableDictionary<Currency, BigInteger> TotalSupplies { get; }

/// <inheritdoc cref="IAccountDelta.ValidatorSet"/>
public ValidatorSet? ValidatorSet { get; }
}
}
Loading

0 comments on commit 8a118ed

Please sign in to comment.