diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 0e891de162..c647cb84fc 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -14,6 +14,8 @@ using System.Linq; using System.Numerics; using System.Text; +using Array = Neo.VM.Types.Array; +using Boolean = Neo.VM.Types.Boolean; namespace Neo.SmartContract { @@ -22,6 +24,7 @@ public static partial class InteropService public const long GasPerByte = 100000; public const int MaxStorageKeySize = 64; public const int MaxStorageValueSize = ushort.MaxValue; + public const int MaxNotificationSize = 1024; private static readonly Dictionary methods = new Dictionary(); @@ -32,8 +35,8 @@ public static partial class InteropService public static readonly uint System_Runtime_Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All); public static readonly uint System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All); public static readonly uint System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All); - public static readonly uint System_Runtime_Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_00000250, TriggerType.All); - public static readonly uint System_Runtime_Log = Register("System.Runtime.Log", Runtime_Log, 0_00300000, TriggerType.All); + public static readonly uint System_Runtime_Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_01000000, TriggerType.All); + public static readonly uint System_Runtime_Log = Register("System.Runtime.Log", Runtime_Log, 0_01000000, TriggerType.All); public static readonly uint System_Runtime_GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application); public static readonly uint System_Runtime_Serialize = Register("System.Runtime.Serialize", Runtime_Serialize, 0_00100000, TriggerType.All); public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000, TriggerType.All); @@ -56,6 +59,54 @@ public static partial class InteropService public static readonly uint System_Storage_Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application); public static readonly uint System_StorageContext_AsReadOnly = Register("System.StorageContext.AsReadOnly", StorageContext_AsReadOnly, 0_00000400, TriggerType.Application); + private static bool CheckItemForNotification(StackItem state) + { + int size = 0; + List items_checked = new List(); + Queue items_unchecked = new Queue(); + while (true) + { + switch (state) + { + case Struct array: + foreach (StackItem item in array) + items_unchecked.Enqueue(item); + break; + case Array array: + if (items_checked.All(p => !ReferenceEquals(p, array))) + { + items_checked.Add(array); + foreach (StackItem item in array) + items_unchecked.Enqueue(item); + } + break; + case Boolean _: + case ByteArray _: + case Integer _: + size += state.GetByteLength(); + break; + case Null _: + break; + case InteropInterface _: + return false; + case Map map: + if (items_checked.All(p => !ReferenceEquals(p, map))) + { + items_checked.Add(map); + foreach (var pair in map) + { + size += pair.Key.GetByteLength(); + items_unchecked.Enqueue(pair.Value); + } + } + break; + } + if (size > MaxNotificationSize) return false; + if (items_unchecked.Count == 0) return true; + state = items_unchecked.Dequeue(); + } + } + private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context) { ContractState contract = engine.Snapshot.Contracts.TryGet(context.ScriptHash); @@ -194,13 +245,17 @@ private static bool Runtime_CheckWitness(ApplicationEngine engine) private static bool Runtime_Notify(ApplicationEngine engine) { - engine.SendNotification(engine.CurrentScriptHash, engine.CurrentContext.EvaluationStack.Pop()); + StackItem state = engine.CurrentContext.EvaluationStack.Pop(); + if (!CheckItemForNotification(state)) return false; + engine.SendNotification(engine.CurrentScriptHash, state); return true; } private static bool Runtime_Log(ApplicationEngine engine) { - string message = Encoding.UTF8.GetString(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); + byte[] state = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + if (state.Length > MaxNotificationSize) return false; + string message = Encoding.UTF8.GetString(state); engine.SendLog(engine.CurrentScriptHash, message); return true; }