neoneo-go/pkg/core/native/native_test/gas_test.go

140 lines
5.4 KiB
Go

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/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/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)
notaryServiceFeePerKey := e.Chain.GetNotaryServiceFeePerKey()
// 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(noderoles.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))*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)) * 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), keys.SignatureLen}, 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(notaryServiceFeePerKey*(nKeys+1)/nNotaries))
}
tsUpdated := getGASTS(t)
tsExpected := tsInitial + 5000_0000 - tx.SystemFee
require.Equal(t, tsExpected, tsUpdated)
}