diff --git a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs
index 3771278839..d50b7df5f8 100644
--- a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs
+++ b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs
@@ -36,6 +36,11 @@ partial class ApplicationEngine
///
public const int MaxNotificationSize = 1024;
+ ///
+ /// The maximum number of notifications per application execution.
+ ///
+ public const int MaxNotificationCount = 512;
+
private uint random_times = 0;
///
@@ -395,8 +400,15 @@ protected internal void RuntimeNotifyV1(byte[] eventName, Array state)
protected internal void SendNotification(UInt160 hash, string eventName, Array state)
{
NotifyEventArgs notification = new(ScriptContainer, hash, eventName, (Array)state.DeepCopy(asImmutable: true));
- Notify?.Invoke(this, notification);
notifications ??= new List();
+ // Restrict the number of notifications for Application executions. Do not check
+ // persisting triggers to avoid native persist failure. Do not check verification
+ // trigger since verification context is loaded with ReadOnly flag.
+ if (IsHardforkEnabled(Hardfork.HF_Echidna) && Trigger == TriggerType.Application && notifications.Count == MaxNotificationCount)
+ {
+ throw new InvalidOperationException($"Maximum number of notifications `{MaxNotificationCount}` is reached.");
+ }
+ Notify?.Invoke(this, notification);
notifications.Add(notification);
CurrentContext.GetState().NotificationCount++;
}