stasatdaglabs dfa24d8353
Implement a stability test for mempool limits (#1647)
* Copy some boilerplate from the other stability tests.

* Fix a copy+paste error in run.sh.

* Copy over some stability test boilerplate go code.

* Run kaspad in the background.

* Catch panics and initialize the RPC client.

* Mine enough blocks to fund filling up the mempool.

* Extract coinbase transactions out of the generated blocks.

* Tidy up a bit.

* Implement submitting transactions.

* Lower the amount of outputs in each transaction.

* Verify that the mempool size has the expected amount of transactions.

* Pregenerate enough funds before submitting the first transaction so that block creation doesn't interfere with the test.

* Empty mempool out by continuously adding blocks to the DAG.

* Handle orphan transactions when overfilling the mempool.

* Increase mempoolSizeLimit to 1m.

* Fix a comment.

* Fix a comment.

* Add mempool-limits to run-slow.sh.

* Rename generateTransactionsWithLotsOfOutputs to generateTransactionsWithMultipleOutputs.

* Rename generateCoinbaseTransaction to mineBlockAndGetCoinbaseTransaction.

* Make generateFundingCoinbaseTransactions return an object instead of store a global variable.

* Convert mempool-limits into a Go test.

* Convert panics to t.Fatalfs.

* Fix a comment.

* Increase mempoolSizeLimit to 1m.

* Run TestMempoolLimits only if RUN_STABILITY_TESTS is set.

* Move the run of mempool-limits in run-slow.sh.

* Add a comment above fundingCoinbaseTransactions.

* Make a couple of stylistic changes.

* Use transactionhelper.CoinbaseTransactionIndex instead of hardcoding 0.

* Make uninteresting errors print %+v instead of %s.

Co-authored-by: Svarog <feanorr@gmail.com>
2021-04-11 16:59:11 +03:00

112 lines
3.4 KiB
Go

package main
import (
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
"github.com/kaspanet/kaspad/stability-tests/common"
"github.com/kaspanet/kaspad/util/panics"
"github.com/kaspanet/kaspad/util/profiling"
"os"
"testing"
)
const (
mempoolSizeLimit = 1_000_000
overfillMempoolByAmount = 1_000
)
func TestMempoolLimits(t *testing.T) {
if os.Getenv("RUN_STABILITY_TESTS") == "" {
t.Skip()
}
defer panics.HandlePanic(log, "mempool-limits-main", nil)
err := parseConfig()
if err != nil {
t.Fatalf("error in parseConfig: %s", err)
}
defer backendLog.Close()
common.UseLogger(backendLog, log.Level())
cfg := activeConfig()
if cfg.Profile != "" {
profiling.Start(cfg.Profile, log)
}
payAddressKeyPair := decodePayAddressKeyPair(t)
payToPayAddressScript := buildPayToPayAddressScript(t)
rpcClient := buildRPCClient(t)
// Create enough funds for the test
fundingTransactions := generateFundingCoinbaseTransactions(t, rpcClient)
// Fill up the mempool to the brim
submitAnAmountOfTransactionsToTheMempool(t, rpcClient, payAddressKeyPair,
payToPayAddressScript, fundingTransactions, mempoolSizeLimit, false)
// Make sure that the mempool size is exactly the limit
mempoolSize := getMempoolSize(t, rpcClient)
if mempoolSize != mempoolSizeLimit {
t.Fatalf("Unexpected mempool size. Want: %d, got: %d",
mempoolSizeLimit, mempoolSize)
}
// Add some more transactions to the mempool. We expect the
// mempool to either not grow or even to shrink, since an eviction
// may also remove any dependant (chained) transactions.
// Note that we pass ignoreOrphanRejects: true because we
// expect some of the submitted transactions to depend on
// transactions that had been evicted from the mempool
submitAnAmountOfTransactionsToTheMempool(t, rpcClient, payAddressKeyPair,
payToPayAddressScript, fundingTransactions, overfillMempoolByAmount, true)
// Make sure that the mempool size is the limit or smaller
mempoolSize = getMempoolSize(t, rpcClient)
if mempoolSize > mempoolSizeLimit {
t.Fatalf("Unexpected mempool size. Want at most: %d, got: %d",
mempoolSizeLimit, mempoolSize)
}
// Empty mempool out by continuously adding blocks to the DAG
emptyOutMempool(t, rpcClient)
log.Infof("mempool-limits passed")
}
func buildRPCClient(t *testing.T) *rpcclient.RPCClient {
client, err := rpcclient.NewRPCClient(activeConfig().KaspadRPCAddress)
if err != nil {
t.Fatalf("error connecting to %s: %s", activeConfig().KaspadRPCAddress, err)
}
return client
}
func getMempoolSize(t *testing.T, rpcClient *rpcclient.RPCClient) uint64 {
getInfoResponse, err := rpcClient.GetInfo()
if err != nil {
t.Fatalf("GetInfo: %+v", err)
}
return getInfoResponse.MempoolSize
}
func emptyOutMempool(t *testing.T, rpcClient *rpcclient.RPCClient) {
log.Infof("Adding blocks until mempool shrinks to 0 transactions")
getInfoResponse, err := rpcClient.GetInfo()
if err != nil {
t.Fatalf("GetInfo: %+v", err)
}
currentMempoolSize := getInfoResponse.MempoolSize
for currentMempoolSize > 0 {
mineBlockAndGetCoinbaseTransaction(t, rpcClient)
getInfoResponse, err := rpcClient.GetInfo()
if err != nil {
t.Fatalf("GetInfo: %+v", err)
}
if getInfoResponse.MempoolSize == currentMempoolSize {
t.Fatalf("Mempool did not shrink after a block was added to the DAG")
}
log.Infof("Mempool shrank from %d transactions to %d transactions",
currentMempoolSize, getInfoResponse.MempoolSize)
currentMempoolSize = getInfoResponse.MempoolSize
}
}