Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Plugin unhandled exception (#3349)" #3366

Merged
merged 4 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 2 additions & 64 deletions src/Neo/Ledger/Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,18 @@
using Akka.Actor;
using Akka.Configuration;
using Akka.IO;
using Akka.Util.Internal;
using Neo.IO.Actors;
using Neo.Network.P2P;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.Plugins;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.VM;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace Neo.Ledger
{
Expand Down Expand Up @@ -472,10 +468,10 @@ private void Persist(Block block)
Context.System.EventStream.Publish(application_executed);
all_application_executed.Add(application_executed);
}
_ = InvokeCommittingAsync(system, block, snapshot, all_application_executed);
Committing?.Invoke(system, block, snapshot, all_application_executed);
snapshot.Commit();
}
_ = InvokeCommittedAsync(system, block);
Committed?.Invoke(system, block);
system.MemPool.UpdatePoolForBlockPersisted(block, system.StoreView);
extensibleWitnessWhiteList = null;
block_cache.Remove(block.PrevHash);
Expand All @@ -484,64 +480,6 @@ private void Persist(Block block)
Debug.Assert(header.Index == block.Index);
}

internal static async Task InvokeCommittingAsync(NeoSystem system, Block block, DataCache snapshot, IReadOnlyList<ApplicationExecuted> applicationExecutedList)
{
await InvokeHandlersAsync(Committing?.GetInvocationList(), h => ((CommittingHandler)h)(system, block, snapshot, applicationExecutedList));
}

internal static async Task InvokeCommittedAsync(NeoSystem system, Block block)
{
await InvokeHandlersAsync(Committed?.GetInvocationList(), h => ((CommittedHandler)h)(system, block));
}

private static async Task InvokeHandlersAsync(Delegate[] handlers, Action<Delegate> handlerAction)
{
if (handlers == null) return;

var exceptions = new ConcurrentBag<Exception>();
var tasks = handlers.Select(handler => Task.Run(() =>
{
try
{
// skip stopped plugin.
if (handler.Target is Plugin { IsStopped: true })
{
return;
}

handlerAction(handler);
}
catch (Exception ex) when (handler.Target is Plugin plugin)
{
switch (plugin.ExceptionPolicy)
{
case UnhandledExceptionPolicy.StopNode:
exceptions.Add(ex);
throw;
case UnhandledExceptionPolicy.StopPlugin:
//Stop plugin on exception
plugin.IsStopped = true;
break;
case UnhandledExceptionPolicy.Ignore:
// Log the exception and continue with the next handler
break;
default:
throw new InvalidCastException($"The exception policy {plugin.ExceptionPolicy} is not valid.");
}

Utility.Log(nameof(plugin), LogLevel.Error, ex);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
})).ToList();

await Task.WhenAll(tasks);

exceptions.ForEach(e => throw e);
}

/// <summary>
/// Gets a <see cref="Akka.Actor.Props"/> object used for creating the <see cref="Blockchain"/> actor.
/// </summary>
Expand Down
56 changes: 6 additions & 50 deletions src/Neo/Plugins/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ public abstract class Plugin : IDisposable
/// <summary>
/// The directory containing the plugin folders. Files can be contained in any subdirectory.
/// </summary>
public static readonly string PluginsDirectory =
Combine(GetDirectoryName(System.AppContext.BaseDirectory), "Plugins");
public static readonly string PluginsDirectory = Combine(GetDirectoryName(System.AppContext.BaseDirectory), "Plugins");

private static readonly FileSystemWatcher configWatcher;

Expand Down Expand Up @@ -68,27 +67,14 @@ public abstract class Plugin : IDisposable
/// </summary>
public virtual Version Version => GetType().Assembly.GetName().Version;

/// <summary>
/// If the plugin should be stopped when an exception is thrown.
/// Default is <see langword="true"/>.
/// </summary>
protected internal virtual UnhandledExceptionPolicy ExceptionPolicy { get; init; } = UnhandledExceptionPolicy.StopNode;

/// <summary>
/// The plugin will be stopped if an exception is thrown.
/// But it also depends on <see cref="UnhandledExceptionPolicy"/>.
/// </summary>
internal bool IsStopped { get; set; }

static Plugin()
{
if (!Directory.Exists(PluginsDirectory)) return;
configWatcher = new FileSystemWatcher(PluginsDirectory)
{
EnableRaisingEvents = true,
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.CreationTime |
NotifyFilters.LastWrite | NotifyFilters.Size,
NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.Size,
};
configWatcher.Changed += ConfigWatcher_Changed;
configWatcher.Created += ConfigWatcher_Changed;
Expand Down Expand Up @@ -120,8 +106,7 @@ private static void ConfigWatcher_Changed(object sender, FileSystemEventArgs e)
{
case ".json":
case ".dll":
Utility.Log(nameof(Plugin), LogLevel.Warning,
$"File {e.Name} is {e.ChangeType}, please restart node.");
Utility.Log(nameof(Plugin), LogLevel.Warning, $"File {e.Name} is {e.ChangeType}, please restart node.");
break;
}
}
Expand All @@ -134,8 +119,7 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven
AssemblyName an = new(args.Name);

Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name) ??
AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.GetName().Name == an.Name);
AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == an.Name);
if (assembly != null) return assembly;

string filename = an.Name + ".dll";
Expand Down Expand Up @@ -166,8 +150,7 @@ public virtual void Dispose()
/// <returns>The content of the configuration file read.</returns>
protected IConfigurationSection GetConfiguration()
{
return new ConfigurationBuilder().AddJsonFile(ConfigFile, optional: true).Build()
.GetSection("PluginConfiguration");
return new ConfigurationBuilder().AddJsonFile(ConfigFile, optional: true).Build().GetSection("PluginConfiguration");
}

private static void LoadPlugin(Assembly assembly)
Expand Down Expand Up @@ -204,7 +187,6 @@ internal static void LoadPlugins()
catch { }
}
}

foreach (Assembly assembly in assemblies)
{
LoadPlugin(assembly);
Expand Down Expand Up @@ -247,33 +229,7 @@ protected internal virtual void OnSystemLoaded(NeoSystem system)
/// <returns><see langword="true"/> if the <paramref name="message"/> is handled by a plugin; otherwise, <see langword="false"/>.</returns>
public static bool SendMessage(object message)
{

return Plugins.Any(plugin =>
{
try
{
return !plugin.IsStopped &&
plugin.OnMessage(message);
}
catch (Exception ex)
{
switch (plugin.ExceptionPolicy)
{
case UnhandledExceptionPolicy.StopNode:
throw;
case UnhandledExceptionPolicy.StopPlugin:
plugin.IsStopped = true;
break;
case UnhandledExceptionPolicy.Ignore:
break;
default:
throw new InvalidCastException($"The exception policy {plugin.ExceptionPolicy} is not valid.");
}
Utility.Log(nameof(Plugin), LogLevel.Error, ex);
return false;
}
}
);
return Plugins.Any(plugin => plugin.OnMessage(message));
}
}
}
33 changes: 0 additions & 33 deletions src/Neo/Plugins/PluginSettings.cs

This file was deleted.

20 changes: 0 additions & 20 deletions src/Neo/Plugins/UnhandledExceptionPolicy.cs

This file was deleted.

3 changes: 1 addition & 2 deletions src/Plugins/ApplicationLogs/ApplicationLogs.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"Path": "ApplicationLogs_{0}",
"Network": 860833102,
"MaxStackSize": 65535,
"Debug": false,
"UnhandledExceptionPolicy": "StopPlugin"
"Debug": false
},
"Dependency": [
"RpcServer"
Expand Down
1 change: 0 additions & 1 deletion src/Plugins/ApplicationLogs/LogReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public class LogReader : Plugin, ICommittingHandler, ICommittedHandler, ILogHand

public override string Name => "ApplicationLogs";
public override string Description => "Synchronizes smart contract VM executions and notifications (NotifyLog) on blockchain.";
protected override UnhandledExceptionPolicy ExceptionPolicy => Settings.Default.ExceptionPolicy;

#region Ctor

Expand Down
4 changes: 2 additions & 2 deletions src/Plugins/ApplicationLogs/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace Neo.Plugins.ApplicationLogs
{
internal class Settings : PluginSettings
internal class Settings
{
public string Path { get; }
public uint Network { get; }
Expand All @@ -23,7 +23,7 @@ internal class Settings : PluginSettings

public static Settings Default { get; private set; }

private Settings(IConfigurationSection section) : base(section)
private Settings(IConfigurationSection section)
{
Path = section.GetValue("Path", "ApplicationLogs_{0}");
Network = section.GetValue("Network", 5195086u);
Expand Down
2 changes: 0 additions & 2 deletions src/Plugins/DBFTPlugin/DBFTPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ public class DBFTPlugin : Plugin, IServiceAddedHandler, IMessageReceivedHandler,

public override string ConfigFile => System.IO.Path.Combine(RootPath, "DBFTPlugin.json");

protected override UnhandledExceptionPolicy ExceptionPolicy => settings.ExceptionPolicy;

public DBFTPlugin()
{
RemoteNode.MessageReceived += ((IMessageReceivedHandler)this).RemoteNode_MessageReceived_Handler;
Expand Down
3 changes: 1 addition & 2 deletions src/Plugins/DBFTPlugin/DBFTPlugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"AutoStart": false,
"Network": 860833102,
"MaxBlockSize": 2097152,
"MaxBlockSystemFee": 150000000000,
"UnhandledExceptionPolicy": "StopNode"
"MaxBlockSystemFee": 150000000000
}
}
4 changes: 2 additions & 2 deletions src/Plugins/DBFTPlugin/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace Neo.Plugins.DBFTPlugin
{
public class Settings : PluginSettings
public class Settings
{
public string RecoveryLogs { get; }
public bool IgnoreRecoveryLogs { get; }
Expand All @@ -22,7 +22,7 @@ public class Settings : PluginSettings
public uint MaxBlockSize { get; }
public long MaxBlockSystemFee { get; }

public Settings(IConfigurationSection section) : base(section)
public Settings(IConfigurationSection section)
{
RecoveryLogs = section.GetValue("RecoveryLogs", "ConsensusState");
IgnoreRecoveryLogs = section.GetValue("IgnoreRecoveryLogs", false);
Expand Down
2 changes: 0 additions & 2 deletions src/Plugins/OracleService/OracleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@

public override string Description => "Built-in oracle plugin";

protected override UnhandledExceptionPolicy ExceptionPolicy => Settings.Default.ExceptionPolicy;

public override string ConfigFile => System.IO.Path.Combine(RootPath, "OracleService.json");

public OracleService()
Expand Down Expand Up @@ -236,7 +234,7 @@

finishedCache.ContainsKey(requestId).False_Or(RpcError.OracleRequestFinished);

using (var snapshot = _system.GetSnapshot())

Check warning on line 237 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test-Everything

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 237 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 237 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 237 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 237 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test-Everything

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 237 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 237 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 237 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'
{
uint height = NativeContract.Ledger.CurrentIndex(snapshot) + 1;
var oracles = NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.Oracle, height);
Expand Down Expand Up @@ -328,7 +326,7 @@
{
while (!cancelSource.IsCancellationRequested)
{
using (var snapshot = _system.GetSnapshot())

Check warning on line 329 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test-Everything

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 329 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 329 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 329 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 329 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test-Everything

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 329 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 329 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'

Check warning on line 329 in src/Plugins/OracleService/OracleService.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

'NeoSystem.GetSnapshot()' is obsolete: 'This method is obsolete, use GetSnapshotCache instead.'
{
SyncPendingQueue(snapshot);
foreach (var (id, request) in NativeContract.Oracle.GetRequests(snapshot))
Expand Down
1 change: 0 additions & 1 deletion src/Plugins/OracleService/OracleService.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"MaxOracleTimeout": 10000,
"AllowPrivateHost": false,
"AllowedContentTypes": [ "application/json" ],
"UnhandledExceptionPolicy": "Ignore",
"Https": {
"Timeout": 5000
},
Expand Down
4 changes: 2 additions & 2 deletions src/Plugins/OracleService/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public NeoFSSettings(IConfigurationSection section)
}
}

class Settings : PluginSettings
class Settings
{
public uint Network { get; }
public Uri[] Nodes { get; }
Expand All @@ -51,7 +51,7 @@ class Settings : PluginSettings

public static Settings Default { get; private set; }

private Settings(IConfigurationSection section) : base(section)
private Settings(IConfigurationSection section)
{
Network = section.GetValue("Network", 5195086u);
Nodes = section.GetSection("Nodes").GetChildren().Select(p => new Uri(p.Get<string>(), UriKind.Absolute)).ToArray();
Expand Down
1 change: 0 additions & 1 deletion src/Plugins/RpcServer/RpcServer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"PluginConfiguration": {
"UnhandledExceptionPolicy": "Ignore",
"Servers": [
{
"Network": 860833102,
Expand Down
1 change: 0 additions & 1 deletion src/Plugins/RpcServer/RpcServerPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public class RpcServerPlugin : Plugin
private static readonly Dictionary<uint, List<object>> handlers = new();

public override string ConfigFile => System.IO.Path.Combine(RootPath, "RpcServer.json");
protected override UnhandledExceptionPolicy ExceptionPolicy => settings.ExceptionPolicy;

protected override void Configure()
{
Expand Down
Loading
Loading