Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(e2e): add rate limiter admin E2E test #2063

Merged
merged 26 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
e2etests.TestUpdateBytecodeZRC20Name,
e2etests.TestUpdateBytecodeConnectorName,
e2etests.TestDepositEtherLiquidityCapName,
e2etests.TestRateLimiterName,

// TestMigrateChainSupportName tests EVM chain migration. Currently this test doesn't work with Anvil because pre-EIP1559 txs are not supported
// See issue below for details
Expand Down
32 changes: 16 additions & 16 deletions contrib/localnet/orchestrator/start-zetae2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,36 @@ sleep 2
### Create the accounts and fund them with Ether on local Ethereum network

# unlock the deployer account
echo "funding deployer address 0xE5C5367B8224807Ac2207d350E60e1b6F27a7ecC with 100 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xE5C5367B8224807Ac2207d350E60e1b6F27a7ecC", value: web3.toWei(100,"ether")})' attach http://eth:8545
echo "funding deployer address 0xE5C5367B8224807Ac2207d350E60e1b6F27a7ecC with 10000 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xE5C5367B8224807Ac2207d350E60e1b6F27a7ecC", value: web3.toWei(10000,"ether")})' attach http://eth:8545

# unlock erc20 tester accounts
echo "funding deployer address 0x6F57D5E7c6DBb75e59F1524a3dE38Fc389ec5Fd6 with 100 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x6F57D5E7c6DBb75e59F1524a3dE38Fc389ec5Fd6", value: web3.toWei(100,"ether")})' attach http://eth:8545
echo "funding deployer address 0x6F57D5E7c6DBb75e59F1524a3dE38Fc389ec5Fd6 with 10000 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x6F57D5E7c6DBb75e59F1524a3dE38Fc389ec5Fd6", value: web3.toWei(10000,"ether")})' attach http://eth:8545

# unlock zeta tester accounts
echo "funding deployer address 0x5cC2fBb200A929B372e3016F1925DcF988E081fd with 100 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x5cC2fBb200A929B372e3016F1925DcF988E081fd", value: web3.toWei(100,"ether")})' attach http://eth:8545
echo "funding deployer address 0x5cC2fBb200A929B372e3016F1925DcF988E081fd with 10000 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x5cC2fBb200A929B372e3016F1925DcF988E081fd", value: web3.toWei(10000,"ether")})' attach http://eth:8545

# unlock bitcoin tester accounts
echo "funding deployer address 0x283d810090EdF4043E75247eAeBcE848806237fD with 100 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x283d810090EdF4043E75247eAeBcE848806237fD", value: web3.toWei(100,"ether")})' attach http://eth:8545
echo "funding deployer address 0x283d810090EdF4043E75247eAeBcE848806237fD with 10000 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x283d810090EdF4043E75247eAeBcE848806237fD", value: web3.toWei(10000,"ether")})' attach http://eth:8545

# unlock ethers tester accounts
echo "funding deployer address 0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A with 100 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A", value: web3.toWei(100,"ether")})' attach http://eth:8545
echo "funding deployer address 0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A with 10000 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A", value: web3.toWei(10000,"ether")})' attach http://eth:8545

# unlock miscellaneous tests accounts
echo "funding deployer address 0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf with 100 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf", value: web3.toWei(100,"ether")})' attach http://eth:8545
echo "funding deployer address 0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf with 10000 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf", value: web3.toWei(10000,"ether")})' attach http://eth:8545

# unlock admin erc20 tests accounts
echo "funding deployer address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 with 100 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", value: web3.toWei(100,"ether")})' attach http://eth:8545
echo "funding deployer address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 with 10000 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", value: web3.toWei(10000,"ether")})' attach http://eth:8545

# unlock the TSS account
echo "funding TSS address 0xF421292cb0d3c97b90EEEADfcD660B893592c6A2 with 100 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xF421292cb0d3c97b90EEEADfcD660B893592c6A2", value: web3.toWei(100,"ether")})' attach http://eth:8545
echo "funding TSS address 0xF421292cb0d3c97b90EEEADfcD660B893592c6A2 with 10000 Ether"
geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xF421292cb0d3c97b90EEEADfcD660B893592c6A2", value: web3.toWei(10000,"ether")})' attach http://eth:8545

### Run zetae2e command depending on the option passed

Expand Down
1 change: 1 addition & 0 deletions docs/cli/zetacored/zetacored_query_crosschain.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ zetacored query crosschain [flags]
* [zetacored query crosschain list-in-tx-tracker](zetacored_query_crosschain_list-in-tx-tracker.md) - shows a list of in tx tracker by chainId
* [zetacored query crosschain list-out-tx-tracker](zetacored_query_crosschain_list-out-tx-tracker.md) - list all OutTxTracker
* [zetacored query crosschain list-pending-cctx](zetacored_query_crosschain_list-pending-cctx.md) - shows pending CCTX
* [zetacored query crosschain list_pending_cctx_within_rate_limit](zetacored_query_crosschain_list_pending_cctx_within_rate_limit.md) - list all pending CCTX within rate limit
* [zetacored query crosschain show-cctx](zetacored_query_crosschain_show-cctx.md) - shows a CCTX
* [zetacored query crosschain show-gas-price](zetacored_query_crosschain_show-gas-price.md) - shows a gasPrice
* [zetacored query crosschain show-in-tx-hash-to-cctx](zetacored_query_crosschain_show-in-tx-hash-to-cctx.md) - shows a inTxHashToCctx
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# query crosschain list_pending_cctx_within_rate_limit

list all pending CCTX within rate limit

```
zetacored query crosschain list_pending_cctx_within_rate_limit [flags]
```

### Options

```
--grpc-addr string the gRPC endpoint to use for this chain
--grpc-insecure allow gRPC over insecure channels, if not TLS the server must use TLS
--height int Use a specific height to query state at (this can error if the node is pruning state)
-h, --help help for list_pending_cctx_within_rate_limit
--node string [host]:[port] to Tendermint RPC interface for this chain
-o, --output string Output format (text|json)
```

### Options inherited from parent commands

```
--chain-id string The network chain ID
--home string directory for config and data
--log_format string The logging format (json|plain)
--log_level string The logging level (trace|debug|info|warn|error|fatal|panic)
--trace print out full stack trace on errors
```

### SEE ALSO

* [zetacored query crosschain](zetacored_query_crosschain.md) - Querying commands for the crosschain module

7 changes: 7 additions & 0 deletions e2e/e2etests/e2etests.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const (
TestPauseZRC20Name = "pause_zrc20"
TestUpdateBytecodeZRC20Name = "update_bytecode_zrc20"
TestUpdateBytecodeConnectorName = "update_bytecode_connector"
TestRateLimiterName = "rate_limiter"
)

// AllE2ETests is an ordered list of all e2e tests
Expand Down Expand Up @@ -386,6 +387,12 @@ var AllE2ETests = []runner.E2ETest{
[]runner.ArgDefinition{},
TestUpdateBytecodeConnector,
),
runner.NewE2ETest(
TestRateLimiterName,
"test sending cctxs with rate limiter enabled and show logs when processing cctxs",
[]runner.ArgDefinition{},
TestRateLimiter,
),
runner.NewE2ETest(
TestMessagePassingZEVMToEVMName,
"zevm -> evm message passing contract call",
Expand Down
4 changes: 2 additions & 2 deletions e2e/e2etests/test_eth_deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ func TestDepositEtherLiquidityCap(r *runner.E2ERunner, args []string) {
}

liquidityCap := math.NewUintFromBigInt(supply).Add(liquidityCapArg)
amountLessThanCap := liquidityCap.BigInt().Div(liquidityCap.BigInt(), big.NewInt(10)) // 1/10 of the cap
amountMoreThanCap := liquidityCap.BigInt().Mul(liquidityCap.BigInt(), big.NewInt(10)) // 10 times the cap
amountLessThanCap := liquidityCapArg.BigInt().Div(liquidityCapArg.BigInt(), big.NewInt(10)) // 1/10 of the cap
amountMoreThanCap := liquidityCapArg.BigInt().Mul(liquidityCapArg.BigInt(), big.NewInt(10)) // 10 times the cap
msg := fungibletypes.NewMsgUpdateZRC20LiquidityCap(
r.ZetaTxServer.GetAccountAddress(0),
r.ETHZRC20Addr.Hex(),
Expand Down
15 changes: 14 additions & 1 deletion e2e/e2etests/test_migrate_chain_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,20 @@ func TestMigrateChainSupport(r *runner.E2ERunner, _ []string) {
newRunner.WaitForMinedCCTX(txEtherDeposit)

// perform withdrawals on the new chain
TestZetaWithdraw(newRunner, []string{"10000000000000000000"})
amount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10))
newRunner.DepositAndApproveWZeta(amount)
tx := newRunner.WithdrawZeta(amount, true)
cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout)
r.Logger.CCTX(*cctx, "zeta withdraw")
if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined {
panic(fmt.Errorf(
"expected cctx status to be %s; got %s, message %s",
crosschaintypes.CctxStatus_OutboundMined,
cctx.CctxStatus.Status.String(),
cctx.CctxStatus.StatusMessage,
))
}

TestEtherWithdraw(newRunner, []string{"50000000000000000"})

// finally try to deposit Zeta back
Expand Down
203 changes: 203 additions & 0 deletions e2e/e2etests/test_rate_limiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package e2etests

import (
"context"
"fmt"
"math/big"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/zeta-chain/zetacore/e2e/runner"
"github.com/zeta-chain/zetacore/e2e/utils"
crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types"
"golang.org/x/sync/errgroup"
)

const RateLimiterWithdrawNumber = 5

// rateLimiterFlags are the rate limiter flags for the test
var rateLimiterFlags = crosschaintypes.RateLimiterFlags{
Enabled: true,
Rate: sdk.NewUint(1e17).MulUint64(5), // this value is used so rate is reached
Window: 10,
}

func TestRateLimiter(r *runner.E2ERunner, _ []string) {
r.Logger.Info("TestRateLimiter")

// deposit and approve 50 WZETA for the tests
r.DepositAndApproveWZeta(big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(50)))

// add liquidity in the pool to prevent high slippage in WZETA/gas pair
if err := addZetaGasLiquidity(r); err != nil {
panic(err)
}

// Set the rate limiter to 0.5ZETA per 10 blocks
// These rate limiter flags will only allow to process 1 withdraw per 10 blocks
r.Logger.Info("setting up rate limiter flags")
if err := setupRateLimiterFlags(r, rateLimiterFlags); err != nil {
panic(err)
}

// Test with rate limiter
// TODO: define proper assertion to check the rate limiter is working
// https://github.com/zeta-chain/node/issues/2090
r.Logger.Print("rate limiter enabled")
if err := createAndWaitWithdraws(r); err != nil {
panic(err)
}

// Disable rate limiter
r.Logger.Info("disabling rate limiter")
if err := setupRateLimiterFlags(r, crosschaintypes.RateLimiterFlags{Enabled: false}); err != nil {
panic(err)
}

// Test without rate limiter again
r.Logger.Print("rate limiter disabled")
if err := createAndWaitWithdraws(r); err != nil {
panic(err)
}
}

// setupRateLimiterFlags sets up the rate limiter flags with flags defined in the test
func setupRateLimiterFlags(r *runner.E2ERunner, flags crosschaintypes.RateLimiterFlags) error {
adminAddr, err := r.ZetaTxServer.GetAccountAddressFromName(utils.FungibleAdminName)
if err != nil {
return err
}
_, err = r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, crosschaintypes.NewMsgUpdateRateLimiterFlags(
adminAddr,
flags,
))
if err != nil {
return err
}

return nil
}

// createAndWaitWithdraws performs RateLimiterWithdrawNumber withdraws
func createAndWaitWithdraws(r *runner.E2ERunner) error {
startTime := time.Now()

r.Logger.Print("starting %d withdraws", RateLimiterWithdrawNumber)

// Perform RateLimiterWithdrawNumber withdraws to log time for completion
txs := make([]*ethtypes.Transaction, RateLimiterWithdrawNumber)
for i := 0; i < RateLimiterWithdrawNumber; i++ {
amount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(3))
txs[i] = r.WithdrawZeta(amount, true)
}

// start a error group to wait for all the withdraws to be mined
g, ctx := errgroup.WithContext(r.Ctx)
for i, tx := range txs {
// capture the loop variables
tx, i := tx, i

// start a goroutine to wait for the withdraw to be mined
g.Go(func() error {
return waitForZetaWithdrawMined(ctx, r, tx, i, startTime)
})
}

// wait for all the withdraws to be mined
if err := g.Wait(); err != nil {
return err
}

duration := time.Now().Sub(startTime).Seconds()
block, err := r.ZEVMClient.BlockNumber(r.Ctx)
if err != nil {
return fmt.Errorf("error getting block number: %w", err)
}
r.Logger.Print("all withdraws completed in %vs at block %d", duration, block)

return nil
}

// waitForZetaWithdrawMined waits for a zeta withdraw to be mined
// we first wait to get the receipt
// NOTE: this could be a more general function but we define it here for this test because we emit in the function logs specific to this test
func waitForZetaWithdrawMined(ctx context.Context, r *runner.E2ERunner, tx *ethtypes.Transaction, index int, startTime time.Time) error {
// wait for the cctx to be mined
cctx := utils.WaitCctxMinedByInTxHash(ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout)
r.Logger.CCTX(*cctx, "zeta withdraw")
if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined {
return fmt.Errorf(
"expected cctx status to be %s; got %s, message %s",
crosschaintypes.CctxStatus_OutboundMined,
cctx.CctxStatus.Status.String(),
cctx.CctxStatus.StatusMessage,
)
}

// record the time for completion
duration := time.Now().Sub(startTime).Seconds()
block, err := r.ZEVMClient.BlockNumber(ctx)
if err != nil {
return err
}
r.Logger.Print("cctx %d mined in %vs at block %d", index, duration, block)

return nil
}

// addZetaGasLiquidity adds liquidity to the ZETA/gas pool
func addZetaGasLiquidity(r *runner.E2ERunner) error {
// use 10 ZETA and 10 ETH for the liquidity
// this will be sufficient for the tests
amount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10))
approveAmount := big.NewInt(0).Mul(amount, big.NewInt(10))

// approve uniswap router to spend gas
txETHZRC20Approve, err := r.ETHZRC20.Approve(r.ZEVMAuth, r.UniswapV2RouterAddr, approveAmount)
if err != nil {
return fmt.Errorf("error approving ZETA: %w", err)
}

// wait for the tx to be mined
receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, txETHZRC20Approve, r.Logger, r.ReceiptTimeout)
if receipt.Status != 1 {
return fmt.Errorf("approve failed")
}

// approve uniswap router to spend ZETA
txZETAApprove, err := r.WZeta.Approve(r.ZEVMAuth, r.UniswapV2RouterAddr, approveAmount)
if err != nil {
return fmt.Errorf("error approving ZETA: %w", err)
}

// wait for the tx to be mined
receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, txZETAApprove, r.Logger, r.ReceiptTimeout)
if receipt.Status != 1 {
return fmt.Errorf("approve failed")
}

// add liquidity in the pool to prevent high slippage in WZETA/gas pair
r.ZEVMAuth.Value = amount
txAddLiquidity, err := r.UniswapV2Router.AddLiquidityETH(
r.ZEVMAuth,
r.ETHZRC20Addr,
amount,
big.NewInt(1e18),
big.NewInt(1e18),
r.DeployerAddress,
big.NewInt(time.Now().Add(10*time.Minute).Unix()),
)
if err != nil {
return fmt.Errorf("error adding liquidity: %w", err)
}
r.ZEVMAuth.Value = big.NewInt(0)

// wait for the tx to be mined
receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, txAddLiquidity, r.Logger, r.ReceiptTimeout)
if receipt.Status != 1 {
return fmt.Errorf("add liquidity failed")
}

return nil
}
Loading
Loading