github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/native_test/common_test.go (about) 1 package native_test 2 3 import ( 4 "math/big" 5 "testing" 6 7 "github.com/nspcc-dev/neo-go/pkg/config" 8 "github.com/nspcc-dev/neo-go/pkg/core/native" 9 "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" 10 "github.com/nspcc-dev/neo-go/pkg/core/state" 11 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 12 "github.com/nspcc-dev/neo-go/pkg/io" 13 "github.com/nspcc-dev/neo-go/pkg/neotest" 14 "github.com/nspcc-dev/neo-go/pkg/neotest/chain" 15 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 16 "github.com/nspcc-dev/neo-go/pkg/vm/emit" 17 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 18 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 19 "github.com/stretchr/testify/require" 20 ) 21 22 func newNativeClient(t *testing.T, name string) *neotest.ContractInvoker { 23 return newCustomNativeClient(t, name, nil) 24 } 25 26 func newCustomNativeClient(t *testing.T, name string, f func(cfg *config.Blockchain)) *neotest.ContractInvoker { 27 bc, acc := chain.NewSingleWithCustomConfig(t, f) 28 e := neotest.NewExecutor(t, bc, acc, acc) 29 30 return e.CommitteeInvoker(e.NativeHash(t, name)) 31 } 32 33 func testGetSet(t *testing.T, c *neotest.ContractInvoker, name string, defaultValue, minValue, maxValue int64) { 34 getName := "get" + name 35 setName := "set" + name 36 37 randomInvoker := c.WithSigners(c.NewAccount(t)) 38 committeeInvoker := c.WithSigners(c.Committee) 39 40 t.Run("set, not signed by committee", func(t *testing.T) { 41 randomInvoker.InvokeFail(t, "invalid committee signature", setName, minValue+1) 42 }) 43 t.Run("get, default value", func(t *testing.T) { 44 randomInvoker.Invoke(t, defaultValue, getName) 45 }) 46 t.Run("set, too small value", func(t *testing.T) { 47 committeeInvoker.InvokeFail(t, "", setName, minValue-1) 48 }) 49 50 if maxValue != 0 { 51 t.Run("set, too large value", func(t *testing.T) { 52 // use big.Int because max can be `math.MaxInt64` 53 max := big.NewInt(maxValue) 54 max.Add(max, big.NewInt(1)) 55 committeeInvoker.InvokeFail(t, "", setName, max) 56 }) 57 } 58 59 t.Run("set, success", func(t *testing.T) { 60 // Set and get in the same block. 61 txSet := committeeInvoker.PrepareInvoke(t, setName, defaultValue+1) 62 txGet := randomInvoker.PrepareInvoke(t, getName) 63 c.AddNewBlock(t, txSet, txGet) 64 c.CheckHalt(t, txSet.Hash(), stackitem.Null{}) 65 66 switch name { 67 case "GasPerBlock": 68 // GasPerBlock is set on the next block 69 c.CheckHalt(t, txGet.Hash(), stackitem.Make(defaultValue)) 70 c.AddNewBlock(t) 71 randomInvoker.Invoke(t, defaultValue+1, getName) 72 case "ExecFeeFactor": 73 // ExecFeeFactor was risen, so the second transaction will fail because 74 // of gas limit exceeding (its fees are out-of-date). 75 c.CheckFault(t, txGet.Hash(), "gas limit exceeded") 76 // Set in a separate block. 77 committeeInvoker.Invoke(t, stackitem.Null{}, setName, defaultValue+1) 78 // Get in the next block. 79 randomInvoker.Invoke(t, defaultValue+1, getName) 80 default: 81 c.CheckHalt(t, txGet.Hash(), stackitem.Make(defaultValue+1)) 82 // Get in the next block. 83 randomInvoker.Invoke(t, defaultValue+1, getName) 84 } 85 }) 86 } 87 88 func testGetSetCache(t *testing.T, c *neotest.ContractInvoker, name string, defaultValue int64) { 89 getName := "get" + name 90 setName := "set" + name 91 92 committeeInvoker := c.WithSigners(c.Committee) 93 94 newVal := defaultValue - 1 95 96 // Change fee, abort the transaction and check that contract cache wasn't persisted 97 // for FAULTed tx at the same block. 98 w := io.NewBufBinWriter() 99 emit.AppCall(w.BinWriter, committeeInvoker.Hash, setName, callflag.All, newVal) 100 emit.Opcodes(w.BinWriter, opcode.ABORT) 101 tx1 := committeeInvoker.PrepareInvocation(t, w.Bytes(), committeeInvoker.Signers) 102 tx2 := committeeInvoker.PrepareInvoke(t, getName) 103 committeeInvoker.AddNewBlock(t, tx1, tx2) 104 committeeInvoker.CheckFault(t, tx1.Hash(), "ABORT") 105 committeeInvoker.CheckHalt(t, tx2.Hash(), stackitem.Make(defaultValue)) 106 107 // Change fee and check that change is available for the next tx. 108 tx1 = committeeInvoker.PrepareInvoke(t, setName, newVal) 109 tx2 = committeeInvoker.PrepareInvoke(t, getName) 110 committeeInvoker.AddNewBlock(t, tx1, tx2) 111 committeeInvoker.CheckHalt(t, tx1.Hash()) 112 if name != "GasPerBlock" { 113 committeeInvoker.CheckHalt(t, tx2.Hash(), stackitem.Make(newVal)) 114 } else { 115 committeeInvoker.CheckHalt(t, tx2.Hash(), stackitem.Make(defaultValue)) 116 committeeInvoker.Invoke(t, newVal, getName) 117 } 118 } 119 120 func setNodesByRole(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, nodes keys.PublicKeys) { 121 pubs := make([]any, len(nodes)) 122 for i := range nodes { 123 pubs[i] = nodes[i].Bytes() 124 } 125 if ok { 126 h := designateInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int64(r), pubs) 127 designateInvoker.CheckTxNotificationEvent(t, h, 0, state.NotificationEvent{ 128 ScriptHash: designateInvoker.Hash, 129 Name: native.DesignationEventName, 130 Item: stackitem.NewArray([]stackitem.Item{ 131 stackitem.Make(int64(r)), 132 stackitem.Make(designateInvoker.Chain.BlockHeight()), 133 }), 134 }) 135 } else { 136 designateInvoker.InvokeFail(t, "", "designateAsRole", int64(r), pubs) 137 } 138 } 139 140 func checkNodeRoles(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, index uint32, res keys.PublicKeys) { 141 if ok { 142 designateInvoker.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { 143 require.Equal(t, 1, len(stack)) 144 arr := stack[0].Value().([]stackitem.Item) 145 require.Equal(t, len(res), len(arr)) 146 for i := range arr { 147 require.Equal(t, res[i].Bytes(), arr[i].Value().([]byte), i) 148 } 149 }, "getDesignatedByRole", int64(r), int64(index)) 150 } else { 151 designateInvoker.InvokeFail(t, "", "getDesignatedByRole", int64(r), int64(index)) 152 } 153 }