-
Notifications
You must be signed in to change notification settings - Fork 1k
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
SmartContract: restrict the number of allowed notifications #3548
base: HF_Echidna
Are you sure you want to change the base?
Conversation
Fix the problem described in nspcc-dev/neo-go#3490. Port the solution from nspcc-dev/neo-go#3640. MaxNotificationsCount constraint is chosen based on the Mainnet statistics of the number of notifications per every transaction, ref. nspcc-dev/neo-go#3490 (comment). Signed-off-by: Anna Shaleva <[email protected]>
Signed-off-by: Anna Shaleva <[email protected]>
This needs to be in VM limit |
@@ -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)); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting job is failing without this change.
Strictly speaking, it's not a VM limit. Notifications are not a part of VM, they are a part of execution engine. |
// 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, we just need to ensure .Count is correct calculated at this step.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is trigger really needed? Limits shouldn't have a trigger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's needed, because we can't allow OnPersist and PostPersist failures (imagine that in future MaxNotificationCount may be reduced to some lower value). And for Verification trigger this check is useless by design, no notifications are possible with this trigger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the same thing. Class is called |
instead of limiting number of notifactions, i prefer to make the price related to the notifaction size. From @AnnaShaleva 's excellent benchmark, i think size * number is the real reason. Cause i think its a normal practice contract may need to send a lof of notifactions, but definately abnormal to send large notifactions. |
Exactly, as described in nspcc-dev/neo-go#3490 (comment).
Yes, it's an alternative solution proposed in nspcc-dev/neo-go#3490 (comment). But we need to discuss it because even with extremely expensive notifications there's a chance that CNs will be killed by a single transaction. Yes, the user will have to pay for it, but CNs may not be able to process this transaction. Also, large number of notifications is harmful for both C# and Go RPC servers. So to me, the restriction of the overall notifications number is required, but at the same time it can be combined with dynamic price solution. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with @Jim8y pay extra per notification size
Economics. Currently Storage fee price is 10K datoshis per byte for mainnet. Typical NEP-17 Based on nspcc-dev/neo-go#3490 even 10K are problematic (204.8 GAS with 2K per byte), so we better target at 2-5K as being practically impossible. Which doesn't really work even with 10K per byte since 10K events would cost the same 1024 GAS then. (side note: @steven1227, these calculations are also relevant for any fee reduction talks) Not sure we can balance this economically. |
More than X notifications (100?), can be priced exponentially by the count. |
Yeah, I had this thought as well, but you can easily avoid it with some number of transactions. Maybe they'd be a little less problematic than a single one, but still. |
/// <summary> | ||
/// The maximum number of notifications per application execution. | ||
/// </summary> | ||
public const int MaxNotificationCount = 512; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ExecutionEngineLimits
Class engine.Limits.MaxNotificationCount
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notifications is a syscall, outside from VM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its still an Engine
Limitation, just cause the VM is splitted into two libraries shouldn't make it be to separate things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have MaxNotificationSize
and MaxEventName
declared in the same class as MaxNotificationCount
, these three constants must be kept together.
Description
The problem is described in nspcc-dev/neo-go#3490. In short words, an arbitrary number of smart contract notifications is allowed (up to VM's GasLimit and MaxBlockSystemFee, but it's huge), which results in a possibility to DoS the node. 10K of large notifications emitted in a single transaction result in block acceptance delays for both Go and C# nodes. 50K of large notifications may result in the node failure. I did these tests on privnet, you may find the results in nspcc-dev/neo-go#3490 (comment). Please, don't try to DoS mainnet/testnet using this vulnerability.
Port the solution from nspcc-dev/neo-go#3640, but an alternative solution is described in nspcc-dev/neo-go#3640 (comment) and in nspcc-dev/neo-go#3490 (comment). MaxNotificationsCount constraint is chosen based on the Mainnet statistics of the number of notifications per every transaction, ref. nspcc-dev/neo-go#3490 (comment).
This PR is a subject of discussion, hence please, share your thoughts on the described problem and proposed solution. Once we agree on the solution, I'll add unit-tests to this PR.
Type of change
How Has This Been Tested?
Checklist: