Skip to content

Commit

Permalink
Merge pull request #2341 from riemannulus/merge/73/to/main
Browse files Browse the repository at this point in the history
🔀 merge release/73 into development
  • Loading branch information
riemannulus authored Dec 5, 2023
2 parents 3961dc8 + 44a26df commit 1e676ba
Show file tree
Hide file tree
Showing 15 changed files with 286 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

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

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

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Reflection;
using System.Security.Cryptography;
using Lib9c.Plugin.Shared;
using Libplanet.Action;
using Libplanet.Action.Loader;
using Libplanet.Common;
using Libplanet.Extensions.ActionEvaluatorCommonComponents;
using Libplanet.Store.Trie;
using Libplanet.Types.Blocks;

namespace Libplanet.Extensions.PluggedActionEvaluator
{
public class PluggedActionEvaluator : IActionEvaluator
{
private readonly IPluginActionEvaluator _pluginActionEvaluator;

public IActionLoader ActionLoader => throw new NotImplementedException();

public PluggedActionEvaluator(string pluginPath, string typeName, IKeyValueStore keyValueStore)
{
_pluginActionEvaluator = CreateActionEvaluator(pluginPath, typeName, keyValueStore);
}

public static Assembly LoadPlugin(string absolutePath)
{
PluginLoadContext loadContext = new PluginLoadContext(absolutePath);
return loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(absolutePath)));
}

public static IPluginActionEvaluator CreateActionEvaluator(Assembly assembly, string typeName, IPluginKeyValueStore keyValueStore)
{
if (assembly.GetType(typeName) is Type type &&
Activator.CreateInstance(type, args: keyValueStore) as IPluginActionEvaluator
is IPluginActionEvaluator pluginActionEvaluator)
{
return pluginActionEvaluator;
}

throw new NullReferenceException("PluginActionEvaluator not found with given parameters");
}

public static IPluginActionEvaluator CreateActionEvaluator(string pluginPath, string typeName, IKeyValueStore keyValueStore)
=> CreateActionEvaluator(LoadPlugin(pluginPath), typeName, new PluginKeyValueStore(keyValueStore));

public IReadOnlyList<ICommittedActionEvaluation> Evaluate(IPreEvaluationBlock block, HashDigest<SHA256>? baseStateRootHash)
=> _pluginActionEvaluator.Evaluate(
PreEvaluationBlockMarshaller.Serialize(block),
baseStateRootHash is { } srh ? srh.ToByteArray() : null)
.Select(eval => ActionEvaluationMarshaller.Deserialize(eval)).ToList().AsReadOnly();
}
}
42 changes: 42 additions & 0 deletions Libplanet.Extensions.PluggedActionEvaluator/PluginKeyValueStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Collections.Immutable;
using Lib9c.Plugin.Shared;
using Libplanet.Store.Trie;

namespace Libplanet.Extensions.PluggedActionEvaluator
{
public class PluginKeyValueStore : IPluginKeyValueStore
{
private readonly IKeyValueStore _keyValueStore;

public PluginKeyValueStore(IKeyValueStore keyValueStore)
{
_keyValueStore = keyValueStore;
}
public byte[] Get(in ImmutableArray<byte> key) =>
_keyValueStore.Get(new KeyBytes(key));

public void Set(in ImmutableArray<byte> key, byte[] value) =>
_keyValueStore.Set(new KeyBytes(key), value);

public void Set(IDictionary<ImmutableArray<byte>, byte[]> values) =>
_keyValueStore.Set(
values.ToDictionary(kv =>
new KeyBytes(kv.Key), kv => kv.Value));

public void Delete(in ImmutableArray<byte> key) =>
_keyValueStore.Delete(new KeyBytes(key));

public void Delete(IEnumerable<ImmutableArray<byte>> keys) =>
_keyValueStore.Delete(
keys.Select(key => new KeyBytes(key)));

public bool Exists(in ImmutableArray<byte> key) =>
_keyValueStore.Exists(new KeyBytes(key));

public IEnumerable<ImmutableArray<byte>> ListKeys() =>
_keyValueStore.ListKeys().Select(key => key.ByteArray);

public void Dispose() =>
_keyValueStore.Dispose();
}
}
36 changes: 36 additions & 0 deletions Libplanet.Extensions.PluggedActionEvaluator/PluginLoadContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.Loader;

namespace Libplanet.Extensions.PluggedActionEvaluator
{
public class PluginLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;

public PluginLoadContext(string pluginPath)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}

protected override Assembly? Load(AssemblyName assemblyName)
{
if (_resolver.ResolveAssemblyToPath(assemblyName) is { } assemblyPath)
{
return LoadFromAssemblyPath(assemblyPath);
}

return null;
}

protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
if (_resolver.ResolveUnmanagedDllToPath(unmanagedDllName) is { } libraryPath)
{
return LoadUnmanagedDllFromPath(libraryPath);
}

return IntPtr.Zero;
}
}
}

1 change: 1 addition & 0 deletions Libplanet.Headless/Hosting/ActionEvaluatorType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ public enum ActionEvaluatorType
Default, // ActionEvaluator
ForkableActionEvaluator,
RemoteActionEvaluator,
PluggedActionEvaluator,
}
67 changes: 50 additions & 17 deletions Libplanet.Headless/Hosting/LibplanetNodeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Threading;
Expand All @@ -15,6 +16,7 @@
using Libplanet.Types.Blocks;
using Libplanet.Crypto;
using Libplanet.Extensions.ForkableActionEvaluator;
using Libplanet.Extensions.PluggedActionEvaluator;
using Libplanet.Extensions.RemoteActionEvaluator;
using Libplanet.Net;
using Libplanet.Net.Consensus;
Expand Down Expand Up @@ -95,7 +97,7 @@ public LibplanetNodeService(

var iceServers = Properties.IceServers;

(Store, StateStore) = LoadStore(
(Store, StateStore, IKeyValueStore keyValueStore) = LoadStore(
Properties.StorePath,
Properties.StoreType,
Properties.StoreStatesCacheSize);
Expand All @@ -116,23 +118,28 @@ public LibplanetNodeService(
}

var blockChainStates = new BlockChainStates(Store, StateStore);

IActionEvaluator BuildActionEvaluator(IActionEvaluatorConfiguration actionEvaluatorConfiguration)
{
return actionEvaluatorConfiguration switch
{
RemoteActionEvaluatorConfiguration remoteActionEvaluatorConfiguration => new RemoteActionEvaluator(
new Uri(remoteActionEvaluatorConfiguration.StateServiceEndpoint)),
DefaultActionEvaluatorConfiguration _ => new ActionEvaluator(
_ => blockPolicy.BlockAction,
stateStore: StateStore,
actionTypeLoader: actionLoader
),
ForkableActionEvaluatorConfiguration forkableActionEvaluatorConfiguration => new
ForkableActionEvaluator(
forkableActionEvaluatorConfiguration.Pairs.Select(pair => (
(pair.Item1.Start, pair.Item1.End), BuildActionEvaluator(pair.Item2)
))
),
PluggedActionEvaluatorConfiguration pluginActionEvaluatorConfiguration =>
new PluggedActionEvaluator(
ResolvePluginPath(pluginActionEvaluatorConfiguration.PluginPath),
pluginActionEvaluatorConfiguration.TypeName,
keyValueStore),
RemoteActionEvaluatorConfiguration remoteActionEvaluatorConfiguration =>
new RemoteActionEvaluator(
new Uri(remoteActionEvaluatorConfiguration.StateServiceEndpoint)),
DefaultActionEvaluatorConfiguration _ =>
new ActionEvaluator(
_ => blockPolicy.BlockAction,
stateStore: StateStore,
actionTypeLoader: actionLoader),
ForkableActionEvaluatorConfiguration forkableActionEvaluatorConfiguration =>
new ForkableActionEvaluator(
forkableActionEvaluatorConfiguration.Pairs.Select(
pair => ((pair.Item1.Start, pair.Item1.End), BuildActionEvaluator(pair.Item2)))),
_ => throw new InvalidOperationException("Unexpected type."),
};
}
Expand Down Expand Up @@ -302,7 +309,7 @@ public override async Task StopAsync(CancellationToken cancellationToken)
}
}

protected (IStore, IStateStore) LoadStore(string path, string type, int statesCacheSize)
protected (IStore, IStateStore, IKeyValueStore) LoadStore(string path, string type, int statesCacheSize)
{
IStore store = null;
if (type == "rocksdb")
Expand Down Expand Up @@ -346,7 +353,7 @@ public override async Task StopAsync(CancellationToken cancellationToken)

IKeyValueStore stateKeyValueStore = new RocksDBKeyValueStore(Path.Combine(path, "states"));
IStateStore stateStore = new TrieStateStore(stateKeyValueStore);
return (store, stateStore);
return (store, stateStore, stateKeyValueStore);
}

private async Task StartSwarm(bool preload, CancellationToken cancellationToken)
Expand Down Expand Up @@ -568,7 +575,7 @@ protected async Task CheckPeerTable(CancellationToken cancellationToken = defaul
if (grace == count)
{
var message = "No any peers are connected even seed peers were given. " +
$"(grace: {grace}";
$"(grace: {grace}";
Log.Error(message);
// _exceptionHandlerAction(RPCException.NetworkException, message);
Properties.NodeExceptionOccurred(NodeExceptionType.NoAnyPeer, message);
Expand Down Expand Up @@ -629,6 +636,32 @@ public override void Dispose()
Log.Debug("Store disposed.");
}

private string ResolvePluginPath(string path) =>
Uri.IsWellFormedUriString(path, UriKind.Absolute)
? DownloadPlugin(path).Result
: path;

private async Task<string> DownloadPlugin(string url)
{
var path = Path.Combine(Environment.CurrentDirectory, "plugins");
Directory.CreateDirectory(path);
var hashed = url.GetHashCode().ToString();
var logger = Log.ForContext("LibplanetNodeService", hashed);
using var httpClient = new HttpClient();
var downloadPath = Path.Join(path, hashed + ".zip");
var extractPath = Path.Join(path, hashed);
logger.Debug("Downloading...");
await File.WriteAllBytesAsync(
downloadPath,
await httpClient.GetByteArrayAsync(url, SwarmCancellationToken),
SwarmCancellationToken);
logger.Debug("Finished downloading.");
logger.Debug("Extracting...");
ZipFile.ExtractToDirectory(downloadPath, extractPath);
logger.Debug("Finished extracting.");
return Path.Combine(extractPath, "Lib9c.Plugin.dll");
}

// FIXME: Request libplanet provide default implementation.
private sealed class ActionTypeLoaderContext : IActionTypeLoaderContext
{
Expand Down
10 changes: 10 additions & 0 deletions Libplanet.Headless/Hosting/PluggedActionEvaluatorConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Libplanet.Headless.Hosting;

public class PluggedActionEvaluatorConfiguration : IActionEvaluatorConfiguration
{
public ActionEvaluatorType Type => ActionEvaluatorType.PluggedActionEvaluator;

public string PluginPath { get; init; }

public string TypeName => "Lib9c.Plugin.PluginActionEvaluator";
}
1 change: 1 addition & 0 deletions Libplanet.Headless/Libplanet.Headless.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
<ProjectReference Include="..\NineChronicles.RPC.Shared\NineChronicles.RPC.Shared\NineChronicles.RPC.Shared.csproj" />
<ProjectReference Include="..\Lib9c\.Libplanet.Extensions.RemoteActionEvaluator\Libplanet.Extensions.RemoteActionEvaluator.csproj" />
<ProjectReference Include="..\Libplanet.Extensions.ForkableActionEvaluator\Libplanet.Extensions.ForkableActionEvaluator.csproj" />
<ProjectReference Include="..\Libplanet.Extensions.PluggedActionEvaluator\Libplanet.Extensions.PluggedActionEvaluator.csproj" />
</ItemGroup>
</Project>
Loading

0 comments on commit 1e676ba

Please sign in to comment.