From b31d95e69a2bef5d32d2a1e007d1d3b1b8b2a7d1 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 23 Oct 2024 18:51:14 +0300 Subject: [PATCH 1/5] SmartContract: restrict the number of allowed notifications Fix the problem described in https://github.com/nspcc-dev/neo-go/issues/3490. Port the solution from https://github.com/nspcc-dev/neo-go/pull/3640. MaxNotificationsCount constraint is chosen based on the Mainnet statistics of the number of notifications per every transaction, ref. https://github.com/nspcc-dev/neo-go/issues/3490#issuecomment-2427153253. Signed-off-by: Anna Shaleva --- src/Neo/SmartContract/ApplicationEngine.Runtime.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) 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++; } From 0c72e571b90cc0583c7d9c527ee1650ba87db595 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 23 Oct 2024 19:14:02 +0300 Subject: [PATCH 2/5] SmartContract: fix format Signed-off-by: Anna Shaleva --- src/Neo/SmartContract/Native/RoleManagement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/RoleManagement.cs b/src/Neo/SmartContract/Native/RoleManagement.cs index a0fc82f126..9ccdbbd7dd 100644 --- a/src/Neo/SmartContract/Native/RoleManagement.cs +++ b/src/Neo/SmartContract/Native/RoleManagement.cs @@ -78,7 +78,7 @@ private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] node list.AddRange(nodes); list.Sort(); engine.SnapshotCache.Add(key, new StorageItem(list)); - + if (engine.IsHardforkEnabled(Hardfork.HF_Echidna)) { var oldNodes = new VM.Types.Array(engine.ReferenceCounter, GetDesignatedByRole(engine.Snapshot, role, index - 1).Select(u => (ByteString)u.EncodePoint(true))); From ee214275092a06cf013d4da27cbecaaa1208507d Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 23 Oct 2024 12:10:12 -0700 Subject: [PATCH 3/5] Update src/Neo/SmartContract/ApplicationEngine.Runtime.cs --- src/Neo/SmartContract/ApplicationEngine.Runtime.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs index d50b7df5f8..83c09340fb 100644 --- a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs @@ -404,7 +404,7 @@ protected internal void SendNotification(UInt160 hash, string eventName, Array s // 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) + if (IsHardforkEnabled(Hardfork.HF_Echidna) && Trigger == TriggerType.Application && notifications.Count >= MaxNotificationCount) { throw new InvalidOperationException($"Maximum number of notifications `{MaxNotificationCount}` is reached."); } From 4a6f36afe29296a4a76a0c2545edf65097521cb3 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 23 Oct 2024 12:11:50 -0700 Subject: [PATCH 4/5] Avoid notification creation --- src/Neo/SmartContract/ApplicationEngine.Runtime.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs index 83c09340fb..692084a3ae 100644 --- a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs @@ -399,7 +399,6 @@ protected internal void RuntimeNotifyV1(byte[] eventName, Array state) /// The arguments of the event. protected internal void SendNotification(UInt160 hash, string eventName, Array state) { - NotifyEventArgs notification = new(ScriptContainer, hash, eventName, (Array)state.DeepCopy(asImmutable: true)); 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 @@ -408,6 +407,7 @@ protected internal void SendNotification(UInt160 hash, string eventName, Array s { throw new InvalidOperationException($"Maximum number of notifications `{MaxNotificationCount}` is reached."); } + NotifyEventArgs notification = new(ScriptContainer, hash, eventName, (Array)state.DeepCopy(asImmutable: true)); Notify?.Invoke(this, notification); notifications.Add(notification); CurrentContext.GetState().NotificationCount++; From 9a657a538a16b72eb1315bde230854919f82311b Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Sun, 17 Nov 2024 09:31:55 -0500 Subject: [PATCH 5/5] fix format --- src/Neo/SmartContract/Native/RoleManagement.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/RoleManagement.cs b/src/Neo/SmartContract/Native/RoleManagement.cs index 1a0cdd57f8..2577300720 100644 --- a/src/Neo/SmartContract/Native/RoleManagement.cs +++ b/src/Neo/SmartContract/Native/RoleManagement.cs @@ -79,7 +79,6 @@ private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] node list.AddRange(nodes); list.Sort(); engine.SnapshotCache.Add(key, new StorageItem(list)); - if (engine.IsHardforkEnabled(Hardfork.HF_Echidna)) { var oldNodes = new VM.Types.Array(engine.ReferenceCounter, GetDesignatedByRole(engine.Snapshot, role, index - 1).Select(u => (ByteString)u.EncodePoint(true)));