diff --git a/Lib9c b/Lib9c
index 1dc61aa5f..5e831650b 160000
--- a/Lib9c
+++ b/Lib9c
@@ -1 +1 @@
-Subproject commit 1dc61aa5f4844a0112ba9dad79bc9dbdc732a54e
+Subproject commit 5e831650b01b19651d4f6ff83cb7d848f62b3ccf
diff --git a/Libplanet.Extensions.PluggedActionEvaluator/Libplanet.Extensions.PluggedActionEvaluator.csproj b/Libplanet.Extensions.PluggedActionEvaluator/Libplanet.Extensions.PluggedActionEvaluator.csproj
new file mode 100644
index 000000000..78e3b7dcd
--- /dev/null
+++ b/Libplanet.Extensions.PluggedActionEvaluator/Libplanet.Extensions.PluggedActionEvaluator.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/Libplanet.Extensions.PluggedActionEvaluator/PluggedActionEvaluator.cs b/Libplanet.Extensions.PluggedActionEvaluator/PluggedActionEvaluator.cs
new file mode 100644
index 000000000..c2ee18264
--- /dev/null
+++ b/Libplanet.Extensions.PluggedActionEvaluator/PluggedActionEvaluator.cs
@@ -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 Evaluate(IPreEvaluationBlock block, HashDigest? baseStateRootHash)
+ => _pluginActionEvaluator.Evaluate(
+ PreEvaluationBlockMarshaller.Serialize(block),
+ baseStateRootHash is { } srh ? srh.ToByteArray() : null)
+ .Select(eval => ActionEvaluationMarshaller.Deserialize(eval)).ToList().AsReadOnly();
+ }
+}
diff --git a/Libplanet.Extensions.PluggedActionEvaluator/PluginKeyValueStore.cs b/Libplanet.Extensions.PluggedActionEvaluator/PluginKeyValueStore.cs
new file mode 100644
index 000000000..e2172fff5
--- /dev/null
+++ b/Libplanet.Extensions.PluggedActionEvaluator/PluginKeyValueStore.cs
@@ -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 key) =>
+ _keyValueStore.Get(new KeyBytes(key));
+
+ public void Set(in ImmutableArray key, byte[] value) =>
+ _keyValueStore.Set(new KeyBytes(key), value);
+
+ public void Set(IDictionary, byte[]> values) =>
+ _keyValueStore.Set(
+ values.ToDictionary(kv =>
+ new KeyBytes(kv.Key), kv => kv.Value));
+
+ public void Delete(in ImmutableArray key) =>
+ _keyValueStore.Delete(new KeyBytes(key));
+
+ public void Delete(IEnumerable> keys) =>
+ _keyValueStore.Delete(
+ keys.Select(key => new KeyBytes(key)));
+
+ public bool Exists(in ImmutableArray key) =>
+ _keyValueStore.Exists(new KeyBytes(key));
+
+ public IEnumerable> ListKeys() =>
+ _keyValueStore.ListKeys().Select(key => key.ByteArray);
+
+ public void Dispose() =>
+ _keyValueStore.Dispose();
+ }
+}
diff --git a/Libplanet.Extensions.PluggedActionEvaluator/PluginLoadContext.cs b/Libplanet.Extensions.PluggedActionEvaluator/PluginLoadContext.cs
new file mode 100644
index 000000000..497e9a791
--- /dev/null
+++ b/Libplanet.Extensions.PluggedActionEvaluator/PluginLoadContext.cs
@@ -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;
+ }
+ }
+}
+
diff --git a/Libplanet.Headless/Hosting/ActionEvaluatorType.cs b/Libplanet.Headless/Hosting/ActionEvaluatorType.cs
index 9d9498b61..616d0d2d1 100644
--- a/Libplanet.Headless/Hosting/ActionEvaluatorType.cs
+++ b/Libplanet.Headless/Hosting/ActionEvaluatorType.cs
@@ -5,4 +5,5 @@ public enum ActionEvaluatorType
Default, // ActionEvaluator
ForkableActionEvaluator,
RemoteActionEvaluator,
+ PluggedActionEvaluator,
}
diff --git a/Libplanet.Headless/Hosting/LibplanetNodeService.cs b/Libplanet.Headless/Hosting/LibplanetNodeService.cs
index cc5a3d55a..a77e4a58f 100644
--- a/Libplanet.Headless/Hosting/LibplanetNodeService.cs
+++ b/Libplanet.Headless/Hosting/LibplanetNodeService.cs
@@ -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;
@@ -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;
@@ -95,7 +97,7 @@ public LibplanetNodeService(
var iceServers = Properties.IceServers;
- (Store, StateStore) = LoadStore(
+ (Store, StateStore, IKeyValueStore keyValueStore) = LoadStore(
Properties.StorePath,
Properties.StoreType,
Properties.StoreStatesCacheSize);
@@ -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."),
};
}
@@ -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")
@@ -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)
@@ -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);
@@ -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 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
{
diff --git a/Libplanet.Headless/Hosting/PluggedActionEvaluatorConfiguration.cs b/Libplanet.Headless/Hosting/PluggedActionEvaluatorConfiguration.cs
new file mode 100644
index 000000000..cca6d3a4d
--- /dev/null
+++ b/Libplanet.Headless/Hosting/PluggedActionEvaluatorConfiguration.cs
@@ -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";
+}
diff --git a/Libplanet.Headless/Libplanet.Headless.csproj b/Libplanet.Headless/Libplanet.Headless.csproj
index 94ce16144..3ada062b3 100644
--- a/Libplanet.Headless/Libplanet.Headless.csproj
+++ b/Libplanet.Headless/Libplanet.Headless.csproj
@@ -25,5 +25,6 @@
+
diff --git a/NineChronicles.Headless.Executable.sln b/NineChronicles.Headless.Executable.sln
index b14931789..4447ecc4e 100644
--- a/NineChronicles.Headless.Executable.sln
+++ b/NineChronicles.Headless.Executable.sln
@@ -60,25 +60,29 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Extensions.Forkab
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".Libplanet", ".Libplanet", "{69F04D28-2B2E-454D-9B15-4D708EEEA8B5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libplanet.Action", "Lib9c\.Libplanet\Libplanet.Action\Libplanet.Action.csproj", "{EB464A50-9976-4DEA-B170-F72C4FB73A9C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Action", "Lib9c\.Libplanet\Libplanet.Action\Libplanet.Action.csproj", "{EB464A50-9976-4DEA-B170-F72C4FB73A9C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libplanet.Common", "Lib9c\.Libplanet\Libplanet.Common\Libplanet.Common.csproj", "{95FB2620-540C-4498-9DAE-65198E89680C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Common", "Lib9c\.Libplanet\Libplanet.Common\Libplanet.Common.csproj", "{95FB2620-540C-4498-9DAE-65198E89680C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libplanet.Types", "Lib9c\.Libplanet\Libplanet.Types\Libplanet.Types.csproj", "{FC65B031-F6EE-4561-A365-47B6FDD1C114}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Types", "Lib9c\.Libplanet\Libplanet.Types\Libplanet.Types.csproj", "{FC65B031-F6EE-4561-A365-47B6FDD1C114}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libplanet.Store", "Lib9c\.Libplanet\Libplanet.Store\Libplanet.Store.csproj", "{2FF6DADC-5E7A-4F03-94D5-2CF50DED8C29}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Store", "Lib9c\.Libplanet\Libplanet.Store\Libplanet.Store.csproj", "{2FF6DADC-5E7A-4F03-94D5-2CF50DED8C29}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libplanet.Crypto", "Lib9c\.Libplanet\Libplanet.Crypto\Libplanet.Crypto.csproj", "{2C3AD392-38A1-4E07-B1F9-694EE4A1E0C0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Crypto", "Lib9c\.Libplanet\Libplanet.Crypto\Libplanet.Crypto.csproj", "{2C3AD392-38A1-4E07-B1F9-694EE4A1E0C0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libplanet.Extensions.ActionEvaluatorCommonComponents", "Lib9c\.Libplanet.Extensions.ActionEvaluatorCommonComponents\Libplanet.Extensions.ActionEvaluatorCommonComponents.csproj", "{A6922395-36E5-4B0A-BEBD-9BCE34D08722}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Extensions.ActionEvaluatorCommonComponents", "Lib9c\.Libplanet.Extensions.ActionEvaluatorCommonComponents\Libplanet.Extensions.ActionEvaluatorCommonComponents.csproj", "{A6922395-36E5-4B0A-BEBD-9BCE34D08722}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib9c.StateService.Shared", "Lib9c\.Lib9c.StateService.Shared\Lib9c.StateService.Shared.csproj", "{6A410F06-134A-46D9-8B39-381FA2ED861F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lib9c.StateService.Shared", "Lib9c\.Lib9c.StateService.Shared\Lib9c.StateService.Shared.csproj", "{6A410F06-134A-46D9-8B39-381FA2ED861F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libplanet.Extensions.RemoteActionEvaluator", "Lib9c\.Libplanet.Extensions.RemoteActionEvaluator\Libplanet.Extensions.RemoteActionEvaluator.csproj", "{C41BA817-5D5B-42A5-9CF8-E8F29D1B71EF}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Extensions.RemoteActionEvaluator", "Lib9c\.Libplanet.Extensions.RemoteActionEvaluator\Libplanet.Extensions.RemoteActionEvaluator.csproj", "{C41BA817-5D5B-42A5-9CF8-E8F29D1B71EF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libplanet.Extensions.RemoteBlockChainStates", "Lib9c\.Libplanet.Extensions.RemoteBlockChainStates\Libplanet.Extensions.RemoteBlockChainStates.csproj", "{8F9E5505-C157-4DF3-A419-FF0108731397}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Extensions.RemoteBlockChainStates", "Lib9c\.Libplanet.Extensions.RemoteBlockChainStates\Libplanet.Extensions.RemoteBlockChainStates.csproj", "{8F9E5505-C157-4DF3-A419-FF0108731397}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NineChronicles.Headless.AccessControlCenter", "NineChronicles.Headless.AccessControlCenter\NineChronicles.Headless.AccessControlCenter.csproj", "{162C0F4B-A1D9-4132-BC34-31F1247BC26B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NineChronicles.Headless.AccessControlCenter", "NineChronicles.Headless.AccessControlCenter\NineChronicles.Headless.AccessControlCenter.csproj", "{162C0F4B-A1D9-4132-BC34-31F1247BC26B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libplanet.Extensions.PluggedActionEvaluator", "Libplanet.Extensions.PluggedActionEvaluator\Libplanet.Extensions.PluggedActionEvaluator.csproj", "{DE91C36D-3999-47B6-A0BD-848C8EBA2A76}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lib9c.Plugin.Shared", "Lib9c\.Lib9c.Plugin.Shared\Lib9c.Plugin.Shared.csproj", "{3D32DA34-E619-429F-8421-848FF4F14417}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -723,6 +727,42 @@ Global
{162C0F4B-A1D9-4132-BC34-31F1247BC26B}.Release|x64.Build.0 = Release|Any CPU
{162C0F4B-A1D9-4132-BC34-31F1247BC26B}.Release|x86.ActiveCfg = Release|Any CPU
{162C0F4B-A1D9-4132-BC34-31F1247BC26B}.Release|x86.Build.0 = Release|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Debug|x64.Build.0 = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Debug|x86.Build.0 = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.DevEx|Any CPU.ActiveCfg = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.DevEx|Any CPU.Build.0 = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.DevEx|x64.ActiveCfg = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.DevEx|x64.Build.0 = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.DevEx|x86.ActiveCfg = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.DevEx|x86.Build.0 = Debug|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Release|x64.ActiveCfg = Release|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Release|x64.Build.0 = Release|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Release|x86.ActiveCfg = Release|Any CPU
+ {DE91C36D-3999-47B6-A0BD-848C8EBA2A76}.Release|x86.Build.0 = Release|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Debug|x64.Build.0 = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Debug|x86.Build.0 = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.DevEx|Any CPU.ActiveCfg = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.DevEx|Any CPU.Build.0 = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.DevEx|x64.ActiveCfg = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.DevEx|x64.Build.0 = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.DevEx|x86.ActiveCfg = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.DevEx|x86.Build.0 = Debug|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Release|x64.ActiveCfg = Release|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Release|x64.Build.0 = Release|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Release|x86.ActiveCfg = Release|Any CPU
+ {3D32DA34-E619-429F-8421-848FF4F14417}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/NineChronicles.Headless.Executable/Program.cs b/NineChronicles.Headless.Executable/Program.cs
index d3ada1c07..2dc68caef 100644
--- a/NineChronicles.Headless.Executable/Program.cs
+++ b/NineChronicles.Headless.Executable/Program.cs
@@ -275,6 +275,10 @@ public async Task Run(
return (range, actionEvaluatorConfiguration);
}).ToImmutableArray()
},
+ ActionEvaluatorType.PluggedActionEvaluator => new PluggedActionEvaluatorConfiguration
+ {
+ PluginPath = configuration.GetValue("PluginPath"),
+ },
_ => throw new InvalidOperationException("Unexpected type."),
};
}
diff --git a/NineChronicles.Headless.Executable/appsettings-schema.json b/NineChronicles.Headless.Executable/appsettings-schema.json
index 19dd91e11..55a6e225a 100644
--- a/NineChronicles.Headless.Executable/appsettings-schema.json
+++ b/NineChronicles.Headless.Executable/appsettings-schema.json
@@ -217,7 +217,21 @@
"type": "string"
}
},
- "required": ["stateServiceEndpoint"],
+ "required": [ "stateServiceEndpoint" ],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "const": "PluggedActionEvaluator"
+ },
+ "pluginPath": {
+ "$comment": "Local path or URI. If it is URI, download it under ./plugin",
+ "type": "string"
+ }
+ },
+ "required": [ "pluginPath" ],
"additionalProperties": false
},
{
@@ -238,7 +252,7 @@
"$ref": "#/definitions/action-evaluator"
}
},
- "required": ["range", "actionEvaluator"],
+ "required": [ "range", "actionEvaluator" ],
"additionalProperties": false
}
}
diff --git a/NineChronicles.Headless/BlockChainService.cs b/NineChronicles.Headless/BlockChainService.cs
index 82fb5cb18..a05b128f7 100644
--- a/NineChronicles.Headless/BlockChainService.cs
+++ b/NineChronicles.Headless/BlockChainService.cs
@@ -221,7 +221,7 @@ public UnaryResult> GetSheets(
foreach (var b in addressBytesList)
{
var address = new Address(b);
- if (_memoryCache.TryGetValue(address.ToString(), out byte[] cached))
+ if (_memoryCache.TryGetSheet(address.ToString(), out byte[] cached))
{
result.TryAdd(b, cached);
}
diff --git a/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs b/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs
index 979e21b97..557a0a12b 100644
--- a/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs
+++ b/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs
@@ -199,11 +199,8 @@ public TransactionHeadlessQuery(StandaloneContext standaloneContext)
Field>>(
name: "transactionResults",
arguments: new QueryArguments(
- new QueryArgument>>>
- {
- Name = "txIds",
- Description = "transaction ids."
- }
+ new QueryArgument>>
+ { Name = "txIds", Description = "transaction ids." }
),
resolve: context =>
{
diff --git a/NineChronicles.Headless/MemoryCacheExtensions.cs b/NineChronicles.Headless/MemoryCacheExtensions.cs
index 2c6366d83..3cda79d22 100644
--- a/NineChronicles.Headless/MemoryCacheExtensions.cs
+++ b/NineChronicles.Headless/MemoryCacheExtensions.cs
@@ -18,9 +18,14 @@ public static byte[] SetSheet(this MemoryCache cache, string cacheKey, IValue va
return compressed;
}
+ public static bool TryGetSheet(this MemoryCache cache, string cacheKey, out T cached)
+ {
+ return cache.TryGetValue(cacheKey, out cached);
+ }
+
public static string? GetSheet(this MemoryCache cache, string cacheKey)
{
- if (cache.TryGetValue(cacheKey, out byte[] cached))
+ if (cache.TryGetSheet(cacheKey, out byte[] cached))
{
return (Text)Codec.Decode(MessagePackSerializer.Deserialize(cached, Lz4Options));
}