neo-go/pkg/core/bench_test.go
Roman Khimov 1b83dc2476 *: improve for loop syntax
Mostly it's about Go 1.22+ syntax with ranging over integers, but it also
prefers ranging over slices where possible (it makes code a little better to
read).

Notice that we have a number of dangerous loops where slices are mutated
during loop execution, many of these can't be converted since we need proper
length evalutation at every iteration.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 21:45:18 +03:00

219 lines
7.3 KiB
Go

package core_test
import (
"fmt"
"math/big"
"path/filepath"
"testing"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"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/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
func BenchmarkBlockchain_VerifyWitness(t *testing.B) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
tx := e.NewTx(t, []neotest.Signer{acc}, e.NativeHash(t, nativenames.Gas), "transfer", acc.ScriptHash(), acc.Script(), 1, nil)
t.ResetTimer()
for range t.N {
_, err := bc.VerifyWitness(tx.Signers[0].Account, tx, &tx.Scripts[0], 100000000)
require.NoError(t, err)
}
}
func BenchmarkBlockchain_ForEachNEP17Transfer(t *testing.B) {
var stores = map[string]func(testing.TB) storage.Store{
"MemPS": func(t testing.TB) storage.Store {
return storage.NewMemoryStore()
},
"BoltPS": newBoltStoreForTesting,
"LevelPS": newLevelDBForTesting,
}
startFrom := []int{1, 100, 1000}
blocksToTake := []int{100, 1000}
for psName, newPS := range stores {
for _, startFromBlock := range startFrom {
for _, nBlocksToTake := range blocksToTake {
t.Run(fmt.Sprintf("%s_StartFromBlockN-%d_Take%dBlocks", psName, startFromBlock, nBlocksToTake), func(t *testing.B) {
ps := newPS(t)
t.Cleanup(func() { ps.Close() })
benchmarkForEachNEP17Transfer(t, ps, startFromBlock, nBlocksToTake)
})
}
}
}
}
func BenchmarkNEO_GetGASPerVote(t *testing.B) {
var stores = map[string]func(testing.TB) storage.Store{
"MemPS": func(t testing.TB) storage.Store {
return storage.NewMemoryStore()
},
"BoltPS": newBoltStoreForTesting,
"LevelPS": newLevelDBForTesting,
}
for psName, newPS := range stores {
for nRewardRecords := 10; nRewardRecords <= 1000; nRewardRecords *= 10 {
for rewardDistance := 1; rewardDistance <= 1000; rewardDistance *= 10 {
t.Run(fmt.Sprintf("%s_%dRewardRecords_%dRewardDistance", psName, nRewardRecords, rewardDistance), func(t *testing.B) {
ps := newPS(t)
t.Cleanup(func() { ps.Close() })
benchmarkGasPerVote(t, ps, nRewardRecords, rewardDistance)
})
}
}
}
}
func benchmarkForEachNEP17Transfer(t *testing.B, ps storage.Store, startFromBlock, nBlocksToTake int) {
var (
chainHeight = 2_100 // constant chain height to be able to compare paging results
transfersPerBlock = state.TokenTransferBatchSize/4 + // 4 blocks per batch
state.TokenTransferBatchSize/32 // shift
)
bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
e := neotest.NewExecutor(t, bc, validators, committee)
gasHash := e.NativeHash(t, nativenames.Gas)
acc := random.Uint160()
from := e.Validator.ScriptHash()
for range chainHeight {
b := smartcontract.NewBuilder()
for range transfersPerBlock {
b.InvokeWithAssert(gasHash, "transfer", from, acc, 1, nil)
}
script, err := b.Script()
require.NoError(t, err)
tx := transaction.New(script, int64(1100_0000*transfersPerBlock))
tx.NetworkFee = 1_0000_000
tx.ValidUntilBlock = bc.BlockHeight() + 1
tx.Nonce = neotest.Nonce()
tx.Signers = []transaction.Signer{{Account: from, Scopes: transaction.CalledByEntry}}
require.NoError(t, validators.SignTx(netmode.UnitTestNet, tx))
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
}
newestB, err := bc.GetBlock(bc.GetHeaderHash(bc.BlockHeight() - uint32(startFromBlock) + 1))
require.NoError(t, err)
newestTimestamp := newestB.Timestamp
oldestB, err := bc.GetBlock(bc.GetHeaderHash(newestB.Index - uint32(nBlocksToTake)))
require.NoError(t, err)
oldestTimestamp := oldestB.Timestamp
t.ResetTimer()
t.ReportAllocs()
t.StartTimer()
for range t.N {
require.NoError(t, bc.ForEachNEP17Transfer(acc, newestTimestamp, func(t *state.NEP17Transfer) (bool, error) {
if t.Timestamp < oldestTimestamp {
// iterating from newest to oldest, already have reached the needed height
return false, nil
}
return true, nil
}))
}
t.StopTimer()
}
func newLevelDBForTesting(t testing.TB) storage.Store {
dbPath := t.TempDir()
dbOptions := dbconfig.LevelDBOptions{
DataDirectoryPath: dbPath,
}
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
require.Nil(t, err, "NewLevelDBStore error")
return newLevelStore
}
func newBoltStoreForTesting(t testing.TB) storage.Store {
d := t.TempDir()
dbPath := filepath.Join(d, "test_bolt_db")
boltDBStore, err := storage.NewBoltDBStore(dbconfig.BoltDBOptions{FilePath: dbPath})
require.NoError(t, err)
return boltDBStore
}
func benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rewardDistance int) {
bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
cfg := bc.GetConfig()
e := neotest.NewExecutor(t, bc, validators, committee)
neoHash := e.NativeHash(t, nativenames.Neo)
gasHash := e.NativeHash(t, nativenames.Gas)
neoSuperInvoker := e.NewInvoker(neoHash, validators, committee)
neoValidatorsInvoker := e.ValidatorInvoker(neoHash)
gasValidatorsInvoker := e.ValidatorInvoker(gasHash)
// Vote for new committee.
sz := len(cfg.StandbyCommittee)
voters := make([]*wallet.Account, sz)
candidates := make(keys.PublicKeys, sz)
txs := make([]*transaction.Transaction, 0, len(voters)*3)
for i := range sz {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
candidates[i] = priv.PublicKey()
voters[i], err = wallet.NewAccount()
require.NoError(t, err)
registerTx := neoSuperInvoker.PrepareInvoke(t, "registerCandidate", candidates[i].Bytes())
txs = append(txs, registerTx)
to := voters[i].Contract.ScriptHash()
transferNeoTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, big.NewInt(int64(sz-i)*1000000).Int64(), nil)
txs = append(txs, transferNeoTx)
transferGasTx := gasValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, int64(1_000_000_000), nil)
txs = append(txs, transferGasTx)
}
e.AddNewBlock(t, txs...)
for _, tx := range txs {
e.CheckHalt(t, tx.Hash())
}
voteTxs := make([]*transaction.Transaction, 0, sz)
for i := range sz {
priv := voters[i].PrivateKey()
h := priv.GetScriptHash()
voteTx := e.NewTx(t, []neotest.Signer{neotest.NewSingleSigner(voters[i])}, neoHash, "vote", h, candidates[i].Bytes())
voteTxs = append(voteTxs, voteTx)
}
e.AddNewBlock(t, voteTxs...)
for _, tx := range voteTxs {
e.CheckHalt(t, tx.Hash())
}
// Collect set of nRewardRecords reward records for each voter.
e.GenerateNewBlocks(t, len(cfg.StandbyCommittee))
// Transfer some more NEO to first voter to update his balance height.
to := voters[0].Contract.ScriptHash()
neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), to, int64(1), nil)
// Advance chain one more time to avoid same start/end rewarding bounds.
e.GenerateNewBlocks(t, rewardDistance)
end := bc.BlockHeight()
t.ResetTimer()
t.ReportAllocs()
t.StartTimer()
for range t.N {
_, err := bc.CalculateClaimable(to, end)
require.NoError(t, err)
}
t.StopTimer()
}