mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-21 11:51:03 +00:00
core: restrict the number of allowed SC notifications
Close #3490. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
7b09812069
commit
59d942ef0d
7 changed files with 80 additions and 25 deletions
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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{}
|
||||
}
|
||||
|
||||
|
@ -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{}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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(),
|
||||
|
|
Loading…
Reference in a new issue