Skip to content

Commit

Permalink
feat: Add the feature to load action loader from external assembly
Browse files Browse the repository at this point in the history
  • Loading branch information
s2quake committed Aug 27, 2024
1 parent 784fe40 commit 4283304
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.40.0"/>
<PackageReference Include="Grpc.AspNetCore.Server.Reflection" Version="2.64.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
Expand All @@ -21,6 +22,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Libplanet.Crypto.Secp256k1\Libplanet.Crypto.Secp256k1.csproj" />
<ProjectReference Include="..\Libplanet.Node.Extensions\Libplanet.Node.Extensions.csproj" />
<ProjectReference Include="..\Libplanet.Node\Libplanet.Node.csproj" />
<ProjectReference Include="..\Libplanet.Node.Swagger\Libplanet.Node.Swagger.csproj" />
Expand Down
2 changes: 2 additions & 0 deletions sdk/node/Libplanet.Node.Executable/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Libplanet.Node.Options.Schema;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Serilog;
using Serilog.Events;

var builder = WebHost.CreateDefaultBuilder(args);
var assemblies = new string[]
Expand Down
32 changes: 29 additions & 3 deletions sdk/node/Libplanet.Node.Executable/appsettings-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,22 @@
}
}
},
"Action": {
"title": "ActionOptions",
"type": "object",
"additionalProperties": false,
"properties": {
"ModulePath": {
"type": "string"
},
"ActionLoaderType": {
"type": "string"
},
"PolicyActionRegistryType": {
"type": "string"
}
}
},
"Genesis": {
"title": "GenesisOptions",
"type": "object",
Expand All @@ -1114,12 +1130,12 @@
"properties": {
"GenesisKey": {
"type": "string",
"description": "The key of the genesis block.",
"description": "The PrivateKey used to generate the genesis block. This property cannot be used with GenesisBlockPath.",
"pattern": "^[0-9a-fA-F]{64}$"
},
"Validators": {
"type": "array",
"description": "Public keys of the validators.",
"description": "Public keys of the validators. This property cannot be used with GenesisBlockPath.",
"items": {
"type": "string"
}
Expand All @@ -1131,7 +1147,7 @@
},
"GenesisBlockPath": {
"type": "string",
"description": "The path of the genesis block."
"description": "The path of the genesis block, which can be a file path or a URI.This property cannot be used with GenesisKey."
}
}
},
Expand Down Expand Up @@ -1221,6 +1237,12 @@
"type": "string",
"description": "The endpoint of the node to block sync.",
"pattern": "^$|^[0-9a-fA-F]{130}|[0-9a-fA-F]{66},\\s*(?:(?:[a-zA-Z0-9\\-\\.]+)|(?:\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})):\\d{1,5}$"
},
"TrustedAppProtocolVersionSigners": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
Expand Down Expand Up @@ -1260,6 +1282,10 @@
"description": "Type 'ExplorerOptions' does not have a description.",
"$ref": "#/definitions/Explorer"
},
"Action": {
"description": "Type 'ActionOptions' does not have a description.",
"$ref": "#/definitions/Action"
},
"Genesis": {
"description": "Options for the genesis block.",
"$ref": "#/definitions/Genesis"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
"Protocols": "Http1AndHttp2"
}
},
"Genesis": {
"GenesisBlockPath": "https://release.nine-chronicles.com/genesis-block-9c-main"
},
"Swarm": {
"IsEnabled": true
},
"Validator": {
"IsEnabled": true
},
"Store": {
"Type": 1
},
"Explorer": {
"IsEnabled": true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public static ILibplanetNodeBuilder AddLibplanetNode(
.Bind(configuration.GetSection(StoreOptions.Position));
services.AddSingleton<IConfigureOptions<StoreOptions>, StoreOptionsConfigurator>();

services.AddOptions<ActionOptions>()
.Bind(configuration.GetSection(ActionOptions.Position));

services.AddOptions<SwarmOptions>()
.Bind(configuration.GetSection(SwarmOptions.Position));
services.AddSingleton<IConfigureOptions<SwarmOptions>, SwarmOptionsConfigurator>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Libplanet.Action;
using Libplanet.Action.Loader;
using Libplanet.Blockchain;
using Libplanet.Crypto;
using Libplanet.Node.Options;
Expand All @@ -20,6 +22,7 @@ public void Create_Test()
services.AddSingleton<IConfigureOptions<GenesisOptions>, GenesisOptionsConfigurator>();
services.AddOptions<StoreOptions>();
services.AddSingleton<IConfigureOptions<StoreOptions>, StoreOptionsConfigurator>();
services.AddOptions<ActionOptions>();
services.AddOptions<SwarmOptions>();
services.AddSingleton<IConfigureOptions<SwarmOptions>, SwarmOptionsConfigurator>();

Expand All @@ -28,11 +31,12 @@ public void Create_Test()
var logger = new NullLoggerFactory().CreateLogger<BlockChainService>();
var genesisOptions = serviceProvider.GetRequiredService<IOptions<GenesisOptions>>();
var storeOptions = serviceProvider.GetRequiredService<IOptions<StoreOptions>>();
var actionOptions = serviceProvider.GetRequiredService<IOptions<ActionOptions>>();
var blockChainService = new BlockChainService(
genesisOptions: genesisOptions,
storeOptions: storeOptions,
actionOptions: actionOptions,
policyService: policyService,
actionLoaderProviders: [],
logger: logger);
var blockChain = blockChainService.BlockChain;

Expand Down
31 changes: 31 additions & 0 deletions sdk/node/Libplanet.Node/Actions/PluginLoadContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Reflection;
using System.Runtime.Loader;

namespace Libplanet.Node.Actions;

internal sealed class PluginLoadContext(string pluginPath) : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver = new(pluginPath);

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

return null;
}

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

return IntPtr.Zero;
}
}
50 changes: 50 additions & 0 deletions sdk/node/Libplanet.Node/Actions/PluginLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Reflection;
using Libplanet.Action;
using Libplanet.Action.Loader;

namespace Libplanet.Node.Actions;

internal static class PluginLoader
{
public static IActionLoader LoadActionLoader(string modulePath, string typeName)
{
var assembly = LoadAssembly(modulePath);
return Create<IActionLoader>(assembly, typeName);
}

public static IPolicyActionsRegistry LoadPolicyActionRegistry(
string relativePath, string typeName)
{
var assembly = LoadAssembly(relativePath);
return Create<IPolicyActionsRegistry>(assembly, typeName);
}

private static T Create<T>(Assembly assembly, string typeName)
where T : class
{
if (assembly.GetType(typeName) is not { } type)
{
throw new ApplicationException(
$"Can't find {typeName} in {assembly} from {assembly.Location}");
}

if (Activator.CreateInstance(type) is not T obj)
{
throw new ApplicationException(
$"Can't create an instance of {type} in {assembly} from {assembly.Location}");
}

return obj;
}

private static Assembly LoadAssembly(string modulePath)
{
var loadContext = new PluginLoadContext(modulePath);
if (Path.GetFileNameWithoutExtension(modulePath) is { } filename)
{
return loadContext.LoadFromAssemblyName(new AssemblyName(filename));
}

throw new ApplicationException($"Can't load plugin from {modulePath}");
}
}
3 changes: 2 additions & 1 deletion sdk/node/Libplanet.Node/Libplanet.Node.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.40.0"/>
<PackageReference Include="Grpc.AspNetCore" Version="2.40.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
Expand All @@ -16,6 +16,7 @@
<ProjectReference Include="..\..\..\src\Libplanet.Store\Libplanet.Store.csproj" />
<ProjectReference Include="..\..\..\src\Libplanet\Libplanet.csproj" />
<ProjectReference Include="..\..\..\src\Libplanet.Net\Libplanet.Net.csproj" />
<ProjectReference Include="..\..\..\..\..\suho\suho-lib9c\.Lib9c.Plugin.Shared\Lib9c.Plugin.Shared.csproj" />
</ItemGroup>

</Project>
41 changes: 41 additions & 0 deletions sdk/node/Libplanet.Node/Options/ActionOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Libplanet.Action;
using Libplanet.Action.Loader;
using Libplanet.Node.Actions;

namespace Libplanet.Node.Options;

[Options(Position)]
public sealed class ActionOptions : OptionsBase<ActionOptions>
{
public const string Position = "Action";

public string ModulePath { get; set; } = string.Empty;

public string ActionLoaderType { get; set; } = string.Empty;

public string PolicyActionRegistryType { get; set; } = string.Empty;

internal IActionLoader GetActionLoader()
{
if (ActionLoaderType != string.Empty)
{
var modulePath = ModulePath;
var actionLoaderType = ActionLoaderType;
return PluginLoader.LoadActionLoader(modulePath, actionLoaderType);
}

return new AggregateTypedActionLoader();
}

internal IPolicyActionsRegistry GetPolicyActionsRegistry()
{
if (PolicyActionRegistryType != string.Empty)
{
var modulePath = ModulePath;
var policyActionRegistryType = PolicyActionRegistryType;
return PluginLoader.LoadPolicyActionRegistry(modulePath, policyActionRegistryType);
}

return new PolicyActionsRegistry();
}
}
3 changes: 3 additions & 0 deletions sdk/node/Libplanet.Node/Options/SwarmOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ public sealed class SwarmOptions : OptionsBase<SwarmOptions>
[BoundPeer]
[Description("The endpoint of the node to block sync.")]
public string BlocksyncSeedPeer { get; set; } = string.Empty;

[PublicKeyArray]
public string[] TrustedAppProtocolVersionSigners { get; set; } = [];
}
Loading

0 comments on commit 4283304

Please sign in to comment.