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)); }