2021-04-29 10:19:51 +00:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math/big"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/internal/random"
|
core: do not allow NEP17 roundtrip in case of insufficient funds
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"
}
```
2021-06-09 08:58:58 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2021-04-29 10:19:51 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
core: do not allow NEP17 roundtrip in case of insufficient funds
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"
}
```
2021-06-09 08:58:58 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
2021-04-29 10:19:51 +00:00
|
|
|
"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)
|
|
|
|
})
|
|
|
|
}
|
core: do not allow NEP17 roundtrip in case of insufficient funds
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"
}
```
2021-06-09 08:58:58 +00:00
|
|
|
|
|
|
|
func TestGAS_Roundtrip(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
|
|
|
|
|
|
|
getUtilityTokenBalance := func(bc *Blockchain, acc util.Uint160) (*big.Int, uint32) {
|
2021-07-25 12:00:44 +00:00
|
|
|
lub, err := bc.GetNEP17LastUpdated(acc)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return bc.GetUtilityTokenBalance(acc), lub[bc.contracts.GAS.ID]
|
core: do not allow NEP17 roundtrip in case of insufficient funds
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"
}
```
2021-06-09 08:58:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
})
|
|
|
|
}
|