Skip to content

Commit

Permalink
add more code comment
Browse files Browse the repository at this point in the history
fix the issue of incorrect error return when checking delegations
adjust the opAmount when batch sending delegations
fix the lint error in readme
add IntervalAfterDelegations and more code comments
print txID for debug
add staker funding for the automated test
fix the issue from code comment
rename test-tool to exocore-test-tool and add more details in the readme
  • Loading branch information
TimmyExogenous committed Dec 22, 2024
1 parent 0c652d1 commit cb3be9c
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 52 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ $(BUILDDIR)/:
mkdir -p $(BUILDDIR)/

build-test-tool:
go build $(BUILD_FLAGS) $(BUILD_ARGS) ./cmd/test-tool
go build $(BUILD_FLAGS) $(BUILD_ARGS) ./cmd/exocore-test-tool
install-test-tool:
go install $(BUILD_FLAGS) $(BUILD_ARGS) ./cmd/test-tool
go install $(BUILD_FLAGS) $(BUILD_ARGS) ./cmd/exocore-test-tool

build-reproducible: go.sum
$(DOCKER) rm latest-build || true
Expand Down
57 changes: 55 additions & 2 deletions cmd/test-tool/README.md → cmd/exocore-test-tool/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# test-tool
# exocore-test-tool

This is a custom tool designed to batch-send test transactions to the Exocore chain. It can be used for stress testing
or routine automated testing of the Exocore chain.

Currently, all test transactions are executed by directly calling precompiles and are signed using automatically
generated private keys. Therefore, a customized Exocore node is required for use, with the node configured to disable
the precompile's gateway contract address check.
the precompile's gateway contract address check. The branch of customized Exocore is as below:
https://github.com/ExocoreNetwork/exocore/tree/pressure-test

When using the test tool to batch-send test transactions, you can dynamically adjust the number of test objects and the
transaction sending rate in the configuration file to control the test volume. This allows for routine automated testing
Expand Down Expand Up @@ -51,6 +52,58 @@ The current implementation primarily provides the following functionalities:
command handles the creation, funding, registration, and opting-in of test objects, while the `batch-test` command
allows for manual batch testing of different transaction types.

The specific processes for the two types of testing are as follows:
A local node needs to be started through `local_node.sh` before testing.

1. automated testing:
Step 1: Initialize the configuration file in current directory

```shell
exocore-test-tool init --home
```

Step 2: Start the test tool

```shell
exocore-test-tool start --home .
```

2. manual testing:

Step 1: Initialize the configuration file in current directory

```shell
exocore-test-tool init --home
```

Step 2: Prepare test environment

```shell
exocore-test-tool prepare --home .
```

Step 3: Run batch tests

```shell
exocore-test-tool batch-test depositLST --home .
```

We can query transactions for a specific batch and status using the following command, either during or after the test:

```shell
exocore-test-tool query-tx-record <msgType> <batch-id> <status>
```

## go binding generation

In the implementation of the test tool, since it needs to directly call precompiled contracts, the Go binding file for
the contract will be used. This binding file is automatically generated using abigen. For example, the Go binding for
the asset precompile can be generated using the following command:

```shell
abigen --abi abi.json --pkg assets --out assets_binding.go
```

## todo

* Feed prices for all test assets. Currently, the test does not enable the oracle. We might consider providing a fake
Expand Down
15 changes: 8 additions & 7 deletions cmd/test-tool/main.go → cmd/exocore-test-tool/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ var initCmd = &cobra.Command{
Short: "init the default config for the test tool",
Long: "init the default config for the test tool, using test-tool-config.toml " +
"as the default name of the config file",
Example: "test-tool init --home .",
Example: "exocore-test-tool init --home .",
Args: cobra.NoArgs,
Run: func(_ *cobra.Command, _ []string) {
configFilePath := filepath.Join(homePath, batch.ConfigFileName)
Expand All @@ -87,6 +87,7 @@ var initCmd = &cobra.Command{
encoder := toml.NewEncoder(file)
if err := encoder.Encode(batch.DefaultTestToolConfig); err != nil {
fmt.Printf("failed to encode config to TOML: %s\r\n", err)
return
}
},
}
Expand All @@ -97,7 +98,7 @@ var startCmd = &cobra.Command{
Short: "start the test tool",
Long: "Start the testing tool to automatically perform preparation steps " +
"and batch tests for multiple message types.",
Example: "test-tool start --home .",
Example: "exocore-test-tool start --home .",
Args: cobra.NoArgs,
Run: func(_ *cobra.Command, _ []string) {
// Start the app manager in a separate goroutine
Expand All @@ -115,7 +116,7 @@ var prepareCmd = &cobra.Command{
Use: "prepare",
Short: "prepare for the batch test",
Long: "prepare the test objects, funding, registration and opting-in for the test tool",
Example: "test-tool prepare --home .",
Example: "exocore-test-tool prepare --home .",
Args: cobra.NoArgs,
Run: func(_ *cobra.Command, _ []string) {
if err := appManager.Prepare(); err != nil {
Expand All @@ -131,7 +132,7 @@ var batchTestCmd = &cobra.Command{
Short: "batch test",
Long: "batch test the multiple functions, the msgType should be: \r\n" +
"depositLST,delegate,undelegate and withdrawLST",
Example: "test-tool batch-test depositLST --home .",
Example: "exocore-test-tool batch-test depositLST --home .",
Args: cobra.ExactArgs(1),
Run: func(_ *cobra.Command, args []string) {
// Start the app manager in a separate goroutine
Expand All @@ -150,7 +151,7 @@ var QueryHelperRecordCmd = &cobra.Command{
Use: "query-helper-record",
Short: "query the helper record info",
Long: "query the helper record info, the info includes: current-batch-id",
Example: "test-tool query-helper-record --home .",
Example: "exocore-test-tool query-helper-record --home .",
Args: cobra.NoArgs,
Run: func(_ *cobra.Command, _ []string) {
helperRecord, err := batch.LoadObjectByID[batch.HelperRecord](sqliteDB, batch.SqliteDefaultStartID)
Expand All @@ -170,7 +171,7 @@ var QueryTestObjectsCmd = &cobra.Command{
Use: "query-test-objects <object>",
Short: "query the specified test objects",
Long: "query the specified test objects, the object type is: asset, staker, operator and AVS",
Example: "test-tool query-test-objects staker --home .",
Example: "exocore-test-tool query-test-objects staker --home .",
Args: cobra.ExactArgs(1),
Run: func(_ *cobra.Command, args []string) {
var err error
Expand Down Expand Up @@ -228,7 +229,7 @@ var QueryTxRecordCmd = &cobra.Command{
"1: pending\r\n" +
"2: OnChainButFailed\r\n" +
"3: OnChainAndSuccessful",
Example: "test-tool query-tx-record depositLST 1 1 --home .",
Example: "exocore-test-tool query-tx-record depositLST 1 1 --home .",
Args: cobra.ExactArgs(3),
Run: func(_ *cobra.Command, args []string) {
batchID, err := strconv.ParseUint(args[1], 10, 32)
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ require (
cosmossdk.io/math v1.2.0
cosmossdk.io/simapp v0.0.0-20230608160436-666c345ad23d
cosmossdk.io/tools/rosetta v0.2.1
github.com/ExocoreNetwork/price-feeder v0.1.15
github.com/BurntSushi/toml v1.3.2
github.com/ExocoreNetwork/price-feeder v0.0.0-20241009094357-40e58e6f1694
github.com/ExocoreNetwork/price-feeder v0.1.15
github.com/agiledragon/gomonkey/v2 v2.11.0
github.com/armon/go-metrics v0.4.1
github.com/cometbft/cometbft v0.37.4
Expand Down
1 change: 0 additions & 1 deletion precompiles/avs/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ func (p Precompile) RegisterAVS(
// The AVS registration is done by the calling contract.
avsParams.AvsAddress = contract.CallerAddress
avsParams.Action = avstypes.RegisterAction
fmt.Println("call RegisterAVS, the avs addr is:", avsParams.AvsAddress)
// Finally, update the AVS information in the keeper.
err = p.avsKeeper.UpdateAVSInfo(ctx, avsParams)
if err != nil {
Expand Down
23 changes: 13 additions & 10 deletions testutil/batch/batch_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,11 @@ func (m *Manager) EnqueueDelegationTxs(batchID uint, msgType string) error {
delegatedAmount, err := m.QueryDelegatedAmount(uint64(asset.ClientChainID), staker.EvmAddress().String(), asset.Address.String(), operator.Address)
if msgType == delegation.MethodUndelegate {
if err == nil {
// undelegates all amount, the expected check value will be zero
opAmount = delegatedAmount
// Undelegates half of the total amount. The expected check value will also be half.
// The reason for keeping some amount delegated is to ensure there is always a portion
// delegated to the operators, which helps in testing the Exocore chain.
opAmount = delegatedAmount.Quo(sdkmath.NewInt(2))
expectedCheckValue = delegatedAmount.Sub(opAmount)
}
// opAmount will be zero if the delegation amount can't be quried, then the undelegation
// will be skipped when checking the opAmount.
Expand Down Expand Up @@ -266,36 +269,36 @@ func (m *Manager) EnqueueDelegationTxs(batchID uint, msgType string) error {
return nil
}

func (m *Manager) SignAndSendTxs(tx interface{}) error {
func (m *Manager) SignAndSendTxs(tx interface{}) (string, string, error) {
// sign and send the transaction
var txID string
evmTx, ok := tx.(*EvmTxInQueue)
if ok {
txHash, err := m.SignAndSendEvmTx(evmTx)
if err != nil {
logger.Error("can't sign and send the evm tx", "txHash", txHash, "err", err)
return err
return txID, evmTx.TxRecord.Type, err
}
txID = txHash.String()
} else {
return xerrors.Errorf("unsupported transaction type: %v", reflect.TypeOf(tx))
return txID, "", xerrors.Errorf("unsupported transaction type: %v", reflect.TypeOf(tx))
}
// todo: address the cosmos transaction for the delegation/undelegation of Exo token.

// update the tx record in the local db for future check
height, err := m.NodeEVMHTTPClients[DefaultNodeIndex].BlockNumber(m.ctx)
if err != nil {
return err
return txID, evmTx.TxRecord.Type, err
}
evmTx.TxRecord.SendTime = time.Now().String()
evmTx.TxRecord.Status = Pending
evmTx.TxRecord.SendHeight = height
evmTx.TxRecord.TxHash = txID
err = SaveObject[Transaction](m.GetDB(), *evmTx.TxRecord)
if err != nil {
return err
return txID, evmTx.TxRecord.Type, err
}
return nil
return txID, evmTx.TxRecord.Type, nil
}

func (m *Manager) TickHandle(handleRate int, handle func() (bool, error)) error {
Expand Down Expand Up @@ -336,12 +339,12 @@ func (m *Manager) DequeueAndSignSendTxs() error {
select {
case tx := <-m.TxsQueue:
m.QueueSize.Add(-1)
err := m.SignAndSendTxs(tx)
txID, txType, err := m.SignAndSendTxs(tx)
if err != nil {
logger.Error("DequeueAndSignSendTxs: can't sign and send the tx", "err", err)
return false, err
}
logger.Info("DequeueAndSignSendTxs, sign and send tx successfully")
logger.Info("DequeueAndSignSendTxs, sign and send tx successfully", "txType", txType, "txID", txID)
default:
return false, nil
}
Expand Down
5 changes: 4 additions & 1 deletion testutil/batch/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,8 +595,11 @@ func (m *Manager) Start() error {
if err := m.EnqueueAndCheckTxsInBatch(assets.MethodWithdrawLST); err != nil {
return xerrors.Errorf("withdrawal batch failed: %w", err)
}
logger.Info("Start: finish enqueuing and checking all withdrawal txs")
logger.Info("Start: finish enqueuing and checking all withdrawal txs", "EachTestInterval", m.config.EachTestInterval)
time.Sleep(time.Duration(m.config.EachTestInterval) * time.Second)
if err := m.FundAndCheckStakers(); err != nil {
return xerrors.Errorf("failed to fund and check the stakers %w", err)
}
return nil
})

Expand Down
15 changes: 15 additions & 0 deletions testutil/batch/prepare.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"math/big"
"strings"
"time"

dogfoodtypes "github.com/ExocoreNetwork/exocore/x/dogfood/types"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -85,6 +86,20 @@ func (m *Manager) LoadSequence(addr common.Address) (uint64, error) {
return 0, xerrors.Errorf("can't load the sequence from the sync map, addr:%s", addr)
}

func (m *Manager) FundAndCheckStakers() error {
logger.Info("start funding stakers")
err := FundingObjects(m, &Staker{}, m.config.StakerExoAmount)
if err != nil {
return xerrors.Errorf("can't fund stakers,err:%w", err)
}
time.Sleep(time.Duration(m.config.BatchTxsCheckInterval) * time.Second)
err = CheckObjectsBalance(m, &Staker{}, m.config.StakerExoAmount)
if err != nil {
return err
}
return nil
}

// Funding : send Exo token to the test objects, which can be used for the tx fee in the next tests.
// all Exo token is sent from the faucet sk.
func (m *Manager) Funding() error {
Expand Down
4 changes: 2 additions & 2 deletions testutil/batch/send_txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func WaitForEvmTxReceipt(client *ethclient.Client, txHash common.Hash, waitDurat
}

// Log a message indicating the receipt is not yet available
logger.Info("can't get the receipt of EVM transaction, continue waiting", "err", err)
logger.Info("can't get the receipt of EVM transaction, continue waiting", "waitDuration", waitDuration, "err", err)

// Wait for the specified duration before retrying
time.Sleep(waitDuration)
Expand Down Expand Up @@ -103,7 +103,7 @@ func (m *Manager) SignAndSendEvmTx(txInfo *EvmTxInQueue) (common.Hash, error) {
GasPrice: gasPrice,
Data: msg.Data,
})
logger.Info("SignAndSendEvmTx", "from", txInfo.From, "to", retTx.To(),
logger.Info("SignAndSendEvmTx", "from", strings.ToLower(txInfo.From.String()), "to", retTx.To(),
"nonce", retTx.Nonce(), "value", retTx.Value(), "gasPrice", retTx.GasPrice(),
"gas", retTx.Gas(), "dataLength", len(retTx.Data()), "time", time.Now().String())
signTx, err := types.SignTx(retTx, m.EthSigner, sk)
Expand Down
17 changes: 10 additions & 7 deletions testutil/batch/tx_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,12 @@ func (m *Manager) PrecompileTxOnChainCheck(batchID uint, msgType string) error {

// DepositWithdrawLSTCheck : By default, we require each batch to follow the order of
// deposits -> delegations -> undelegations -> withdrawals for batch testing.
// During delegation, only half of the deposit amount is used, with the other half
// reserved for withdrawals. Therefore, when checking:
// 1. After the deposits are completed, the totalDepositAmount should be equal to:
// DefaultDepositAmount + batchID * (DefaultDepositAmount / 2) where batchID starts from 0.
// 2. After the withdrawal tests are completed, the totalDepositAmount should be:
// (DefaultDepositAmount / 2) * (batchID + 1).
// `DefaultDepositAmount` is used as the amount for batch deposit tests.
// Therefore, we only need to verify if `StakerAssetInfo.total_deposit_amount`
// has increased by `DefaultDepositAmount`.
// For batch withdrawal tests, we will withdraw the total withdrawable amount
// and record the opAmount. Thus, when checking withdrawal test transactions,
// we only need to verify if `StakerAssetInfo.total_deposit_amount` has decreased by opAmount.
func (m *Manager) DepositWithdrawLSTCheck(batchID uint, msgType string) error {
stakerOpFunc := func(_ uint, _ int64, staker *Staker) error {
assetOpFunc := func(_ uint, _ int64, asset *Asset) error {
Expand Down Expand Up @@ -205,7 +205,10 @@ func (m *Manager) EvmDelegationCheck(batchID uint, msgType string) error {
operatorOpFunc := func(_ uint, _ int64, operator *Operator) error {
delegatedAmount, err := m.QueryDelegatedAmount(uint64(asset.ClientChainID), staker.EvmAddress().String(), asset.Address.String(), operator.Address)
if err != nil {
return err
logger.Error("EvmDelegationCheck, error occurs when querying the delegated amount",
"staker", staker.Name, "asset", asset.Name, "operator", operator.Name, "err", err)
// return nil to continue the next check
return nil
}
var transaction Transaction
err = m.GetDB().
Expand Down
Loading

0 comments on commit cb3be9c

Please sign in to comment.