mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-28 19:31:34 +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
|
DefaultBaseExecFee = 30
|
||||||
// ContextNonceDataLen is a length of [Context.NonceData] in bytes.
|
// ContextNonceDataLen is a length of [Context.NonceData] in bytes.
|
||||||
ContextNonceDataLen = 16
|
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.
|
// 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.
|
// 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{
|
ic.Notifications = append(ic.Notifications, state.NotificationEvent{
|
||||||
ScriptHash: hash,
|
ScriptHash: hash,
|
||||||
Name: name,
|
Name: name,
|
||||||
Item: item,
|
Item: item,
|
||||||
})
|
})
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,8 +114,7 @@ func Notify(ic *interop.Context) error {
|
||||||
if len(bytes) > MaxNotificationSize {
|
if len(bytes) > MaxNotificationSize {
|
||||||
return fmt.Errorf("notification size shouldn't exceed %d", MaxNotificationSize)
|
return fmt.Errorf("notification size shouldn't exceed %d", MaxNotificationSize)
|
||||||
}
|
}
|
||||||
ic.AddNotification(curHash, name, stackitem.DeepCopy(stackitem.NewArray(args), true).(*stackitem.Array))
|
return ic.AddNotification(curHash, name, stackitem.DeepCopy(stackitem.NewArray(args), true).(*stackitem.Array))
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadScript takes a script and arguments from the stack and loads it into the VM.
|
// 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)
|
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(r))),
|
||||||
stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))),
|
stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))),
|
||||||
}))
|
}))
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Designate) getRole(item stackitem.Item) (noderoles.Role, bool) {
|
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)
|
panic(err)
|
||||||
}
|
}
|
||||||
m.callDeploy(ic, newcontract, args[2], false)
|
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)
|
return contractToStack(newcontract)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +447,10 @@ func (m *Management) updateWithData(ic *interop.Context, args []stackitem.Item)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
m.callDeploy(ic, contract, args[2], true)
|
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{}
|
return stackitem.Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,7 +503,10 @@ func (m *Management) destroy(ic *interop.Context, sis []stackitem.Item) stackite
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
m.emitNotification(ic, contractDestroyNotificationName, hash)
|
err = m.emitNotification(ic, contractDestroyNotificationName, hash)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return stackitem.Null{}
|
return stackitem.Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,7 +690,10 @@ func (m *Management) OnPersist(ic *interop.Context) error {
|
||||||
if isUpdate {
|
if isUpdate {
|
||||||
ntfName = contractUpdateNotificationName
|
ntfName = contractUpdateNotificationName
|
||||||
}
|
}
|
||||||
m.emitNotification(ic, ntfName, cs.Hash)
|
err = m.emitNotification(ic, ntfName, cs.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -806,8 +818,8 @@ func (m *Management) getNextContractID(d *dao.Simple) (int32, error) {
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) {
|
func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) error {
|
||||||
ic.AddNotification(m.Hash, name, stackitem.NewArray([]stackitem.Item{addrToStackItem(&hash)}))
|
return ic.AddNotification(m.Hash, name, stackitem.NewArray([]stackitem.Item{addrToStackItem(&hash)}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkScriptAndMethods(ic *interop.Context, script []byte, methods []manifest.Method) error {
|
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()))
|
ic.DAO.PutStorageItem(n.ID, prefixCommittee, cache.committee.Bytes(ic.DAO.GetItemCtx()))
|
||||||
|
|
||||||
if oldCommittee != nil {
|
if oldCommittee != nil {
|
||||||
ic.AddNotification(n.Hash, "CommitteeChanged", stackitem.NewArray([]stackitem.Item{
|
err := ic.AddNotification(n.Hash, "CommitteeChanged", stackitem.NewArray([]stackitem.Item{
|
||||||
oldCommittee, newCommittee,
|
oldCommittee, newCommittee,
|
||||||
}))
|
}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -828,7 +831,8 @@ func (n *NEO) registerCandidate(ic *interop.Context, args []stackitem.Item) stac
|
||||||
return stackitem.NewBool(err == nil)
|
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 {
|
func (n *NEO) RegisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
||||||
var emitEvent = true
|
var emitEvent = true
|
||||||
|
|
||||||
|
@ -843,16 +847,23 @@ func (n *NEO) RegisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey
|
||||||
c.Registered = true
|
c.Registered = true
|
||||||
}
|
}
|
||||||
err := putConvertibleToDAO(n.ID, ic.DAO, key, c)
|
err := putConvertibleToDAO(n.ID, ic.DAO, key, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if emitEvent {
|
if emitEvent {
|
||||||
cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
|
cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
|
||||||
cache.votesChanged = true
|
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.NewByteArray(pub.Bytes()),
|
||||||
stackitem.NewBool(c.Registered),
|
stackitem.NewBool(c.Registered),
|
||||||
stackitem.NewBigInteger(&c.Votes),
|
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 {
|
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)
|
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 {
|
func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -887,14 +899,21 @@ func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicK
|
||||||
if !ok {
|
if !ok {
|
||||||
err = putConvertibleToDAO(n.ID, ic.DAO, key, c)
|
err = putConvertibleToDAO(n.ID, ic.DAO, key, c)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if emitEvent {
|
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.NewByteArray(pub.Bytes()),
|
||||||
stackitem.NewBool(c.Registered),
|
stackitem.NewBool(c.Registered),
|
||||||
stackitem.NewBigInteger(&c.Votes),
|
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 {
|
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)
|
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 {
|
func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.PublicKey) error {
|
||||||
ok, err := runtime.CheckHashedWitness(ic, h)
|
ok, err := runtime.CheckHashedWitness(ic, h)
|
||||||
if err != nil {
|
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.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()),
|
stackitem.NewByteArray(h.BytesBE()),
|
||||||
keyToStackItem(oldVote),
|
keyToStackItem(oldVote),
|
||||||
keyToStackItem(pub),
|
keyToStackItem(pub),
|
||||||
stackitem.NewBigInteger(&acc.Balance),
|
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.
|
if newGas != nil { // Can be if it was already distributed in the same block.
|
||||||
n.GAS.mint(ic, h, newGas, true)
|
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 {
|
if to == nil || !callOnPayment {
|
||||||
return
|
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) {
|
func (c *nep17TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) error {
|
||||||
ic.AddNotification(c.Hash, "Transfer", stackitem.NewArray([]stackitem.Item{
|
return ic.AddNotification(c.Hash, "Transfer", stackitem.NewArray([]stackitem.Item{
|
||||||
addrToStackItem(from),
|
addrToStackItem(from),
|
||||||
addrToStackItem(to),
|
addrToStackItem(to),
|
||||||
stackitem.NewBigInteger(amount),
|
stackitem.NewBigInteger(amount),
|
||||||
|
|
|
@ -316,10 +316,13 @@ func (o *Oracle) FinishInternal(ic *interop.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrRequestNotFound
|
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(resp.ID),
|
||||||
stackitem.Make(req.OriginalTxID.BytesBE()),
|
stackitem.Make(req.OriginalTxID.BytesBE()),
|
||||||
}))
|
}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
origTx, _, err := ic.DAO.GetTransaction(req.OriginalTxID)
|
origTx, _, err := ic.DAO.GetTransaction(req.OriginalTxID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -422,12 +425,15 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url string, filter *string
|
||||||
} else {
|
} else {
|
||||||
filterNotif = stackitem.Null{}
|
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(id),
|
||||||
stackitem.Make(ic.VM.GetCallingScriptHash().BytesBE()),
|
stackitem.Make(ic.VM.GetCallingScriptHash().BytesBE()),
|
||||||
stackitem.Make(url),
|
stackitem.Make(url),
|
||||||
filterNotif,
|
filterNotif,
|
||||||
}))
|
}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
req := &state.OracleRequest{
|
req := &state.OracleRequest{
|
||||||
OriginalTxID: o.getOriginalTxID(ic.DAO, ic.Tx),
|
OriginalTxID: o.getOriginalTxID(ic.DAO, ic.Tx),
|
||||||
GasForResponse: gas.Uint64(),
|
GasForResponse: gas.Uint64(),
|
||||||
|
|
Loading…
Reference in a new issue