package core import ( "math/big" "testing" "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/stretchr/testify/require" ) func TestGAS_Refuel(t *testing.T) { bc := newTestChain(t) cs, _ := getTestContractState(bc) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) const ( sysFee = 10_000000 burnFee = sysFee + 12345678 ) accs := []*wallet.Account{ newAccountWithGAS(t, bc), newAccountWithGAS(t, bc), } t.Run("good, refuel from self", func(t *testing.T) { before0 := bc.GetUtilityTokenBalance(accs[0].Contract.ScriptHash()) aer, err := invokeContractMethodGeneric(bc, sysFee, bc.contracts.GAS.Hash, "refuel", accs[0], accs[0].Contract.ScriptHash(), int64(burnFee)) require.NoError(t, err) require.Equal(t, vm.HaltState, aer.VMState) after0 := bc.GetUtilityTokenBalance(accs[0].Contract.ScriptHash()) tx, _, _ := bc.GetTransaction(aer.Container) require.Equal(t, before0, new(big.Int).Add(after0, big.NewInt(tx.SystemFee+tx.NetworkFee+burnFee))) }) t.Run("good, refuel from other", func(t *testing.T) { before0 := bc.GetUtilityTokenBalance(accs[0].Contract.ScriptHash()) before1 := bc.GetUtilityTokenBalance(accs[1].Contract.ScriptHash()) aer, err := invokeContractMethodGeneric(bc, sysFee, cs.Hash, "refuelGas", accs, accs[1].Contract.ScriptHash(), int64(burnFee), int64(burnFee)) require.NoError(t, err) require.Equal(t, vm.HaltState, aer.VMState) after0 := bc.GetUtilityTokenBalance(accs[0].Contract.ScriptHash()) after1 := bc.GetUtilityTokenBalance(accs[1].Contract.ScriptHash()) tx, _, _ := bc.GetTransaction(aer.Container) require.Equal(t, before0, new(big.Int).Add(after0, big.NewInt(tx.SystemFee+tx.NetworkFee))) require.Equal(t, before1, new(big.Int).Add(after1, big.NewInt(burnFee))) }) t.Run("bad, invalid witness", func(t *testing.T) { aer, err := invokeContractMethodGeneric(bc, sysFee, cs.Hash, "refuelGas", accs, random.Uint160(), int64(1), int64(1)) require.NoError(t, err) require.Equal(t, vm.FaultState, aer.VMState) }) t.Run("bad, invalid GAS amount", func(t *testing.T) { aer, err := invokeContractMethodGeneric(bc, sysFee, cs.Hash, "refuelGas", accs, accs[0].Contract.ScriptHash(), int64(0), int64(1)) require.NoError(t, err) require.Equal(t, vm.FaultState, aer.VMState) }) } func TestGAS_Roundtrip(t *testing.T) { bc := newTestChain(t) getUtilityTokenBalance := func(bc *Blockchain, acc util.Uint160) (*big.Int, uint32) { bs, err := bc.dao.GetNEP17Balances(acc) if err != nil { return big.NewInt(0), 0 } balance := bs.Trackers[bc.contracts.GAS.ID] return &balance.Balance, balance.LastUpdatedBlock } initialBalance, _ := getUtilityTokenBalance(bc, neoOwner) require.NotNil(t, initialBalance) t.Run("bad: amount > initial balance", func(t *testing.T) { tx := transferTokenFromMultisigAccountWithAssert(t, bc, neoOwner, bc.contracts.GAS.Hash, initialBalance.Int64()+1, false) aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application) require.NoError(t, err) require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException) // transfer without assert => HALT state checkResult(t, &aer[0], stackitem.NewBool(false)) require.Len(t, aer[0].Events, 0) // failed transfer => no events // check balance and height were not changed updatedBalance, updatedHeight := getUtilityTokenBalance(bc, neoOwner) initialBalance.Sub(initialBalance, big.NewInt(tx.SystemFee+tx.NetworkFee)) require.Equal(t, initialBalance, updatedBalance) require.Equal(t, bc.BlockHeight(), updatedHeight) }) t.Run("good: amount < initial balance", func(t *testing.T) { amount := initialBalance.Int64() - 10_00000000 tx := transferTokenFromMultisigAccountWithAssert(t, bc, neoOwner, bc.contracts.GAS.Hash, amount, false) aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application) require.NoError(t, err) require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException) checkResult(t, &aer[0], stackitem.NewBool(true)) require.Len(t, aer[0].Events, 1) // roundtrip // check balance wasn't changed and height was updated updatedBalance, updatedHeight := getUtilityTokenBalance(bc, neoOwner) initialBalance.Sub(initialBalance, big.NewInt(tx.SystemFee+tx.NetworkFee)) require.Equal(t, initialBalance, updatedBalance) require.Equal(t, bc.BlockHeight(), updatedHeight) }) }