Merge pull request #2169 from nspcc-dev/states-diff_mainnet_131795
core: allow transfer 0 GAS/NEO with zero balance
This commit is contained in:
commit
24a3cce1ca
2 changed files with 84 additions and 1 deletions
|
@ -171,9 +171,13 @@ func (c *nep17TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint16
|
||||||
key := makeAccountKey(acc)
|
key := makeAccountKey(acc)
|
||||||
si := ic.DAO.GetStorageItem(c.ID, key)
|
si := ic.DAO.GetStorageItem(c.ID, key)
|
||||||
if si == nil {
|
if si == nil {
|
||||||
if amount.Sign() <= 0 {
|
if amount.Sign() < 0 {
|
||||||
return errors.New("insufficient funds")
|
return errors.New("insufficient funds")
|
||||||
}
|
}
|
||||||
|
if amount.Sign() == 0 {
|
||||||
|
// it's OK to transfer 0 if the balance 0, no need to put si to the storage
|
||||||
|
return nil
|
||||||
|
}
|
||||||
si = state.StorageItem{}
|
si = state.StorageItem{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
@ -357,3 +358,81 @@ func TestNEO_Roundtrip(t *testing.T) {
|
||||||
require.Equal(t, bc.BlockHeight(), updatedHeight)
|
require.Equal(t, bc.BlockHeight(), updatedHeight)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNEO_TransferZeroWithZeroBalance(t *testing.T) {
|
||||||
|
bc := newTestChain(t)
|
||||||
|
|
||||||
|
check := func(t *testing.T, roundtrip bool) {
|
||||||
|
acc := newAccountWithGAS(t, bc)
|
||||||
|
from := acc.PrivateKey().GetScriptHash()
|
||||||
|
to := from
|
||||||
|
if !roundtrip {
|
||||||
|
to = random.Uint160()
|
||||||
|
}
|
||||||
|
transferTx := newNEP17TransferWithAssert(bc.contracts.NEO.Hash, acc.PrivateKey().GetScriptHash(), to, 0, true)
|
||||||
|
transferTx.SystemFee = 100000000
|
||||||
|
transferTx.NetworkFee = 10000000
|
||||||
|
transferTx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||||
|
addSigners(acc.PrivateKey().GetScriptHash(), transferTx)
|
||||||
|
require.NoError(t, acc.SignTx(bc.config.Magic, transferTx))
|
||||||
|
b := bc.newBlock(transferTx)
|
||||||
|
require.NoError(t, bc.AddBlock(b))
|
||||||
|
|
||||||
|
aer, err := bc.GetAppExecResults(transferTx.Hash(), trigger.Application)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
||||||
|
require.Len(t, aer[0].Events, 1) // roundtrip only, no GAS claim
|
||||||
|
require.Equal(t, stackitem.NewBigInteger(big.NewInt(0)), aer[0].Events[0].Item.Value().([]stackitem.Item)[2]) // amount is 0
|
||||||
|
// check balance wasn't changed and height wasn't updated
|
||||||
|
updatedBalance, updatedHeight := bc.GetGoverningTokenBalance(acc.PrivateKey().GetScriptHash())
|
||||||
|
require.Equal(t, big.NewInt(0), updatedBalance)
|
||||||
|
require.Equal(t, uint32(0), updatedHeight)
|
||||||
|
}
|
||||||
|
t.Run("roundtrip: amount == initial balance == 0", func(t *testing.T) {
|
||||||
|
check(t, true)
|
||||||
|
})
|
||||||
|
t.Run("non-roundtrip: amount == initial balance == 0", func(t *testing.T) {
|
||||||
|
check(t, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNEO_TransferZeroWithNonZeroBalance(t *testing.T) {
|
||||||
|
bc := newTestChain(t)
|
||||||
|
|
||||||
|
check := func(t *testing.T, roundtrip bool) {
|
||||||
|
acc := newAccountWithGAS(t, bc)
|
||||||
|
transferTokenFromMultisigAccount(t, bc, acc.PrivateKey().GetScriptHash(), bc.contracts.NEO.Hash, 100)
|
||||||
|
initialBalance, _ := bc.GetGoverningTokenBalance(acc.PrivateKey().GetScriptHash())
|
||||||
|
require.True(t, initialBalance.Sign() > 0)
|
||||||
|
|
||||||
|
from := acc.PrivateKey().GetScriptHash()
|
||||||
|
to := from
|
||||||
|
if !roundtrip {
|
||||||
|
to = random.Uint160()
|
||||||
|
}
|
||||||
|
transferTx := newNEP17TransferWithAssert(bc.contracts.NEO.Hash, acc.PrivateKey().GetScriptHash(), to, 0, true)
|
||||||
|
transferTx.SystemFee = 100000000
|
||||||
|
transferTx.NetworkFee = 10000000
|
||||||
|
transferTx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||||
|
addSigners(acc.PrivateKey().GetScriptHash(), transferTx)
|
||||||
|
require.NoError(t, acc.SignTx(bc.config.Magic, transferTx))
|
||||||
|
b := bc.newBlock(transferTx)
|
||||||
|
require.NoError(t, bc.AddBlock(b))
|
||||||
|
|
||||||
|
aer, err := bc.GetAppExecResults(transferTx.Hash(), trigger.Application)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
||||||
|
require.Len(t, aer[0].Events, 2) // roundtrip + GAS claim
|
||||||
|
require.Equal(t, stackitem.NewBigInteger(big.NewInt(0)), aer[0].Events[1].Item.Value().([]stackitem.Item)[2]) // amount is 0
|
||||||
|
// check balance wasn't changed and height was updated
|
||||||
|
updatedBalance, updatedHeight := bc.GetGoverningTokenBalance(acc.PrivateKey().GetScriptHash())
|
||||||
|
require.Equal(t, initialBalance, updatedBalance)
|
||||||
|
require.Equal(t, bc.BlockHeight(), updatedHeight)
|
||||||
|
}
|
||||||
|
t.Run("roundtrip", func(t *testing.T) {
|
||||||
|
check(t, true)
|
||||||
|
})
|
||||||
|
t.Run("non-roundtrip", func(t *testing.T) {
|
||||||
|
check(t, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue