github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/native_test/gas_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/nativenames" 9 "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" 10 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 11 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 12 "github.com/nspcc-dev/neo-go/pkg/neotest" 13 "github.com/nspcc-dev/neo-go/pkg/neotest/chain" 14 "github.com/nspcc-dev/neo-go/pkg/util" 15 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 16 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func newGasClient(t *testing.T) *neotest.ContractInvoker { 21 return newNativeClient(t, nativenames.Gas) 22 } 23 24 func TestGAS_Roundtrip(t *testing.T) { 25 c := newGasClient(t) 26 e := c.Executor 27 gasInvoker := c.WithSigners(c.NewAccount(t)) 28 owner := gasInvoker.Signers[0].ScriptHash() 29 30 getUtilityTokenBalance := func(acc util.Uint160) (*big.Int, uint32) { 31 lub, err := e.Chain.GetTokenLastUpdated(acc) 32 require.NoError(t, err) 33 return e.Chain.GetUtilityTokenBalance(acc), lub[e.NativeID(t, nativenames.Gas)] 34 } 35 36 initialBalance, _ := getUtilityTokenBalance(owner) 37 require.NotNil(t, initialBalance) 38 39 t.Run("bad: amount > initial balance", func(t *testing.T) { 40 h := gasInvoker.Invoke(t, false, "transfer", owner, owner, initialBalance.Int64()+1, nil) 41 tx, height := e.GetTransaction(t, h) 42 require.Equal(t, 0, len(e.GetTxExecResult(t, h).Events)) // no events (failed transfer) 43 // check balance and height were not changed 44 updatedBalance, updatedHeight := getUtilityTokenBalance(owner) 45 initialBalance.Sub(initialBalance, big.NewInt(tx.SystemFee+tx.NetworkFee)) 46 require.Equal(t, initialBalance, updatedBalance) 47 require.Equal(t, height, updatedHeight) 48 }) 49 50 t.Run("good: amount < initial balance", func(t *testing.T) { 51 h := gasInvoker.Invoke(t, true, "transfer", owner, owner, initialBalance.Int64()-1_0000_0000, nil) 52 tx, height := e.GetTransaction(t, h) 53 require.Equal(t, 1, len(e.GetTxExecResult(t, h).Events)) // roundtrip 54 // check balance wasn't changed and height was updated 55 updatedBalance, updatedHeight := getUtilityTokenBalance(owner) 56 initialBalance.Sub(initialBalance, big.NewInt(tx.SystemFee+tx.NetworkFee)) 57 require.Equal(t, initialBalance, updatedBalance) 58 require.Equal(t, height, updatedHeight) 59 }) 60 } 61 62 func TestGAS_RewardWithP2PSigExtensionsEnabled(t *testing.T) { 63 const ( 64 nNotaries = 2 65 nKeys = 4 66 ) 67 68 bc, validator, committee := chain.NewMultiWithCustomConfig(t, func(cfg *config.Blockchain) { 69 cfg.P2PSigExtensions = true 70 }) 71 e := neotest.NewExecutor(t, bc, validator, committee) 72 gasCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Gas)) 73 notaryHash := e.NativeHash(t, nativenames.Notary) 74 notaryServiceFeePerKey := e.Chain.GetNotaryServiceFeePerKey() 75 76 // transfer funds to committee 77 e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)).Invoke(t, true, "transfer", e.Validator.ScriptHash(), e.CommitteeHash, 1000_0000_0000, nil) 78 79 // set Notary nodes and check their balance 80 notaryNodes := make([]*keys.PrivateKey, nNotaries) 81 notaryNodesPublicKeys := make([]any, nNotaries) 82 var err error 83 for i := range notaryNodes { 84 notaryNodes[i], err = keys.NewPrivateKey() 85 require.NoError(t, err) 86 notaryNodesPublicKeys[i] = notaryNodes[i].PublicKey().Bytes() 87 } 88 e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation)).Invoke(t, stackitem.Null{}, "designateAsRole", int(noderoles.P2PNotary), notaryNodesPublicKeys) 89 for _, notaryNode := range notaryNodes { 90 e.CheckGASBalance(t, notaryNode.GetScriptHash(), big.NewInt(0)) 91 } 92 93 // deposit GAS for `signer` with lock until the next block (inclusively) 94 depositAmount := 100_0000 + (2+int64(nKeys))*notaryServiceFeePerKey // sysfee + netfee of the next transaction 95 gasCommitteeInvoker.Invoke(t, true, "transfer", e.CommitteeHash, notaryHash, depositAmount, []any{e.CommitteeHash, e.Chain.BlockHeight() + 2}) 96 97 // save initial GAS total supply 98 getGASTS := func(t *testing.T) int64 { 99 stack, err := gasCommitteeInvoker.TestInvoke(t, "totalSupply") 100 require.NoError(t, err) 101 return stack.Pop().Value().(*big.Int).Int64() 102 } 103 tsInitial := getGASTS(t) 104 105 // send transaction with Notary contract as a sender 106 tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1_000_000) 107 tx.Nonce = neotest.Nonce() 108 tx.ValidUntilBlock = e.Chain.BlockHeight() + 1 109 tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: uint8(nKeys)}}) 110 tx.NetworkFee = (2 + int64(nKeys)) * notaryServiceFeePerKey 111 tx.Signers = []transaction.Signer{ 112 { 113 Account: notaryHash, 114 Scopes: transaction.None, 115 }, 116 { 117 Account: e.CommitteeHash, 118 Scopes: transaction.None, 119 }, 120 } 121 tx.Scripts = []transaction.Witness{ 122 { 123 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, notaryNodes[0].SignHashable(uint32(e.Chain.GetConfig().Magic), tx)...), 124 }, 125 { 126 InvocationScript: e.Committee.SignHashable(uint32(e.Chain.GetConfig().Magic), tx), 127 VerificationScript: e.Committee.Script(), 128 }, 129 } 130 e.AddNewBlock(t, tx) 131 132 // check balance of notaries 133 e.CheckGASBalance(t, notaryHash, big.NewInt(int64(depositAmount-tx.SystemFee-tx.NetworkFee))) 134 for _, notaryNode := range notaryNodes { 135 e.CheckGASBalance(t, notaryNode.GetScriptHash(), big.NewInt(notaryServiceFeePerKey*(nKeys+1)/nNotaries)) 136 } 137 tsUpdated := getGASTS(t) 138 tsExpected := tsInitial + 5000_0000 - tx.SystemFee 139 require.Equal(t, tsExpected, tsUpdated) 140 }