mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 13:41:37 +00:00
nativetest: migrate GAS contract tests to neotest framework
This commit is contained in:
parent
4e7435081f
commit
3f1af71f52
2 changed files with 139 additions and 149 deletions
139
pkg/core/native/native_test/gas_test.go
Normal file
139
pkg/core/native/native_test/gas_test.go
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
package native_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newGasClient(t *testing.T) *neotest.ContractInvoker {
|
||||||
|
return newNativeClient(t, nativenames.Gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGAS_Roundtrip(t *testing.T) {
|
||||||
|
c := newGasClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
gasInvoker := c.WithSigners(c.NewAccount(t))
|
||||||
|
owner := gasInvoker.Signers[0].ScriptHash()
|
||||||
|
|
||||||
|
getUtilityTokenBalance := func(acc util.Uint160) (*big.Int, uint32) {
|
||||||
|
lub, err := e.Chain.GetTokenLastUpdated(acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return e.Chain.GetUtilityTokenBalance(acc), lub[e.NativeID(t, nativenames.Gas)]
|
||||||
|
}
|
||||||
|
|
||||||
|
initialBalance, _ := getUtilityTokenBalance(owner)
|
||||||
|
require.NotNil(t, initialBalance)
|
||||||
|
|
||||||
|
t.Run("bad: amount > initial balance", func(t *testing.T) {
|
||||||
|
h := gasInvoker.Invoke(t, false, "transfer", owner, owner, initialBalance.Int64()+1, nil)
|
||||||
|
tx, height := e.GetTransaction(t, h)
|
||||||
|
require.Equal(t, 0, len(e.GetTxExecResult(t, h).Events)) // no events (failed transfer)
|
||||||
|
// check balance and height were not changed
|
||||||
|
updatedBalance, updatedHeight := getUtilityTokenBalance(owner)
|
||||||
|
initialBalance.Sub(initialBalance, big.NewInt(tx.SystemFee+tx.NetworkFee))
|
||||||
|
require.Equal(t, initialBalance, updatedBalance)
|
||||||
|
require.Equal(t, height, updatedHeight)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("good: amount < initial balance", func(t *testing.T) {
|
||||||
|
h := gasInvoker.Invoke(t, true, "transfer", owner, owner, initialBalance.Int64()-1_0000_0000, nil)
|
||||||
|
tx, height := e.GetTransaction(t, h)
|
||||||
|
require.Equal(t, 1, len(e.GetTxExecResult(t, h).Events)) // roundtrip
|
||||||
|
// check balance wasn't changed and height was updated
|
||||||
|
updatedBalance, updatedHeight := getUtilityTokenBalance(owner)
|
||||||
|
initialBalance.Sub(initialBalance, big.NewInt(tx.SystemFee+tx.NetworkFee))
|
||||||
|
require.Equal(t, initialBalance, updatedBalance)
|
||||||
|
require.Equal(t, height, updatedHeight)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGAS_RewardWithP2PSigExtensionsEnabled(t *testing.T) {
|
||||||
|
const (
|
||||||
|
nNotaries = 2
|
||||||
|
nKeys = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
bc, validator, committee := chain.NewMultiWithCustomConfig(t, func(cfg *config.ProtocolConfiguration) {
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
e := neotest.NewExecutor(t, bc, validator, committee)
|
||||||
|
gasCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Gas))
|
||||||
|
notaryHash := e.NativeHash(t, nativenames.Notary)
|
||||||
|
|
||||||
|
// transfer funds to committee
|
||||||
|
e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)).Invoke(t, true, "transfer", e.Validator.ScriptHash(), e.CommitteeHash, 1000_0000_0000, nil)
|
||||||
|
|
||||||
|
// set Notary nodes and check their balance
|
||||||
|
notaryNodes := make([]*keys.PrivateKey, nNotaries)
|
||||||
|
notaryNodesPublicKeys := make([]interface{}, nNotaries)
|
||||||
|
var err error
|
||||||
|
for i := range notaryNodes {
|
||||||
|
notaryNodes[i], err = keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
notaryNodesPublicKeys[i] = notaryNodes[i].PublicKey().Bytes()
|
||||||
|
}
|
||||||
|
e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation)).Invoke(t, stackitem.Null{}, "designateAsRole", int(roles.P2PNotary), notaryNodesPublicKeys)
|
||||||
|
for _, notaryNode := range notaryNodes {
|
||||||
|
e.CheckGASBalance(t, notaryNode.GetScriptHash(), big.NewInt(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// deposit GAS for `signer` with lock until the next block
|
||||||
|
depositAmount := 100_0000 + (2+int64(nKeys))*transaction.NotaryServiceFeePerKey // sysfee + netfee of the next transaction
|
||||||
|
gasCommitteeInvoker.Invoke(t, true, "transfer", e.CommitteeHash, notaryHash, depositAmount, []interface{}{e.CommitteeHash, e.Chain.BlockHeight() + 1})
|
||||||
|
|
||||||
|
// save initial GAS total supply
|
||||||
|
getGASTS := func(t *testing.T) int64 {
|
||||||
|
stack, err := gasCommitteeInvoker.TestInvoke(t, "totalSupply")
|
||||||
|
require.NoError(t, err)
|
||||||
|
return stack.Pop().Value().(*big.Int).Int64()
|
||||||
|
}
|
||||||
|
tsInitial := getGASTS(t)
|
||||||
|
|
||||||
|
// send transaction with Notary contract as a sender
|
||||||
|
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1_000_000)
|
||||||
|
tx.Nonce = neotest.Nonce()
|
||||||
|
tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
|
||||||
|
tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: uint8(nKeys)}})
|
||||||
|
tx.NetworkFee = (2 + int64(nKeys)) * transaction.NotaryServiceFeePerKey
|
||||||
|
tx.Signers = []transaction.Signer{
|
||||||
|
{
|
||||||
|
Account: notaryHash,
|
||||||
|
Scopes: transaction.None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Account: e.CommitteeHash,
|
||||||
|
Scopes: transaction.None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tx.Scripts = []transaction.Witness{
|
||||||
|
{
|
||||||
|
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notaryNodes[0].SignHashable(uint32(e.Chain.GetConfig().Magic), tx)...),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
InvocationScript: e.Committee.SignHashable(uint32(e.Chain.GetConfig().Magic), tx),
|
||||||
|
VerificationScript: e.Committee.Script(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
e.AddNewBlock(t, tx)
|
||||||
|
|
||||||
|
// check balance of notaries
|
||||||
|
e.CheckGASBalance(t, notaryHash, big.NewInt(int64(depositAmount-tx.SystemFee-tx.NetworkFee)))
|
||||||
|
for _, notaryNode := range notaryNodes {
|
||||||
|
e.CheckGASBalance(t, notaryNode.GetScriptHash(), big.NewInt(int64(transaction.NotaryServiceFeePerKey*(nKeys+1)/nNotaries)))
|
||||||
|
}
|
||||||
|
tsUpdated := getGASTS(t)
|
||||||
|
tsExpected := tsInitial + 5000_0000 - tx.SystemFee
|
||||||
|
require.Equal(t, tsExpected, tsUpdated)
|
||||||
|
}
|
|
@ -1,149 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"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/opcode"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGAS_Roundtrip(t *testing.T) {
|
|
||||||
bc := newTestChain(t)
|
|
||||||
|
|
||||||
getUtilityTokenBalance := func(bc *Blockchain, acc util.Uint160) (*big.Int, uint32) {
|
|
||||||
lub, err := bc.GetTokenLastUpdated(acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return bc.GetUtilityTokenBalance(acc), lub[bc.contracts.GAS.ID]
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGAS_RewardWithP2PSigExtensionsEnabled(t *testing.T) {
|
|
||||||
chain := newTestChain(t)
|
|
||||||
notaryHash := chain.contracts.Notary.Hash
|
|
||||||
gasHash := chain.contracts.GAS.Hash
|
|
||||||
signer := testchain.MultisigScriptHash()
|
|
||||||
var err error
|
|
||||||
|
|
||||||
const (
|
|
||||||
nNotaries = 2
|
|
||||||
nKeys = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// set Notary nodes and check their balance
|
|
||||||
notaryNodes := make([]*keys.PrivateKey, nNotaries)
|
|
||||||
notaryNodesPublicKeys := make(keys.PublicKeys, nNotaries)
|
|
||||||
for i := range notaryNodes {
|
|
||||||
notaryNodes[i], err = keys.NewPrivateKey()
|
|
||||||
require.NoError(t, err)
|
|
||||||
notaryNodesPublicKeys[i] = notaryNodes[i].PublicKey()
|
|
||||||
}
|
|
||||||
chain.setNodesByRole(t, true, noderoles.P2PNotary, notaryNodesPublicKeys)
|
|
||||||
for _, notaryNode := range notaryNodesPublicKeys {
|
|
||||||
checkBalanceOf(t, chain, notaryNode.GetScriptHash(), 0)
|
|
||||||
}
|
|
||||||
// deposit GAS for `signer` with lock until the next block
|
|
||||||
depositAmount := 100_0000 + (2+int64(nKeys))*transaction.NotaryServiceFeePerKey // sysfee + netfee of the next transaction
|
|
||||||
transferTx := transferTokenFromMultisigAccount(t, chain, notaryHash, gasHash, depositAmount, signer, int64(chain.BlockHeight()+1))
|
|
||||||
res, err := chain.GetAppExecResults(transferTx.Hash(), trigger.Application)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, vm.HaltState, res[0].VMState)
|
|
||||||
require.Equal(t, 0, len(res[0].Stack))
|
|
||||||
|
|
||||||
// save initial validators balance
|
|
||||||
balances := make(map[int]int64, testchain.ValidatorsCount)
|
|
||||||
for i := 0; i < testchain.ValidatorsCount; i++ {
|
|
||||||
balances[i] = chain.GetUtilityTokenBalance(testchain.PrivateKeyByID(i).GetScriptHash()).Int64()
|
|
||||||
}
|
|
||||||
ic := interop.NewContext(trigger.Application, chain, chain.dao, nil, nil, nil, nil, chain.log)
|
|
||||||
tsInitial := chain.contracts.GAS.TotalSupply(ic, nil).Value().(*big.Int).Int64()
|
|
||||||
|
|
||||||
// send transaction with Notary contract as a sender
|
|
||||||
tx := chain.newTestTx(util.Uint160{}, []byte{byte(opcode.PUSH1)})
|
|
||||||
tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: uint8(nKeys)}})
|
|
||||||
tx.NetworkFee = (2 + int64(nKeys)) * transaction.NotaryServiceFeePerKey
|
|
||||||
tx.Signers = []transaction.Signer{
|
|
||||||
{
|
|
||||||
Account: notaryHash,
|
|
||||||
Scopes: transaction.None,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Account: signer,
|
|
||||||
Scopes: transaction.None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
tx.Scripts = []transaction.Witness{
|
|
||||||
{
|
|
||||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, notaryNodes[0].SignHashable(uint32(testchain.Network()), tx)...),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
InvocationScript: testchain.Sign(tx),
|
|
||||||
VerificationScript: testchain.MultisigVerificationScript(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b := chain.newBlock(tx)
|
|
||||||
require.NoError(t, chain.AddBlock(b))
|
|
||||||
checkBalanceOf(t, chain, notaryHash, int(depositAmount-tx.SystemFee-tx.NetworkFee))
|
|
||||||
singleReward := transaction.NotaryServiceFeePerKey * (nKeys + 1) / nNotaries
|
|
||||||
for _, notaryNode := range notaryNodesPublicKeys {
|
|
||||||
checkBalanceOf(t, chain, notaryNode.GetScriptHash(), singleReward)
|
|
||||||
}
|
|
||||||
for i := 0; i < testchain.ValidatorsCount; i++ {
|
|
||||||
newBalance := chain.GetUtilityTokenBalance(testchain.PrivateKeyByID(i).GetScriptHash()).Int64()
|
|
||||||
expectedBalance := balances[i]
|
|
||||||
if i == int(b.Index)%testchain.CommitteeSize() {
|
|
||||||
// committee reward
|
|
||||||
expectedBalance += 5000_0000
|
|
||||||
}
|
|
||||||
if testchain.IDToOrder(i) == int(b.PrimaryIndex) {
|
|
||||||
// primary reward
|
|
||||||
expectedBalance += tx.NetworkFee - int64(singleReward*nNotaries)
|
|
||||||
}
|
|
||||||
assert.Equal(t, expectedBalance, newBalance, i)
|
|
||||||
}
|
|
||||||
tsUpdated := chain.contracts.GAS.TotalSupply(ic, nil).Value().(*big.Int).Int64()
|
|
||||||
tsExpected := tsInitial + 5000_0000 - tx.SystemFee
|
|
||||||
require.Equal(t, tsExpected, tsUpdated)
|
|
||||||
}
|
|
Loading…
Reference in a new issue