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

core: restrict the number of allowed SC notifications #3640

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion pkg/core/interop/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const (
DefaultBaseExecFee = 30
// ContextNonceDataLen is a length of [Context.NonceData] in bytes.
ContextNonceDataLen = 16
// MaxNotificationCount is the maximum number of notifications per a single
// application execution.
MaxNotificationCount = 512
)

// Ledger is the interface to Blockchain required for Context functionality.
Expand Down Expand Up @@ -564,10 +567,18 @@ func (ic *Context) IsHardforkActivation(hf config.Hardfork) bool {
}

// AddNotification creates notification event and appends it to the notification list.
func (ic *Context) AddNotification(hash util.Uint160, name string, item *stackitem.Array) {
func (ic *Context) AddNotification(hash util.Uint160, name string, item *stackitem.Array) error {
if ic.IsHardforkEnabled(config.HFEchidna) {
// Do not check persisting triggers to avoid native persist failure. Do not check
// verification trigger since verification context is loaded with ReadOnly flag.
if ic.Trigger == trigger.Application && len(ic.Notifications) == MaxNotificationCount {
return fmt.Errorf("notification count shouldn't exceed %d", MaxNotificationCount)
}
}
ic.Notifications = append(ic.Notifications, state.NotificationEvent{
ScriptHash: hash,
Name: name,
Item: item,
})
return nil
}
3 changes: 1 addition & 2 deletions pkg/core/interop/runtime/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,7 @@ func Notify(ic *interop.Context) error {
if len(bytes) > MaxNotificationSize {
return fmt.Errorf("notification size shouldn't exceed %d", MaxNotificationSize)
}
ic.AddNotification(curHash, name, stackitem.DeepCopy(stackitem.NewArray(args), true).(*stackitem.Array))
return nil
return ic.AddNotification(curHash, name, stackitem.DeepCopy(stackitem.NewArray(args), true).(*stackitem.Array))
}

// LoadScript takes a script and arguments from the stack and loads it into the VM.
Expand Down
3 changes: 1 addition & 2 deletions pkg/core/native/designate.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,11 +412,10 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs
return fmt.Errorf("failed to update Designation role data cache: %w", err)
}

ic.AddNotification(s.Hash, DesignationEventName, stackitem.NewArray([]stackitem.Item{
return ic.AddNotification(s.Hash, DesignationEventName, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(r))),
stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))),
}))
return nil
}

func (s *Designate) getRole(item stackitem.Item) (noderoles.Role, bool) {
Expand Down
24 changes: 18 additions & 6 deletions pkg/core/native/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,10 @@ func (m *Management) deployWithData(ic *interop.Context, args []stackitem.Item)
panic(err)
}
m.callDeploy(ic, newcontract, args[2], false)
m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash)
err = m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash)
if err != nil {
panic(err)
}
return contractToStack(newcontract)
}

Expand Down Expand Up @@ -444,7 +447,10 @@ func (m *Management) updateWithData(ic *interop.Context, args []stackitem.Item)
panic(err)
}
m.callDeploy(ic, contract, args[2], true)
m.emitNotification(ic, contractUpdateNotificationName, contract.Hash)
err = m.emitNotification(ic, contractUpdateNotificationName, contract.Hash)
if err != nil {
panic(err)
}
return stackitem.Null{}
}

Expand Down Expand Up @@ -497,7 +503,10 @@ func (m *Management) destroy(ic *interop.Context, sis []stackitem.Item) stackite
if err != nil {
panic(err)
}
m.emitNotification(ic, contractDestroyNotificationName, hash)
err = m.emitNotification(ic, contractDestroyNotificationName, hash)
if err != nil {
panic(err)
}
return stackitem.Null{}
}

Expand Down Expand Up @@ -681,7 +690,10 @@ func (m *Management) OnPersist(ic *interop.Context) error {
if isUpdate {
ntfName = contractUpdateNotificationName
}
m.emitNotification(ic, ntfName, cs.Hash)
err = m.emitNotification(ic, ntfName, cs.Hash)
if err != nil {
return err
}
}

return nil
Expand Down Expand Up @@ -806,8 +818,8 @@ func (m *Management) getNextContractID(d *dao.Simple) (int32, error) {
return ret, nil
}

func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) {
ic.AddNotification(m.Hash, name, stackitem.NewArray([]stackitem.Item{addrToStackItem(&hash)}))
func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) error {
return ic.AddNotification(m.Hash, name, stackitem.NewArray([]stackitem.Item{addrToStackItem(&hash)}))
}

func checkScriptAndMethods(ic *interop.Context, script []byte, methods []manifest.Method) error {
Expand Down
42 changes: 33 additions & 9 deletions pkg/core/native/native_neo.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,12 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
ic.DAO.PutStorageItem(n.ID, prefixCommittee, cache.committee.Bytes(ic.DAO.GetItemCtx()))

if oldCommittee != nil {
ic.AddNotification(n.Hash, "CommitteeChanged", stackitem.NewArray([]stackitem.Item{
err := ic.AddNotification(n.Hash, "CommitteeChanged", stackitem.NewArray([]stackitem.Item{
oldCommittee, newCommittee,
}))
if err != nil {
return err
}
}
}
return nil
Expand Down Expand Up @@ -828,7 +831,8 @@ func (n *NEO) registerCandidate(ic *interop.Context, args []stackitem.Item) stac
return stackitem.NewBool(err == nil)
}

// RegisterCandidateInternal registers pub as a new candidate.
// RegisterCandidateInternal registers pub as a new candidate. This method must not be
// called outside of VM since it panics on critical errors.
func (n *NEO) RegisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error {
var emitEvent = true

Expand All @@ -843,16 +847,23 @@ func (n *NEO) RegisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey
c.Registered = true
}
err := putConvertibleToDAO(n.ID, ic.DAO, key, c)
if err != nil {
return err
}
if emitEvent {
cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
cache.votesChanged = true
ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{
err = ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(pub.Bytes()),
stackitem.NewBool(c.Registered),
stackitem.NewBigInteger(&c.Votes),
}))
if err != nil {
// Panic since it's a critical error that must abort execution.
panic(err)
}
}
return err
return nil
}

func (n *NEO) unregisterCandidate(ic *interop.Context, args []stackitem.Item) stackitem.Item {
Expand All @@ -867,7 +878,8 @@ func (n *NEO) unregisterCandidate(ic *interop.Context, args []stackitem.Item) st
return stackitem.NewBool(err == nil)
}

// UnregisterCandidateInternal unregisters pub as a candidate.
// UnregisterCandidateInternal unregisters pub as a candidate. This method must not be
// called outside of VM since it panics on critical errors.
func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error {
var err error

Expand All @@ -887,14 +899,21 @@ func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicK
if !ok {
err = putConvertibleToDAO(n.ID, ic.DAO, key, c)
}
if err != nil {
return err
}
if emitEvent {
ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{
err := ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(pub.Bytes()),
stackitem.NewBool(c.Registered),
stackitem.NewBigInteger(&c.Votes),
}))
if err != nil {
// Panic since it's a critical error that must abort execution.
panic(err)
}
}
return err
return nil
}

func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item {
Expand All @@ -907,7 +926,8 @@ func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item {
return stackitem.NewBool(err == nil)
}

// VoteInternal votes from account h for validarors specified in pubs.
// VoteInternal votes from account h for validarors specified in pubs. This method
// must not be called outside of VM since it panics on critical errors.
func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.PublicKey) error {
ok, err := runtime.CheckHashedWitness(ic, h)
if err != nil {
Expand Down Expand Up @@ -969,12 +989,16 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.Public
}
ic.DAO.PutStorageItem(n.ID, key, acc.Bytes(ic.DAO.GetItemCtx()))

ic.AddNotification(n.Hash, "Vote", stackitem.NewArray([]stackitem.Item{
err = ic.AddNotification(n.Hash, "Vote", stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(h.BytesBE()),
keyToStackItem(oldVote),
keyToStackItem(pub),
stackitem.NewBigInteger(&acc.Balance),
}))
if err != nil {
// Panic since it's a critical error that must abort execution.
panic(err)
}

if newGas != nil { // Can be if it was already distributed in the same block.
n.GAS.mint(ic, h, newGas, true)
Expand Down
10 changes: 7 additions & 3 deletions pkg/core/native/native_nep17.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,11 @@ func (c *nep17TokenNative) postTransfer(ic *interop.Context, from, to *util.Uint
}
}
}()
c.emitTransfer(ic, from, to, amount)
err := c.emitTransfer(ic, from, to, amount)
if err != nil {
skipPostCalls = true
panic(err)
}
if to == nil || !callOnPayment {
return
}
Expand All @@ -167,8 +171,8 @@ func (c *nep17TokenNative) postTransfer(ic *interop.Context, from, to *util.Uint
}
}

func (c *nep17TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) {
ic.AddNotification(c.Hash, "Transfer", stackitem.NewArray([]stackitem.Item{
func (c *nep17TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) error {
return ic.AddNotification(c.Hash, "Transfer", stackitem.NewArray([]stackitem.Item{
addrToStackItem(from),
addrToStackItem(to),
stackitem.NewBigInteger(amount),
Expand Down
10 changes: 8 additions & 2 deletions pkg/core/native/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,13 @@ func (o *Oracle) FinishInternal(ic *interop.Context) error {
if err != nil {
return ErrRequestNotFound
}
ic.AddNotification(o.Hash, "OracleResponse", stackitem.NewArray([]stackitem.Item{
err = ic.AddNotification(o.Hash, "OracleResponse", stackitem.NewArray([]stackitem.Item{
stackitem.Make(resp.ID),
stackitem.Make(req.OriginalTxID.BytesBE()),
}))
if err != nil {
return err
}

origTx, _, err := ic.DAO.GetTransaction(req.OriginalTxID)
if err != nil {
Expand Down Expand Up @@ -422,12 +425,15 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url string, filter *string
} else {
filterNotif = stackitem.Null{}
}
ic.AddNotification(o.Hash, "OracleRequest", stackitem.NewArray([]stackitem.Item{
err = ic.AddNotification(o.Hash, "OracleRequest", stackitem.NewArray([]stackitem.Item{
stackitem.Make(id),
stackitem.Make(ic.VM.GetCallingScriptHash().BytesBE()),
stackitem.Make(url),
filterNotif,
}))
if err != nil {
return err
}
req := &state.OracleRequest{
OriginalTxID: o.getOriginalTxID(ic.DAO, ic.Tx),
GasForResponse: gas.Uint64(),
Expand Down
Loading