be902afe9e
NEP17 roundtrip is prohibited if from account doesn't have enough funds. This commit fixes states diff in block 92057 where account NfuwpaQ1A2xaeVbxWe8FRtaRgaMa8yF3YM initiates two NEO roundtrips with amount exceeding the account's balance: block 92057: value mismatch for key +////xTbYWBH3r5qhRKZAPFPHabKfb2vhQ==: QQMhAkwBIQOZZwEA vs QQMhAkwBIQN/ZwEA block 92057: value mismatch for key +v///ws=: kqlddcitCg== vs tphddcitCg== block 92057: value mismatch for key +v///xTbYWBH3r5qhRKZAPFPHabKfb2vhQ==: QQEhBUWyDu0W vs QQEhBWmhDu0W C#'s applog (contains False and False on stack for both transfers): ``` { "id" : 1, "jsonrpc" : "2.0", "result" : { "executions" : [ { "gasconsumed" : "11955500", "exception" : null, "stack" : [ { "value" : false, "type" : "Boolean" }, { "value" : false, "type" : "Boolean" } ], "vmstate" : "HALT", "trigger" : "Application", "notifications" : [] } ], "txid" : "0x8e73a7e9a566a514813907272ad65fc965002c3b098eacc5bdda529af19d7688" } } ``` Go's applog (both transfers succeeded and GAS minted): ``` { "result" : { "executions" : [ { "gasconsumed" : "11955500", "trigger" : "Application", "stack" : [ { "type" : "Boolean", "value" : true }, { "type" : "Boolean", "value" : true } ], "vmstate" : "HALT", "notifications" : [ { "eventname" : "Transfer", "contract" : "0xd2a4cff31913016155e38e474a2c06d08be276cf", "state" : { "value" : [ { "type" : "Any" }, { "value" : "22FgR96+aoUSmQDxTx2myn29r4U=", "type" : "ByteString" }, { "value" : "4316", "type" : "Integer" } ], "type" : "Array" } }, { "state" : { "type" : "Array", "value" : [ { "value" : "22FgR96+aoUSmQDxTx2myn29r4U=", "type" : "ByteString" }, { "type" : "ByteString", "value" : "22FgR96+aoUSmQDxTx2myn29r4U=" }, { "type" : "Integer", "value" : "1111111111" } ] }, "contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", "eventname" : "Transfer" }, { "contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", "state" : { "type" : "Array", "value" : [ { "value" : "22FgR96+aoUSmQDxTx2myn29r4U=", "type" : "ByteString" }, { "type" : "ByteString", "value" : "22FgR96+aoUSmQDxTx2myn29r4U=" }, { "type" : "Integer", "value" : "1111111" } ] }, "eventname" : "Transfer" } ] } ], "txid" : "0x8e73a7e9a566a514813907272ad65fc965002c3b098eacc5bdda529af19d7688" }, "id" : 1, "jsonrpc" : "2.0" } ```
118 lines
4.5 KiB
Go
118 lines
4.5 KiB
Go
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)
|
|
})
|
|
}
|