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

Notification limits #1100

Merged
merged 10 commits into from
Oct 18, 2019
61 changes: 57 additions & 4 deletions neo/SmartContract/InteropService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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<uint, InteropDescriptor> methods = new Dictionary<uint, InteropDescriptor>();

Expand All @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is not very expensive?

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);
Expand Down Expand Up @@ -64,6 +67,52 @@ 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)
shargon marked this conversation as resolved.
Show resolved Hide resolved
{
int size = 0;
List<StackItem> items_checked = new List<StackItem>();
Queue<StackItem> items_unchecked = new Queue<StackItem>();
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 InteropInterface _:
shargon marked this conversation as resolved.
Show resolved Hide resolved
return false;
case Map map:
shargon marked this conversation as resolved.
Show resolved Hide resolved
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;
shargon marked this conversation as resolved.
Show resolved Hide resolved
state = items_unchecked.Dequeue();
}
}

shargon marked this conversation as resolved.
Show resolved Hide resolved
private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context)
{
ContractState contract = engine.Snapshot.Contracts.TryGet(context.ScriptHash);
Expand Down Expand Up @@ -200,13 +249,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;
shargon marked this conversation as resolved.
Show resolved Hide resolved
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;
shargon marked this conversation as resolved.
Show resolved Hide resolved
string message = Encoding.UTF8.GetString(state);
engine.SendLog(engine.CurrentScriptHash, message);
return true;
}
Expand Down