Merge pull request #3485 from nspcc-dev/test-maxstacksize

This commit is contained in:
Roman Khimov 2024-06-11 19:04:26 +03:00 committed by GitHub
commit 02627e948f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 83 additions and 2 deletions

View file

@ -2620,3 +2620,78 @@ func TestBlockchain_StoreAsTransaction_ExecutableConflict(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 2, len(aer))
}
// TestEngineLimits ensures that MaxStackSize limit is preserved during System.Runtime.GetNotifications
// syscall handling. This test is an adjusted port of https://github.com/lazynode/Tanya/pull/33 and makes
// sure that NeoGo node is not affected by https://github.com/neo-project/neo/issues/3300 and does not need
// the https://github.com/neo-project/neo/pull/3301.
func TestEngineLimits(t *testing.T) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
src := `package test
import (
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)
// args is an array of LargeEvent parameters containing 500 empty strings.
var args = []any{"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" };
func ProduceNumerousNotifications(count int) [][]any {
for i := 0; i < count; i++ {
runtime.Notify("LargeEvent", args...)
}
return runtime.GetNotifications(runtime.GetExecutingScriptHash())
}
func ProduceLargeObject(count int) int {
for i := 0; i < count; i++ {
runtime.Notify("LargeEvent", args...)
}
var (
smallObject = make([]int, 100)
res []int
)
for i := 0; i < count; i++ {
runtime.GetNotifications(runtime.GetExecutingScriptHash())
res = append(res, smallObject...)
}
return len(res)
}`
const eArgsCount = 500
eParams := make([]compiler.HybridParameter, eArgsCount)
for i := range eParams {
eParams[i].Name = fmt.Sprintf("str%d", i)
eParams[i].Type = smartcontract.ByteArrayType
}
c := neotest.CompileSource(t, acc.ScriptHash(), strings.NewReader(src), &compiler.Options{
Name: "test_contract",
ContractEvents: []compiler.HybridEvent{
{
Name: "LargeEvent",
Parameters: eParams,
},
},
})
e.DeployContract(t, c, nil)
// ProduceNumerousNotifications: 1 iteration, no limits are hit.
var args = make([]stackitem.Item, eArgsCount)
for i := range args {
args[i] = stackitem.Make("")
}
cInv := e.NewInvoker(c.Hash, acc)
cInv.Invoke(t, stackitem.Make([]stackitem.Item{
stackitem.Make([]stackitem.Item{
stackitem.Make(c.Hash),
stackitem.Make("LargeEvent"),
stackitem.Make(args),
}),
}), "produceNumerousNotifications", 1)
// ProduceNumerousNotifications: hit the limit.
cInv.InvokeFail(t, "stack is too big", "produceNumerousNotifications", 500)
// ProduceLargeObject: 1 iteration, no limits are hit.
cInv.Invoke(t, 100*1, "produceLargeObject", 1)
// ProduceLargeObject: hit the limit.
cInv.InvokeFail(t, "stack is too big", "produceLargeObject", 500)
}

View file

@ -128,6 +128,12 @@ func Make(v any) Item {
a = append(a, Make(i))
}
return Make(a)
case []string:
var a []Item
for _, i := range val {
a = append(a, Make(i))
}
return Make(a)
case []any:
res := make([]Item, len(val))
for i := range val {

View file

@ -594,7 +594,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
err = newError(ctx.ip, op, errRecover)
} else if v.refs > MaxStackSize {
v.state = vmstate.Fault
err = newError(ctx.ip, op, "stack is too big")
err = newError(ctx.ip, op, fmt.Sprintf("stack is too big: %d vs %d", int(v.refs), MaxStackSize))
}
}()
@ -1995,7 +1995,7 @@ func validateMapKey(key Element) {
func (v *VM) checkInvocationStackSize() {
if len(v.istack) >= MaxInvocationStackSize {
panic("invocation stack is too big")
panic(fmt.Sprintf("invocation stack is too big: %d", len(v.istack)))
}
}