forked from TrueCloudLab/neoneo-go
148 lines
5.2 KiB
Go
148 lines
5.2 KiB
Go
package native_test
|
|
|
|
import (
|
|
"math/big"
|
|
"testing"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func newNativeClient(t *testing.T, name string) *neotest.ContractInvoker {
|
|
bc, acc := chain.NewSingle(t)
|
|
e := neotest.NewExecutor(t, bc, acc, acc)
|
|
|
|
return e.CommitteeInvoker(e.NativeHash(t, name))
|
|
}
|
|
|
|
func testGetSet(t *testing.T, c *neotest.ContractInvoker, name string, defaultValue, minValue, maxValue int64) {
|
|
getName := "get" + name
|
|
setName := "set" + name
|
|
|
|
randomInvoker := c.WithSigners(c.NewAccount(t))
|
|
committeeInvoker := c.WithSigners(c.Committee)
|
|
|
|
t.Run("set, not signed by committee", func(t *testing.T) {
|
|
randomInvoker.InvokeFail(t, "invalid committee signature", setName, minValue+1)
|
|
})
|
|
t.Run("get, default value", func(t *testing.T) {
|
|
randomInvoker.Invoke(t, defaultValue, getName)
|
|
})
|
|
t.Run("set, too small value", func(t *testing.T) {
|
|
committeeInvoker.InvokeFail(t, "", setName, minValue-1)
|
|
})
|
|
|
|
if maxValue != 0 {
|
|
t.Run("set, too large value", func(t *testing.T) {
|
|
// use big.Int because max can be `math.MaxInt64`
|
|
max := big.NewInt(maxValue)
|
|
max.Add(max, big.NewInt(1))
|
|
committeeInvoker.InvokeFail(t, "", setName, max)
|
|
})
|
|
}
|
|
|
|
t.Run("set, success", func(t *testing.T) {
|
|
// Set and get in the same block.
|
|
txSet := committeeInvoker.PrepareInvoke(t, setName, defaultValue+1)
|
|
txGet := randomInvoker.PrepareInvoke(t, getName)
|
|
c.AddNewBlock(t, txSet, txGet)
|
|
c.CheckHalt(t, txSet.Hash(), stackitem.Null{})
|
|
|
|
switch name {
|
|
case "GasPerBlock":
|
|
// GasPerBlock is set on the next block
|
|
c.CheckHalt(t, txGet.Hash(), stackitem.Make(defaultValue))
|
|
c.AddNewBlock(t)
|
|
randomInvoker.Invoke(t, defaultValue+1, getName)
|
|
case "ExecFeeFactor":
|
|
// ExecFeeFactor was risen, so the second transaction will fail because
|
|
// of gas limit exceeding (its fees are out-of-date).
|
|
c.CheckFault(t, txGet.Hash(), "gas limit exceeded")
|
|
// Set in a separate block.
|
|
committeeInvoker.Invoke(t, stackitem.Null{}, setName, defaultValue+1)
|
|
// Get in the next block.
|
|
randomInvoker.Invoke(t, defaultValue+1, getName)
|
|
default:
|
|
c.CheckHalt(t, txGet.Hash(), stackitem.Make(defaultValue+1))
|
|
// Get in the next block.
|
|
randomInvoker.Invoke(t, defaultValue+1, getName)
|
|
}
|
|
})
|
|
}
|
|
|
|
func testGetSetCache(t *testing.T, c *neotest.ContractInvoker, name string, defaultValue int64) {
|
|
getName := "get" + name
|
|
setName := "set" + name
|
|
|
|
committeeInvoker := c.WithSigners(c.Committee)
|
|
|
|
newVal := defaultValue - 1
|
|
|
|
// Change fee, abort the transaction and check that contract cache wasn't persisted
|
|
// for FAULTed tx at the same block.
|
|
w := io.NewBufBinWriter()
|
|
emit.AppCall(w.BinWriter, committeeInvoker.Hash, setName, callflag.All, newVal)
|
|
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
|
tx1 := committeeInvoker.PrepareInvocation(t, w.Bytes(), committeeInvoker.Signers)
|
|
tx2 := committeeInvoker.PrepareInvoke(t, getName)
|
|
committeeInvoker.AddNewBlock(t, tx1, tx2)
|
|
committeeInvoker.CheckFault(t, tx1.Hash(), "ABORT")
|
|
committeeInvoker.CheckHalt(t, tx2.Hash(), stackitem.Make(defaultValue))
|
|
|
|
// Change fee and check that change is available for the next tx.
|
|
tx1 = committeeInvoker.PrepareInvoke(t, setName, newVal)
|
|
tx2 = committeeInvoker.PrepareInvoke(t, getName)
|
|
committeeInvoker.AddNewBlock(t, tx1, tx2)
|
|
committeeInvoker.CheckHalt(t, tx1.Hash())
|
|
if name != "GasPerBlock" {
|
|
committeeInvoker.CheckHalt(t, tx2.Hash(), stackitem.Make(newVal))
|
|
} else {
|
|
committeeInvoker.CheckHalt(t, tx2.Hash(), stackitem.Make(defaultValue))
|
|
committeeInvoker.Invoke(t, newVal, getName)
|
|
}
|
|
}
|
|
|
|
func setNodesByRole(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, nodes keys.PublicKeys) {
|
|
pubs := make([]interface{}, len(nodes))
|
|
for i := range nodes {
|
|
pubs[i] = nodes[i].Bytes()
|
|
}
|
|
if ok {
|
|
h := designateInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int64(r), pubs)
|
|
designateInvoker.CheckTxNotificationEvent(t, h, 0, state.NotificationEvent{
|
|
ScriptHash: designateInvoker.Hash,
|
|
Name: native.DesignationEventName,
|
|
Item: stackitem.NewArray([]stackitem.Item{
|
|
stackitem.Make(int64(r)),
|
|
stackitem.Make(designateInvoker.Chain.BlockHeight()),
|
|
}),
|
|
})
|
|
} else {
|
|
designateInvoker.InvokeFail(t, "", "designateAsRole", int64(r), pubs)
|
|
}
|
|
}
|
|
|
|
func checkNodeRoles(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, index uint32, res keys.PublicKeys) {
|
|
if ok {
|
|
designateInvoker.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
|
require.Equal(t, 1, len(stack))
|
|
arr := stack[0].Value().([]stackitem.Item)
|
|
require.Equal(t, len(res), len(arr))
|
|
for i := range arr {
|
|
require.Equal(t, res[i].Bytes(), arr[i].Value().([]byte), i)
|
|
}
|
|
}, "getDesignatedByRole", int64(r), int64(index))
|
|
} else {
|
|
designateInvoker.InvokeFail(t, "", "getDesignatedByRole", int64(r), int64(index))
|
|
}
|
|
}
|