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  }