From 578afccb54c86839c9c505dab05893132164b7ee Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 24 Apr 2024 13:43:44 -0500 Subject: [PATCH 1/4] added zero rate check; added comment to make unit test clearer --- x/crosschain/keeper/grpc_query_cctx.go | 5 +- .../keeper/grpc_query_cctx_rate_limit.go | 5 +- .../keeper/grpc_query_cctx_rate_limit_test.go | 69 ++++++++++++++----- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index 2aeab7007a..688c9cb205 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -16,6 +16,9 @@ import ( const ( // MaxPendingCctxs is the maximum number of pending cctxs that can be queried MaxPendingCctxs = 500 + + // MaxLookbackNonce is the maximum number of nonces to look back to find missed pending cctxs + MaxLookbackNonce = 1000 ) func (k Keeper) ZetaAccounting(c context.Context, _ *types.QueryZetaAccountingRequest) (*types.QueryZetaAccountingResponse, error) { @@ -122,7 +125,7 @@ func (k Keeper) ListPendingCctx(c context.Context, req *types.QueryListPendingCc // now query the previous nonces up to 1000 prior to find any pending cctx that we might have missed // need this logic because a confirmation of higher nonce will automatically update the p.NonceLow // therefore might mask some lower nonce cctx that is still pending. - startNonce := pendingNonces.NonceLow - 1000 + startNonce := pendingNonces.NonceLow - MaxLookbackNonce if startNonce < 0 { startNonce = 0 } diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index ce9987d2a1..95a8c5b031 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -41,6 +41,9 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que if !found || !rateLimitFlags.Enabled { applyLimit = false } + if rateLimitFlags.Rate.IsNil() || rateLimitFlags.Rate.IsZero() { + applyLimit = false + } // fallback to non-rate-limited query if rate limiter is disabled if !applyLimit { @@ -143,7 +146,7 @@ LoopBackwards: // therefore might mask some lower nonce cctx that is still pending. pendingNonces := pendingNoncesMap[chain.ChainId] startNonce := pendingNonces.NonceLow - 1 - endNonce := pendingNonces.NonceLow - 1000 + endNonce := pendingNonces.NonceLow - MaxLookbackNonce if endNonce < 0 { endNonce = 0 } diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go index c47317edad..f5ed9e4413 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -170,7 +170,7 @@ func Test_ConvertCctxValue(t *testing.T) { cctx.InboundTxParams.Asset = "" cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(3e15) // 0.003 ETH - // convert cctx value + // convert cctx value: 0.003 ETH * 2500 = 7.5 ZETA value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) require.Equal(t, sdk.MustNewDecFromStr("7.5"), value) }) @@ -181,7 +181,7 @@ func Test_ConvertCctxValue(t *testing.T) { cctx.InboundTxParams.Asset = "" cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(70000) // 0.0007 BTC - // convert cctx value + // convert cctx value: 0.0007 BTC * 50000 = 35.0 ZETA value := keeper.ConvertCctxValue(btcChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) require.Equal(t, sdk.MustNewDecFromStr("35.0"), value) }) @@ -192,7 +192,7 @@ func Test_ConvertCctxValue(t *testing.T) { cctx.InboundTxParams.Asset = assetUSDT cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(3e6) // 3 USDT - // convert cctx value + // convert cctx value: 3 USDT * 0.8 = 2.4 ZETA value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) require.Equal(t, sdk.MustNewDecFromStr("2.4"), value) }) @@ -273,11 +273,13 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { zrc20BTC := sample.EthAddress().Hex() zrc20USDT := sample.EthAddress().Hex() - // create Eth chain mined and pending cctxs for rate limiter test - ethMindedCctxs := createCctxsWithCoinTypeAndHeightRange(t, 1, 999, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_OutboundMined) + // create Eth chain 999 mined and 200 pending cctxs for rate limiter test + // the number 999 is to make it less than `MaxLookbackNonce` so the LoopBackwards gets the chance to hit nonce 0 + ethMinedCctxs := createCctxsWithCoinTypeAndHeightRange(t, 1, 999, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_OutboundMined) ethPendingCctxs := createCctxsWithCoinTypeAndHeightRange(t, 1000, 1199, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_PendingOutbound) - // create Btc chain mined and pending cctxs for rate limiter test + // create Btc chain 999 mined and 200 pending cctxs for rate limiter test + // the number 999 is to make it less than `MaxLookbackNonce` so the LoopBackwards gets the chance to hit nonce 0 btcMinedCctxs := createCctxsWithCoinTypeAndHeightRange(t, 1, 999, btcChainID, coin.CoinType_Gas, "", 1000, types.CctxStatus_OutboundMined) btcPendingCctxs := createCctxsWithCoinTypeAndHeightRange(t, 1000, 1199, btcChainID, coin.CoinType_Gas, "", 1000, types.CctxStatus_PendingOutbound) @@ -287,7 +289,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { rateLimitFlags *types.RateLimiterFlags // Eth chain cctxs setup - ethMindedCctxs []*types.CrossChainTx + ethMinedCctxs []*types.CrossChainTx ethPendingCctxs []*types.CrossChainTx ethPendingNonces observertypes.PendingNonces @@ -309,7 +311,31 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { { name: "should use fallback query if rate limiter is disabled", rateLimitFlags: nil, // no rate limiter flags set in the keeper - ethMindedCctxs: ethMindedCctxs, + ethMinedCctxs: ethMinedCctxs, + ethPendingCctxs: ethPendingCctxs, + ethPendingNonces: observertypes.PendingNonces{ + ChainId: ethChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + btcMinedCctxs: btcMinedCctxs, + btcPendingCctxs: btcPendingCctxs, + btcPendingNonces: observertypes.PendingNonces{ + ChainId: btcChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + currentHeight: 1199, + queryLimit: keeper.MaxPendingCctxs, + expectedCctxs: append(append([]*types.CrossChainTx{}, btcPendingCctxs...), ethPendingCctxs...), + expectedTotalPending: 400, + }, + { + name: "should use fallback query if rate is 0", + rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(0), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + ethMinedCctxs: ethMinedCctxs, ethPendingCctxs: ethPendingCctxs, ethPendingNonces: observertypes.PendingNonces{ ChainId: ethChainID, @@ -333,7 +359,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { { name: "can retrieve pending cctx in range without exceeding rate limit", rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(5000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMindedCctxs: ethMindedCctxs, + ethMinedCctxs: ethMinedCctxs, ethPendingCctxs: ethPendingCctxs, ethPendingNonces: observertypes.PendingNonces{ ChainId: ethChainID, @@ -359,7 +385,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { { name: "can loop backwards all the way to endNonce 0", rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(5000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMindedCctxs: ethMindedCctxs, + ethMinedCctxs: ethMinedCctxs, ethPendingCctxs: ethPendingCctxs, ethPendingNonces: observertypes.PendingNonces{ ChainId: ethChainID, @@ -385,7 +411,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { { name: "set a low rate (< 1200) to early break the LoopBackwards with criteria #2", rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(1000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), // 1000 < 1200 - ethMindedCctxs: ethMindedCctxs, + ethMinedCctxs: ethMinedCctxs, ethPendingCctxs: ethPendingCctxs, ethPendingNonces: observertypes.PendingNonces{ ChainId: ethChainID, @@ -408,10 +434,11 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { rateLimitExceeded: true, }, { - name: "set high rate and wide window to early to break inner loop with the criteria #1", + // this test case is to break the LoopBackwards with criteria #1: the 1st break in LoopBackwards + name: "set high rate and wide window to break inner loop of LoopBackwards when left window boundary is reached", // The left boundary will be 49 (currentHeight-1150), which will be less than the endNonce 99 (1099 - 1000) rateLimitFlags: createTestRateLimiterFlags(1150, math.NewUint(10000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMindedCctxs: ethMindedCctxs, + ethMinedCctxs: ethMinedCctxs, ethPendingCctxs: ethPendingCctxs, ethPendingNonces: observertypes.PendingNonces{ ChainId: ethChainID, @@ -437,7 +464,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { { name: "set lower request limit to early break the LoopForwards loop", rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(5000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMindedCctxs: ethMindedCctxs, + ethMinedCctxs: ethMinedCctxs, ethPendingCctxs: ethPendingCctxs, ethPendingNonces: observertypes.PendingNonces{ ChainId: ethChainID, @@ -461,9 +488,11 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { rateLimitExceeded: false, }, { - name: "set rate to middle value (1200 < rate < 1500) to early break the LoopForwards loop with criteria #2", + // all pending cctxs fall within the sliding window in this test case. + // this test case is to break the LoopBackwards with criteria #2: the 2nd break in LoopForwards + name: "set rate to middle value (1200 < rate < 1500) to early break the LoopForwards when rate limit is exceeded", rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(1300), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), // 1200 < 1300 < 1500 - ethMindedCctxs: ethMindedCctxs, + ethMinedCctxs: ethMinedCctxs, ethPendingCctxs: ethPendingCctxs, ethPendingNonces: observertypes.PendingNonces{ ChainId: ethChainID, @@ -487,12 +516,14 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { rateLimitExceeded: true, }, { - name: "set low rate and narrow window to early break the LoopForwards loop with criteria #2", + // many pending cctxs fall beyond the sliding window in this test case. + // this test case is to break the LoopBackwards with criteria #2: the 2nd break in LoopForwards + name: "set low rate and narrow window to early break the LoopForwards when rate limit is exceeded", // the left boundary will be 1149 (currentHeight-50), the pending nonces [1099, 1148] fall beyond the left boundary. // `pendingCctxWindow` is 100 which is wider than rate limiter window 50. // give a block rate of 2 ZETA/block, the max value allowed should be 100 * 2 = 200 ZETA rateLimitFlags: createTestRateLimiterFlags(50, math.NewUint(100), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMindedCctxs: ethMindedCctxs, + ethMinedCctxs: ethMinedCctxs, ethPendingCctxs: ethPendingCctxs, ethPendingNonces: observertypes.PendingNonces{ ChainId: ethChainID, @@ -535,7 +566,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { } // Set Eth chain mined cctxs, pending ccxts and pending nonces - setCctxsInKeeper(ctx, *k, zk, tss, tt.ethMindedCctxs) + setCctxsInKeeper(ctx, *k, zk, tss, tt.ethMinedCctxs) setCctxsInKeeper(ctx, *k, zk, tss, tt.ethPendingCctxs) zk.ObserverKeeper.SetPendingNonces(ctx, tt.ethPendingNonces) From fa24f32191e070f8729095afe027e573ac1a3d51 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 24 Apr 2024 14:59:54 -0500 Subject: [PATCH 2/4] added unit test and note for method GetAllForeignCoinMap --- x/fungible/keeper/foreign_coins.go | 1 + x/fungible/keeper/foreign_coins_test.go | 66 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/x/fungible/keeper/foreign_coins.go b/x/fungible/keeper/foreign_coins.go index 5dfe744829..5c52eb543e 100644 --- a/x/fungible/keeper/foreign_coins.go +++ b/x/fungible/keeper/foreign_coins.go @@ -82,6 +82,7 @@ func (k Keeper) GetAllForeignCoins(ctx sdk.Context) (list []types.ForeignCoins) } // GetAllForeignCoinMap returns all foreign ERC20 coins in a map of chainID -> asset -> coin +// Note: DO NOT use this method outside of gRPC queries func (k Keeper) GetAllForeignCoinMap(ctx sdk.Context) map[int64]map[string]types.ForeignCoins { allForeignCoins := k.GetAllForeignCoins(ctx) diff --git a/x/fungible/keeper/foreign_coins_test.go b/x/fungible/keeper/foreign_coins_test.go index d9a98a4031..f3a6f6dacd 100644 --- a/x/fungible/keeper/foreign_coins_test.go +++ b/x/fungible/keeper/foreign_coins_test.go @@ -2,8 +2,10 @@ package keeper_test import ( "strconv" + "strings" "testing" + "cosmossdk.io/math" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/coin" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" @@ -152,3 +154,67 @@ func TestKeeperGetForeignCoinFromAsset(t *testing.T) { require.Equal(t, "foo", fc.Name) }) } + +func TestKeeperGetAllForeignCoinMap(t *testing.T) { + t.Run("can get all foreign foreign map", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + + // create foreign coins + coinFoo1 := types.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().String(), + Asset: strings.ToLower(sample.EthAddress().String()), + ForeignChainId: 1, + Decimals: 6, + CoinType: coin.CoinType_ERC20, + Name: "foo", + LiquidityCap: math.NewUint(100), + } + coinBar1 := types.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().String(), + Asset: "", + ForeignChainId: 1, + Decimals: 18, + CoinType: coin.CoinType_Gas, + Name: "bar", + LiquidityCap: math.NewUint(100), + } + coinFoo2 := types.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().String(), + Asset: strings.ToLower(sample.EthAddress().String()), + ForeignChainId: 2, + Decimals: 8, + CoinType: coin.CoinType_ERC20, + Name: "foo", + LiquidityCap: math.NewUint(200), + } + coinBar2 := types.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().String(), + Asset: "", + ForeignChainId: 2, + Decimals: 18, + CoinType: coin.CoinType_Gas, + Name: "bar", + LiquidityCap: math.NewUint(200), + } + + // populate and get + setForeignCoins(ctx, k, + coinFoo1, + coinBar1, + coinFoo2, + coinBar2, + ) + foreignCoinMap := k.GetAllForeignCoinMap(ctx) + + // check length + require.Len(t, foreignCoinMap, 2) + require.Len(t, foreignCoinMap[1], 2) + require.Len(t, foreignCoinMap[2], 2) + + // check coin + require.Equal(t, coinFoo1, foreignCoinMap[1][coinFoo1.Asset]) + require.Equal(t, coinBar1, foreignCoinMap[1][coinBar1.Asset]) + require.Equal(t, coinFoo2, foreignCoinMap[2][coinFoo2.Asset]) + require.Equal(t, coinBar2, foreignCoinMap[2][coinBar2.Asset]) + }) +} From 61627b1b22a0400c7f865f4c64b000ea4824a4bf Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 25 Apr 2024 01:57:26 -0500 Subject: [PATCH 3/4] treat Rate as average block rate; stop outbound when current rate limit exceeds Rate; updated metrics --- docs/openapi/openapi.swagger.yaml | 6 +- proto/crosschain/query.proto | 5 +- typescript/crosschain/query_pb.d.ts | 11 +- .../keeper/grpc_query_cctx_rate_limit.go | 83 ++-- .../keeper/grpc_query_cctx_rate_limit_test.go | 437 +++++++++--------- x/crosschain/types/query.pb.go | 325 +++++++------ zetaclient/interfaces/interfaces.go | 2 +- zetaclient/testutils/stub/core_bridge.go | 6 +- zetaclient/zetabridge/query.go | 6 +- zetaclient/zetacore_observer.go | 13 +- 10 files changed, 473 insertions(+), 421 deletions(-) diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 139ee8110a..903cbd10b2 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -54013,9 +54013,11 @@ definitions: total_pending: type: string format: uint64 - value_within_window: + current_withdraw_window: + type: string + format: int64 + current_withdraw_rate: type: string - format: uint64 rate_limit_exceeded: type: boolean crosschainQueryMessagePassingProtocolFeeResponse: diff --git a/proto/crosschain/query.proto b/proto/crosschain/query.proto index 1ecd041c48..07c65e4c31 100644 --- a/proto/crosschain/query.proto +++ b/proto/crosschain/query.proto @@ -270,8 +270,9 @@ message QueryListPendingCctxWithinRateLimitRequest { message QueryListPendingCctxWithinRateLimitResponse { repeated CrossChainTx cross_chain_tx = 1; uint64 total_pending = 2; - uint64 value_within_window = 3; - bool rate_limit_exceeded = 4; + int64 current_withdraw_window = 3; + string current_withdraw_rate = 4; + bool rate_limit_exceeded = 5; } message QueryLastZetaHeightRequest {} diff --git a/typescript/crosschain/query_pb.d.ts b/typescript/crosschain/query_pb.d.ts index 62e8fdfe3a..19877c30bd 100644 --- a/typescript/crosschain/query_pb.d.ts +++ b/typescript/crosschain/query_pb.d.ts @@ -910,12 +910,17 @@ export declare class QueryListPendingCctxWithinRateLimitResponse extends Message totalPending: bigint; /** - * @generated from field: uint64 value_within_window = 3; + * @generated from field: int64 current_withdraw_window = 3; */ - valueWithinWindow: bigint; + currentWithdrawWindow: bigint; /** - * @generated from field: bool rate_limit_exceeded = 4; + * @generated from field: string current_withdraw_rate = 4; + */ + currentWithdrawRate: string; + + /** + * @generated from field: bool rate_limit_exceeded = 5; */ rateLimitExceeded: boolean; diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 95a8c5b031..aadb94674d 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -31,7 +31,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que // define a few variables to be used in the query loops limitExceeded := false totalPending := uint64(0) - totalCctxValueInZeta := sdk.NewDec(0) + totalWithdrawInZeta := sdk.NewDec(0) cctxs := make([]*types.CrossChainTx, 0) chains := k.zetaObserverKeeper.GetSupportedForeignChains(ctx) @@ -86,12 +86,14 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que if applyLimit { gasCoinRates, erc20CoinRates = k.GetRateLimiterRates(ctx) foreignCoinMap = k.fungibleKeeper.GetAllForeignCoinMap(ctx) - windowLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()) - blockLimitInZeta = windowLimitInZeta.Quo(sdk.NewDec(rateLimitFlags.Window)) + + // convert the rate limit from aZETA to ZETA + blockLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()).Quo(sdk.NewDec(10).Power(18)) + windowLimitInZeta = blockLimitInZeta.Mul(sdk.NewDec(rateLimitFlags.Window)) } // the criteria to stop adding cctxs to the rpc response - maxCCTXsReached := func() bool { + maxCCTXsReached := func(cctxs []*types.CrossChainTx) bool { // #nosec G701 len always positive return uint32(len(cctxs)) >= limit } @@ -127,19 +129,21 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que } } - // invariant: for period of time > window, the average rate per block cannot exceed `blockLimitInZeta` - pendingCctxsLimitInZeta := windowLimitInZeta + // invariant: for period of time >= `rateLimitFlags.Window`, the zetaclient-side average withdraw rate should be <= `blockLimitInZeta` + // otherwise, this query should return empty result and wait for the average rate to drop below `blockLimitInZeta` + withdrawWindow := rateLimitFlags.Window + withdrawLimitInZeta := windowLimitInZeta if lowestPendingCctxHeight != 0 { // `pendingCctxWindow` is the width of [lowestPendingCctxHeight, height] window - // if the window can be wider than the rate limit window, we should adjust the total limit proportionally + // if the window can be wider than `rateLimitFlags.Window`, we should adjust the total withdraw limit proportionally pendingCctxWindow := height - lowestPendingCctxHeight + 1 if pendingCctxWindow > rateLimitFlags.Window { - pendingCctxsLimitInZeta = blockLimitInZeta.Mul(sdk.NewDec(pendingCctxWindow)) + withdrawWindow = pendingCctxWindow + withdrawLimitInZeta = blockLimitInZeta.Mul(sdk.NewDec(pendingCctxWindow)) } } // query backwards for potential missed pending cctxs for each foreign chain -LoopBackwards: for _, chain := range chains { // we should at least query 1000 prior to find any pending cctx that we might have missed // this logic is needed because a confirmation of higher nonce will automatically update the p.NonceLow @@ -151,12 +155,6 @@ LoopBackwards: endNonce = 0 } - // add the pending nonces to the total pending - // Note: the `totalPending` may not be accurate only if the rate limiter triggers early exit - // `totalPending` is now used for metrics only and it's okay to trade off accuracy for performance - // #nosec G701 always in range - totalPending += uint64(pendingNonces.NonceHigh - pendingNonces.NonceLow) - // query cctx by nonce backwards to the left boundary of the rate limit sliding window for nonce := startNonce; nonce >= 0; nonce-- { cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, chain.ChainId, nonce) @@ -165,53 +163,63 @@ LoopBackwards: } inWindow := isCctxInWindow(cctx) - // We should at least go backwards by 1000 nonces to pick up missed pending cctxs - // We might go even further back if rate limiter is enabled and the endNonce hasn't hit the left window boundary yet - // There are two criteria to stop scanning backwards: - // criteria #1: we'll stop at the left window boundary if the `endNonce` hasn't hit it yet + // we should at least go backwards by 1000 nonces to pick up missed pending cctxs + // we might go even further back if rate limiter is enabled and the endNonce hasn't hit the left window boundary yet + // stop at the left window boundary if the `endNonce` hasn't hit it yet if nonce < endNonce && !inWindow { break } - // criteria #2: we should finish the RPC call if the rate limit is exceeded - if inWindow && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, windowLimitInZeta) { + // skip the cctx if rate limit is exceeded but still accumulate the total withdraw value + if inWindow && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalWithdrawInZeta, withdrawLimitInZeta) { limitExceeded = true - break LoopBackwards + continue } // only take a `limit` number of pending cctxs as result but still count the total pending cctxs if IsPending(cctx) { totalPending++ - if !maxCCTXsReached() { + if !maxCCTXsReached(cctxs) { cctxs = append(cctxs, cctx) } } } } + // remember the number of missed pending cctxs + missedPending := len(cctxs) + // query forwards for pending cctxs for each foreign chain -LoopForwards: for _, chain := range chains { // query the pending cctxs in range [NonceLow, NonceHigh) pendingNonces := pendingNoncesMap[chain.ChainId] + + // #nosec G701 always in range + totalPending += uint64(pendingNonces.NonceHigh - pendingNonces.NonceLow) + for nonce := pendingNonces.NonceLow; nonce < pendingNonces.NonceHigh; nonce++ { cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, chain.ChainId, nonce) if err != nil { return nil, err } - // only take a `limit` number of pending cctxs as result - if maxCCTXsReached() { - break LoopForwards - } - // criteria #2: we should finish the RPC call if the rate limit is exceeded - if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, pendingCctxsLimitInZeta) { + // skip the cctx if rate limit is exceeded but still accumulate the total withdraw value + if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalWithdrawInZeta, withdrawLimitInZeta) { limitExceeded = true - break LoopForwards + continue + } + // only take a `limit` number of pending cctxs as result + if maxCCTXsReached(cctxs) { + continue } cctxs = append(cctxs, cctx) } } + // if the rate limit is exceeded, only return the missed pending cctxs + if limitExceeded { + cctxs = cctxs[:missedPending] + } + // sort the cctxs by chain ID and nonce (lower nonce holds higher priority for scheduling) sort.Slice(cctxs, func(i, j int) bool { if cctxs[i].GetCurrentOutTxParam().ReceiverChainId == cctxs[j].GetCurrentOutTxParam().ReceiverChainId { @@ -221,10 +229,11 @@ LoopForwards: }) return &types.QueryListPendingCctxWithinRateLimitResponse{ - CrossChainTx: cctxs, - TotalPending: totalPending, - ValueWithinWindow: totalCctxValueInZeta.TruncateInt().Uint64(), - RateLimitExceeded: limitExceeded, + CrossChainTx: cctxs, + TotalPending: totalPending, + CurrentWithdrawWindow: withdrawWindow, + CurrentWithdrawRate: totalWithdrawInZeta.Mul(sdk.NewDec(10).Power(18)).Quo(sdk.NewDec(withdrawWindow)).String(), + RateLimitExceeded: limitExceeded, }, nil } @@ -298,9 +307,9 @@ func rateLimitExceeded( erc20CoinRates map[int64]map[string]sdk.Dec, foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins, currentCctxValue *sdk.Dec, - rateLimitValue sdk.Dec, + withdrawLimitInZeta sdk.Dec, ) bool { amountZeta := ConvertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) *currentCctxValue = currentCctxValue.Add(amountZeta) - return currentCctxValue.GT(rateLimitValue) + return currentCctxValue.GT(withdrawLimitInZeta) } diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go index f5ed9e4413..2aa96ca51b 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -145,7 +145,7 @@ func Test_ConvertCctxValue(t *testing.T) { setupForeignCoins(t, ctx, zk, zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT) // Set rate limiter flags - rateLimiterFlags := createTestRateLimiterFlags(500, math.NewUint(5000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8") + rateLimiterFlags := createTestRateLimiterFlags(500, math.NewUint(10), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8") k.SetRateLimiterFlags(ctx, *rateLimiterFlags) // get rate limiter rates @@ -286,6 +286,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { // define test cases tests := []struct { name string + fallback bool rateLimitFlags *types.RateLimiterFlags // Eth chain cctxs setup @@ -303,226 +304,206 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { queryLimit uint32 // expected results - expectedCctxs []*types.CrossChainTx - expectedTotalPending uint64 - expectedTotalValue uint64 - rateLimitExceeded bool + expectedCctxs []*types.CrossChainTx + expectedTotalPending uint64 + expectedWithdrawWindow int64 + expectedWithdrawRate string + rateLimitExceeded bool }{ + // { + // name: "should use fallback query if rate limiter is disabled", + // fallback: true, + // rateLimitFlags: nil, // no rate limiter flags set in the keeper + // ethMinedCctxs: ethMinedCctxs, + // ethPendingCctxs: ethPendingCctxs, + // ethPendingNonces: observertypes.PendingNonces{ + // ChainId: ethChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // btcMinedCctxs: btcMinedCctxs, + // btcPendingCctxs: btcPendingCctxs, + // btcPendingNonces: observertypes.PendingNonces{ + // ChainId: btcChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // currentHeight: 1199, + // queryLimit: keeper.MaxPendingCctxs, + // expectedCctxs: append(append([]*types.CrossChainTx{}, btcPendingCctxs...), ethPendingCctxs...), + // expectedTotalPending: 400, + // }, + // { + // name: "should use fallback query if rate is 0", + // fallback: true, + // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(0), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + // ethMinedCctxs: ethMinedCctxs, + // ethPendingCctxs: ethPendingCctxs, + // ethPendingNonces: observertypes.PendingNonces{ + // ChainId: ethChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // btcMinedCctxs: btcMinedCctxs, + // btcPendingCctxs: btcPendingCctxs, + // btcPendingNonces: observertypes.PendingNonces{ + // ChainId: btcChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // currentHeight: 1199, + // queryLimit: keeper.MaxPendingCctxs, + // expectedCctxs: append(append([]*types.CrossChainTx{}, btcPendingCctxs...), ethPendingCctxs...), + // expectedTotalPending: 400, + // }, + // { + // name: "can retrieve all pending cctx without exceeding rate limit", + // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(10*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + // ethMinedCctxs: ethMinedCctxs, + // ethPendingCctxs: ethPendingCctxs, + // ethPendingNonces: observertypes.PendingNonces{ + // ChainId: ethChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // btcMinedCctxs: btcMinedCctxs, + // btcPendingCctxs: btcPendingCctxs, + // btcPendingNonces: observertypes.PendingNonces{ + // ChainId: btcChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // currentHeight: 1199, + // queryLimit: keeper.MaxPendingCctxs, + // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...), + // expectedTotalPending: 400, + // expectedWithdrawWindow: 500, // the sliding window + // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + // rateLimitExceeded: false, + // }, + // { + // name: "can loop backwards all the way to endNonce 0", + // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(10*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + // ethMinedCctxs: ethMinedCctxs, + // ethPendingCctxs: ethPendingCctxs, + // ethPendingNonces: observertypes.PendingNonces{ + // ChainId: ethChainID, + // NonceLow: 999, // endNonce will be set to 0 (NonceLow - 1000 < 0) + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // btcMinedCctxs: btcMinedCctxs, + // btcPendingCctxs: btcPendingCctxs, + // btcPendingNonces: observertypes.PendingNonces{ + // ChainId: btcChainID, + // NonceLow: 999, // endNonce will be set to 0 (NonceLow - 1000 < 0) + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // currentHeight: 1199, + // queryLimit: keeper.MaxPendingCctxs, + // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...), + // expectedTotalPending: 400, + // expectedWithdrawWindow: 500, // the sliding window + // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + // rateLimitExceeded: false, + // }, + // { + // name: "set a low rate (rate < 2.4 ZETA) to exceed rate limit in backward loop", + // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(2*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + // ethMinedCctxs: ethMinedCctxs, + // ethPendingCctxs: ethPendingCctxs, + // ethPendingNonces: observertypes.PendingNonces{ + // ChainId: ethChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // btcMinedCctxs: btcMinedCctxs, + // btcPendingCctxs: btcPendingCctxs, + // btcPendingNonces: observertypes.PendingNonces{ + // ChainId: btcChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // currentHeight: 1199, + // queryLimit: keeper.MaxPendingCctxs, + // // return missed cctxs only if rate limit is exceeded + // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...), + // expectedTotalPending: 400, + // expectedWithdrawWindow: 500, // the sliding window + // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + // rateLimitExceeded: true, + // }, + // { + // name: "set a lower gRPC request limit and reach the limit of the query in forward loop", + // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(10*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + // ethMinedCctxs: ethMinedCctxs, + // ethPendingCctxs: ethPendingCctxs, + // ethPendingNonces: observertypes.PendingNonces{ + // ChainId: ethChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // btcMinedCctxs: btcMinedCctxs, + // btcPendingCctxs: btcPendingCctxs, + // btcPendingNonces: observertypes.PendingNonces{ + // ChainId: btcChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // currentHeight: 1199, + // queryLimit: 300, // 300 < keeper.MaxPendingCctxs + // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs...), + // expectedTotalPending: 400, + // expectedWithdrawWindow: 500, // the sliding window + // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + // rateLimitExceeded: false, + // }, + // { + // name: "set a median rate (2.4 ZETA < rate < 3 ZETA) to exceed rate limit in forward loop", + // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(26*1e17), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + // ethMinedCctxs: ethMinedCctxs, + // ethPendingCctxs: ethPendingCctxs, + // ethPendingNonces: observertypes.PendingNonces{ + // ChainId: ethChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // btcMinedCctxs: btcMinedCctxs, + // btcPendingCctxs: btcPendingCctxs, + // btcPendingNonces: observertypes.PendingNonces{ + // ChainId: btcChainID, + // NonceLow: 1099, + // NonceHigh: 1199, + // Tss: tss.TssPubkey, + // }, + // currentHeight: 1199, + // queryLimit: keeper.MaxPendingCctxs, + // // return missed cctxs only if rate limit is exceeded + // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...), + // expectedTotalPending: 400, + // expectedWithdrawWindow: 500, // the sliding window + // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + // rateLimitExceeded: true, + // }, { - name: "should use fallback query if rate limiter is disabled", - rateLimitFlags: nil, // no rate limiter flags set in the keeper - ethMinedCctxs: ethMinedCctxs, - ethPendingCctxs: ethPendingCctxs, - ethPendingNonces: observertypes.PendingNonces{ - ChainId: ethChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - btcMinedCctxs: btcMinedCctxs, - btcPendingCctxs: btcPendingCctxs, - btcPendingNonces: observertypes.PendingNonces{ - ChainId: btcChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - currentHeight: 1199, - queryLimit: keeper.MaxPendingCctxs, - expectedCctxs: append(append([]*types.CrossChainTx{}, btcPendingCctxs...), ethPendingCctxs...), - expectedTotalPending: 400, - }, - { - name: "should use fallback query if rate is 0", - rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(0), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMinedCctxs: ethMinedCctxs, - ethPendingCctxs: ethPendingCctxs, - ethPendingNonces: observertypes.PendingNonces{ - ChainId: ethChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - btcMinedCctxs: btcMinedCctxs, - btcPendingCctxs: btcPendingCctxs, - btcPendingNonces: observertypes.PendingNonces{ - ChainId: btcChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - currentHeight: 1199, - queryLimit: keeper.MaxPendingCctxs, - expectedCctxs: append(append([]*types.CrossChainTx{}, btcPendingCctxs...), ethPendingCctxs...), - expectedTotalPending: 400, - }, - { - name: "can retrieve pending cctx in range without exceeding rate limit", - rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(5000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMinedCctxs: ethMinedCctxs, - ethPendingCctxs: ethPendingCctxs, - ethPendingNonces: observertypes.PendingNonces{ - ChainId: ethChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - btcMinedCctxs: btcMinedCctxs, - btcPendingCctxs: btcPendingCctxs, - btcPendingNonces: observertypes.PendingNonces{ - ChainId: btcChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - currentHeight: 1199, - queryLimit: keeper.MaxPendingCctxs, - expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...), - expectedTotalPending: 400, - expectedTotalValue: 1500, // 500 (window) * (2.5 + 0.5) - rateLimitExceeded: false, - }, - { - name: "can loop backwards all the way to endNonce 0", - rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(5000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMinedCctxs: ethMinedCctxs, - ethPendingCctxs: ethPendingCctxs, - ethPendingNonces: observertypes.PendingNonces{ - ChainId: ethChainID, - NonceLow: 999, // endNonce will set to 0 as NonceLow - 1000 < 0 - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - btcMinedCctxs: btcMinedCctxs, - btcPendingCctxs: btcPendingCctxs, - btcPendingNonces: observertypes.PendingNonces{ - ChainId: btcChainID, - NonceLow: 999, // endNonce will set to 0 as NonceLow - 1000 < 0 - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - currentHeight: 1199, - queryLimit: keeper.MaxPendingCctxs, - expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...), - expectedTotalPending: 400, - expectedTotalValue: 1500, // 500 (window) * (2.5 + 0.5) - rateLimitExceeded: false, - }, - { - name: "set a low rate (< 1200) to early break the LoopBackwards with criteria #2", - rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(1000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), // 1000 < 1200 - ethMinedCctxs: ethMinedCctxs, - ethPendingCctxs: ethPendingCctxs, - ethPendingNonces: observertypes.PendingNonces{ - ChainId: ethChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - btcMinedCctxs: btcMinedCctxs, - btcPendingCctxs: btcPendingCctxs, - btcPendingNonces: observertypes.PendingNonces{ - ChainId: btcChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - currentHeight: 1199, - queryLimit: keeper.MaxPendingCctxs, - expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...), - expectedTotalPending: 400, - rateLimitExceeded: true, - }, - { - // this test case is to break the LoopBackwards with criteria #1: the 1st break in LoopBackwards - name: "set high rate and wide window to break inner loop of LoopBackwards when left window boundary is reached", - // The left boundary will be 49 (currentHeight-1150), which will be less than the endNonce 99 (1099 - 1000) - rateLimitFlags: createTestRateLimiterFlags(1150, math.NewUint(10000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMinedCctxs: ethMinedCctxs, - ethPendingCctxs: ethPendingCctxs, - ethPendingNonces: observertypes.PendingNonces{ - ChainId: ethChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - btcMinedCctxs: btcMinedCctxs, - btcPendingCctxs: btcPendingCctxs, - btcPendingNonces: observertypes.PendingNonces{ - ChainId: btcChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - currentHeight: 1199, - queryLimit: keeper.MaxPendingCctxs, - expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...), - expectedTotalPending: 400, - expectedTotalValue: 3450, // 1150 (window) * (2.5 + 0.5) - rateLimitExceeded: false, - }, - { - name: "set lower request limit to early break the LoopForwards loop", - rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(5000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - ethMinedCctxs: ethMinedCctxs, - ethPendingCctxs: ethPendingCctxs, - ethPendingNonces: observertypes.PendingNonces{ - ChainId: ethChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - btcMinedCctxs: btcMinedCctxs, - btcPendingCctxs: btcPendingCctxs, - btcPendingNonces: observertypes.PendingNonces{ - ChainId: btcChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - currentHeight: 1199, - queryLimit: 300, // 300 < keeper.MaxPendingCctxs - expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs...), - expectedTotalPending: 400, - expectedTotalValue: 1250, // 500 * 0.5 + 400 * 2.5 - rateLimitExceeded: false, - }, - { - // all pending cctxs fall within the sliding window in this test case. - // this test case is to break the LoopBackwards with criteria #2: the 2nd break in LoopForwards - name: "set rate to middle value (1200 < rate < 1500) to early break the LoopForwards when rate limit is exceeded", - rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(1300), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), // 1200 < 1300 < 1500 - ethMinedCctxs: ethMinedCctxs, - ethPendingCctxs: ethPendingCctxs, - ethPendingNonces: observertypes.PendingNonces{ - ChainId: ethChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - btcMinedCctxs: btcMinedCctxs, - btcPendingCctxs: btcPendingCctxs, - btcPendingNonces: observertypes.PendingNonces{ - ChainId: btcChainID, - NonceLow: 1099, - NonceHigh: 1199, - Tss: tss.TssPubkey, - }, - currentHeight: 1199, - queryLimit: keeper.MaxPendingCctxs, - // 120 ETH cctx + 200 BTC cctx - expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:120]...), btcPendingCctxs...), - expectedTotalPending: 400, - rateLimitExceeded: true, - }, - { - // many pending cctxs fall beyond the sliding window in this test case. - // this test case is to break the LoopBackwards with criteria #2: the 2nd break in LoopForwards - name: "set low rate and narrow window to early break the LoopForwards when rate limit is exceeded", + // the pending cctxs window is wider than the rate limiter sliding window in this test case. + name: "set low rate and narrow window to early exceed rate limit in forward loop", // the left boundary will be 1149 (currentHeight-50), the pending nonces [1099, 1148] fall beyond the left boundary. // `pendingCctxWindow` is 100 which is wider than rate limiter window 50. // give a block rate of 2 ZETA/block, the max value allowed should be 100 * 2 = 200 ZETA - rateLimitFlags: createTestRateLimiterFlags(50, math.NewUint(100), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + rateLimitFlags: createTestRateLimiterFlags(50, math.NewUint(2*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), ethMinedCctxs: ethMinedCctxs, ethPendingCctxs: ethPendingCctxs, ethPendingNonces: observertypes.PendingNonces{ @@ -541,10 +522,12 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { }, currentHeight: 1199, queryLimit: keeper.MaxPendingCctxs, - // 160 ETH cctx + 200 BTC cctx - expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:160]...), btcPendingCctxs...), - expectedTotalPending: 400, - rateLimitExceeded: true, + // return missed cctxs only if rate limit is exceeded + expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...), + expectedTotalPending: 400, + expectedWithdrawWindow: 100, // 100 > sliding window 50 + expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + rateLimitExceeded: true, }, } @@ -582,15 +565,13 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{Limit: tt.queryLimit}) require.NoError(t, err) require.EqualValues(t, tt.expectedCctxs, res.CrossChainTx) - require.EqualValues(t, tt.expectedTotalPending, res.TotalPending) + require.Equal(t, tt.expectedTotalPending, res.TotalPending) - // check rate limiter related fields only if rate limiter flags is enabled - if tt.rateLimitFlags != nil { - if !tt.rateLimitExceeded { - require.EqualValues(t, tt.expectedTotalValue, res.ValueWithinWindow) - } else { - require.True(t, res.ValueWithinWindow > tt.rateLimitFlags.Rate.Uint64()) - } + // check rate limiter related fields only if it's not a fallback query + if !tt.fallback { + require.Equal(t, tt.expectedWithdrawWindow, res.CurrentWithdrawWindow) + require.Equal(t, tt.expectedWithdrawRate, res.CurrentWithdrawRate) + require.Equal(t, tt.rateLimitExceeded, res.RateLimitExceeded) } }) } diff --git a/x/crosschain/types/query.pb.go b/x/crosschain/types/query.pb.go index 4d7c826736..8fce000ace 100644 --- a/x/crosschain/types/query.pb.go +++ b/x/crosschain/types/query.pb.go @@ -1636,10 +1636,11 @@ func (m *QueryListPendingCctxWithinRateLimitRequest) GetLimit() uint32 { } type QueryListPendingCctxWithinRateLimitResponse struct { - CrossChainTx []*CrossChainTx `protobuf:"bytes,1,rep,name=cross_chain_tx,json=crossChainTx,proto3" json:"cross_chain_tx,omitempty"` - TotalPending uint64 `protobuf:"varint,2,opt,name=total_pending,json=totalPending,proto3" json:"total_pending,omitempty"` - ValueWithinWindow uint64 `protobuf:"varint,3,opt,name=value_within_window,json=valueWithinWindow,proto3" json:"value_within_window,omitempty"` - RateLimitExceeded bool `protobuf:"varint,4,opt,name=rate_limit_exceeded,json=rateLimitExceeded,proto3" json:"rate_limit_exceeded,omitempty"` + CrossChainTx []*CrossChainTx `protobuf:"bytes,1,rep,name=cross_chain_tx,json=crossChainTx,proto3" json:"cross_chain_tx,omitempty"` + TotalPending uint64 `protobuf:"varint,2,opt,name=total_pending,json=totalPending,proto3" json:"total_pending,omitempty"` + CurrentWithdrawWindow int64 `protobuf:"varint,3,opt,name=current_withdraw_window,json=currentWithdrawWindow,proto3" json:"current_withdraw_window,omitempty"` + CurrentWithdrawRate string `protobuf:"bytes,4,opt,name=current_withdraw_rate,json=currentWithdrawRate,proto3" json:"current_withdraw_rate,omitempty"` + RateLimitExceeded bool `protobuf:"varint,5,opt,name=rate_limit_exceeded,json=rateLimitExceeded,proto3" json:"rate_limit_exceeded,omitempty"` } func (m *QueryListPendingCctxWithinRateLimitResponse) Reset() { @@ -1693,13 +1694,20 @@ func (m *QueryListPendingCctxWithinRateLimitResponse) GetTotalPending() uint64 { return 0 } -func (m *QueryListPendingCctxWithinRateLimitResponse) GetValueWithinWindow() uint64 { +func (m *QueryListPendingCctxWithinRateLimitResponse) GetCurrentWithdrawWindow() int64 { if m != nil { - return m.ValueWithinWindow + return m.CurrentWithdrawWindow } return 0 } +func (m *QueryListPendingCctxWithinRateLimitResponse) GetCurrentWithdrawRate() string { + if m != nil { + return m.CurrentWithdrawRate + } + return "" +} + func (m *QueryListPendingCctxWithinRateLimitResponse) GetRateLimitExceeded() bool { if m != nil { return m.RateLimitExceeded @@ -2110,128 +2118,130 @@ func init() { func init() { proto.RegisterFile("crosschain/query.proto", fileDescriptor_65a992045e92a606) } var fileDescriptor_65a992045e92a606 = []byte{ - // 1931 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0x41, 0x6f, 0xdc, 0xc6, - 0x15, 0xf6, 0x68, 0xed, 0x44, 0x1e, 0xc9, 0x56, 0x34, 0x51, 0x5d, 0x95, 0x91, 0x56, 0x36, 0x55, - 0x5b, 0xaa, 0x54, 0x2d, 0x23, 0x39, 0x56, 0x6a, 0x5b, 0x29, 0xba, 0x92, 0x23, 0x45, 0xad, 0x92, - 0x28, 0x0b, 0x15, 0x2e, 0x5c, 0x14, 0xc4, 0x88, 0x3b, 0xe1, 0x12, 0xa1, 0x48, 0x65, 0x39, 0x6b, - 0xaf, 0x2c, 0xe8, 0x12, 0xa0, 0x3d, 0x17, 0xc8, 0xa1, 0x97, 0x5e, 0x8b, 0xe6, 0xd0, 0x43, 0x0f, - 0x45, 0x7b, 0x28, 0x90, 0xa2, 0x68, 0xeb, 0xfa, 0x18, 0xa0, 0x40, 0x51, 0xb4, 0x40, 0x51, 0xd8, - 0xfd, 0x05, 0xfd, 0x05, 0x05, 0x87, 0x8f, 0xbb, 0x43, 0x2e, 0xb9, 0x3b, 0x5a, 0x6d, 0x0e, 0x3e, - 0x79, 0xc9, 0x99, 0xf7, 0xe6, 0xfb, 0xde, 0xbc, 0x79, 0x7c, 0xf3, 0x59, 0xf8, 0x8a, 0x55, 0xf7, - 0x83, 0xc0, 0xaa, 0x51, 0xc7, 0x33, 0x3e, 0x6e, 0xb0, 0xfa, 0x51, 0xe9, 0xb0, 0xee, 0x73, 0x9f, - 0x4c, 0x3f, 0x66, 0x9c, 0x8a, 0xd7, 0x25, 0xf1, 0xcb, 0xaf, 0xb3, 0x52, 0x7b, 0xaa, 0xb6, 0x60, - 0xf9, 0xc1, 0x81, 0x1f, 0x18, 0xfb, 0x34, 0x60, 0x91, 0x9d, 0xf1, 0x70, 0x79, 0x9f, 0x71, 0xba, - 0x6c, 0x1c, 0x52, 0xdb, 0xf1, 0x28, 0x77, 0x7c, 0x2f, 0x72, 0xa5, 0xcd, 0x48, 0x4b, 0x88, 0x9f, - 0xa6, 0xf8, 0x6d, 0xf2, 0x26, 0x4c, 0xd0, 0xa4, 0x09, 0x36, 0x0d, 0xcc, 0xc3, 0xba, 0x63, 0x31, - 0x18, 0x9b, 0x95, 0xc6, 0x84, 0x8d, 0x59, 0xa3, 0x41, 0xcd, 0xe4, 0xbe, 0x69, 0x59, 0x2d, 0x07, - 0xc5, 0x8e, 0x49, 0xbc, 0x4e, 0xad, 0x8f, 0x58, 0x1d, 0xc6, 0x75, 0x69, 0xdc, 0xa5, 0x01, 0x37, - 0xf7, 0x5d, 0xdf, 0xfa, 0xc8, 0xac, 0x31, 0xc7, 0xae, 0xf1, 0x0c, 0x94, 0x7e, 0x83, 0x77, 0x3a, - 0x91, 0x91, 0xd4, 0x29, 0x67, 0xa6, 0xeb, 0x1c, 0x38, 0x9c, 0xd5, 0xcd, 0x0f, 0x5d, 0x6a, 0x07, - 0x30, 0x69, 0xc2, 0xf6, 0x6d, 0x5f, 0xfc, 0x34, 0xc2, 0x5f, 0xf0, 0x76, 0xca, 0xf6, 0x7d, 0xdb, - 0x65, 0x06, 0x3d, 0x74, 0x0c, 0xea, 0x79, 0x3e, 0x17, 0xe1, 0x01, 0x1b, 0x7d, 0x0a, 0x6b, 0x1f, - 0x84, 0x11, 0x7c, 0xc0, 0x38, 0x2d, 0x5b, 0x96, 0xdf, 0xf0, 0xb8, 0xe3, 0xd9, 0x15, 0xf6, 0x71, - 0x83, 0x05, 0x5c, 0x7f, 0x17, 0xbf, 0x96, 0x39, 0x1a, 0x1c, 0xfa, 0x5e, 0xc0, 0x48, 0x09, 0xbf, - 0x4a, 0xf7, 0xfd, 0x3a, 0x67, 0x55, 0x33, 0xdc, 0x27, 0x93, 0x1e, 0x84, 0x33, 0x26, 0xd1, 0x55, - 0x34, 0x7f, 0xb1, 0x32, 0x0e, 0x43, 0xc2, 0x56, 0x0c, 0xb4, 0xdc, 0x6d, 0x31, 0xfe, 0x7e, 0x83, - 0xef, 0x35, 0xf7, 0x22, 0x8e, 0xb0, 0x1a, 0x99, 0xc4, 0x2f, 0x0b, 0x86, 0xdb, 0xf7, 0x84, 0x8b, - 0x42, 0x25, 0x7e, 0x24, 0x13, 0xf8, 0x82, 0xe7, 0x7b, 0x16, 0x9b, 0x1c, 0xba, 0x8a, 0xe6, 0xcf, - 0x57, 0xa2, 0x07, 0xbd, 0x81, 0xa7, 0xb2, 0xdd, 0x01, 0xbc, 0xef, 0xe3, 0x51, 0x5f, 0x7a, 0x2f, - 0x9c, 0x8e, 0xac, 0x2c, 0x96, 0xba, 0x66, 0x57, 0x49, 0x76, 0xb5, 0x7e, 0xfe, 0xe9, 0xbf, 0x67, - 0xce, 0x55, 0x12, 0x6e, 0x74, 0x06, 0x2c, 0xca, 0xae, 0x9b, 0xc5, 0x62, 0x13, 0xe3, 0x76, 0x16, - 0xc2, 0x9a, 0x37, 0x4a, 0x51, 0xca, 0x96, 0xc2, 0x94, 0x2d, 0x45, 0xa9, 0x0e, 0x29, 0x5b, 0xda, - 0xa5, 0x36, 0x03, 0xdb, 0x8a, 0x64, 0xa9, 0x7f, 0x8e, 0x80, 0x5e, 0xc7, 0x3a, 0xb9, 0xf4, 0x0a, - 0x03, 0xa0, 0x47, 0xb6, 0x12, 0xf8, 0x87, 0x04, 0xfe, 0xb9, 0x9e, 0xf8, 0x23, 0x4c, 0x09, 0x02, - 0x9f, 0x20, 0xac, 0x67, 0x11, 0x58, 0x3f, 0xda, 0x08, 0x91, 0xc4, 0xf1, 0x9a, 0xc0, 0x17, 0x04, - 0x32, 0xd8, 0xf3, 0xe8, 0x21, 0x15, 0xc5, 0xa1, 0xbe, 0xa3, 0xf8, 0x17, 0x84, 0x67, 0xbb, 0x82, - 0x78, 0x41, 0x82, 0xf9, 0x13, 0x84, 0xaf, 0xc5, 0x3c, 0xb6, 0xbd, 0xbc, 0x58, 0x7e, 0x0d, 0x0f, - 0x47, 0xe5, 0xcd, 0xa9, 0x26, 0x8f, 0x50, 0x75, 0x60, 0x01, 0xfd, 0xa3, 0xb4, 0xab, 0x59, 0x40, - 0x20, 0x9e, 0x15, 0x3c, 0xe2, 0x78, 0xe9, 0x70, 0x2e, 0xf4, 0x08, 0xa7, 0xec, 0x2f, 0x8a, 0xa6, - 0xec, 0x64, 0x70, 0xc1, 0x94, 0x4e, 0xb0, 0xb4, 0x64, 0x30, 0xe8, 0x13, 0xfc, 0x7b, 0xe9, 0x04, - 0x27, 0xd7, 0x79, 0x11, 0x82, 0x74, 0x17, 0x4f, 0xc7, 0xd5, 0x35, 0x5c, 0xf2, 0x1d, 0x1a, 0xd4, - 0xf6, 0xfc, 0x0d, 0x8b, 0x37, 0xe3, 0x30, 0x69, 0x78, 0xd8, 0x81, 0x01, 0x28, 0xf9, 0xad, 0x67, - 0xfd, 0x04, 0x17, 0xf3, 0x8c, 0x81, 0xfb, 0x0f, 0xf1, 0x65, 0x27, 0x31, 0x02, 0x81, 0x5e, 0x52, - 0xa0, 0xdf, 0x36, 0x82, 0x08, 0xa4, 0x5c, 0xe9, 0x6b, 0xb0, 0x7c, 0x72, 0xf2, 0x3d, 0xca, 0xa9, - 0x0a, 0xf8, 0xc7, 0x78, 0x26, 0xd7, 0x1a, 0xd0, 0xdf, 0xc7, 0x97, 0x36, 0x42, 0x4c, 0x22, 0xe9, - 0xf7, 0x9a, 0x81, 0x62, 0xbd, 0x90, 0x6d, 0x00, 0x7a, 0xd2, 0x8f, 0x6e, 0x43, 0xd4, 0x21, 0x65, - 0x3a, 0xa3, 0x3e, 0xa8, 0xe4, 0x7c, 0x82, 0x20, 0x46, 0x19, 0x2b, 0x75, 0xd9, 0xa2, 0xc2, 0x80, - 0xb6, 0x68, 0x70, 0x79, 0x6a, 0xe0, 0xaf, 0xc6, 0xa9, 0xb6, 0x45, 0x83, 0xdd, 0xb0, 0x7d, 0x93, - 0x3e, 0x2d, 0x8e, 0x57, 0x65, 0x4d, 0xd8, 0xe1, 0xe8, 0x41, 0x37, 0xf1, 0x64, 0xa7, 0x01, 0x50, - 0xde, 0xc0, 0xc3, 0xf1, 0x3b, 0x88, 0xed, 0x5c, 0x0f, 0xb2, 0x2d, 0x17, 0x2d, 0x43, 0x9d, 0x02, - 0xa2, 0xb2, 0xeb, 0xa6, 0x11, 0x0d, 0x6a, 0xf7, 0x3e, 0x43, 0x40, 0x22, 0xb1, 0x46, 0x26, 0x89, - 0x42, 0x5f, 0x24, 0x06, 0xb7, 0x3f, 0xab, 0xed, 0x52, 0xb0, 0x43, 0x03, 0xbe, 0x1e, 0x76, 0xbf, - 0xef, 0x88, 0xe6, 0xb7, 0xfb, 0x36, 0x1d, 0xc3, 0x29, 0xcc, 0xb2, 0x03, 0xa2, 0x3f, 0xc0, 0x63, - 0xa9, 0x21, 0x08, 0x69, 0xa9, 0x07, 0xdf, 0xb4, 0xc3, 0xb4, 0x1b, 0xbd, 0xd6, 0x3e, 0x1c, 0x39, - 0xa0, 0x07, 0xb5, 0x93, 0x7f, 0x46, 0xc0, 0x33, 0x6b, 0xa9, 0x6e, 0x3c, 0x0b, 0x03, 0xe0, 0x39, - 0xb8, 0x5d, 0x5e, 0xc4, 0xaf, 0xc6, 0xbb, 0x25, 0x57, 0xab, 0xec, 0xad, 0xdd, 0x81, 0x4b, 0x07, - 0x4c, 0x5e, 0x3f, 0x7a, 0x2f, 0xec, 0xe7, 0xfb, 0xbd, 0x06, 0xd8, 0x78, 0x22, 0xb9, 0x34, 0x44, - 0xed, 0x7d, 0x3c, 0x2a, 0xd7, 0x56, 0xc5, 0xf6, 0x5f, 0x36, 0xa9, 0x24, 0x1c, 0xe8, 0x3f, 0x02, - 0x8e, 0x65, 0xd7, 0xfd, 0x32, 0x2a, 0xf2, 0xaf, 0x11, 0x10, 0x69, 0xf9, 0xcf, 0x25, 0x52, 0x38, - 0x13, 0x91, 0xc1, 0xed, 0xfa, 0x7b, 0xd0, 0x48, 0xed, 0x38, 0x01, 0xdf, 0x65, 0x5e, 0xd5, 0xf1, - 0x6c, 0x39, 0x32, 0x5d, 0xda, 0xd1, 0x09, 0x7c, 0x41, 0x5c, 0x61, 0xc5, 0xea, 0x97, 0x2a, 0xd1, - 0x83, 0xfe, 0x69, 0xdc, 0x31, 0x75, 0x38, 0xfc, 0xb2, 0x42, 0xa1, 0xe3, 0x51, 0xee, 0x73, 0xea, - 0xc2, 0x62, 0x90, 0x59, 0x89, 0x77, 0xfa, 0x3a, 0x5e, 0xc8, 0x02, 0x75, 0xdf, 0xe1, 0x35, 0xc7, - 0xab, 0x50, 0xce, 0x76, 0x42, 0xf0, 0x52, 0xca, 0x47, 0xcc, 0x90, 0xcc, 0xec, 0xc7, 0x43, 0x78, - 0x51, 0xc9, 0x09, 0x10, 0xfd, 0x00, 0x5f, 0x4e, 0xca, 0x15, 0x7d, 0x51, 0xb5, 0x64, 0xaa, 0xb3, - 0xf8, 0x92, 0xa0, 0x65, 0x1e, 0xe6, 0x73, 0x0d, 0xaf, 0xf4, 0x0f, 0xa9, 0xdb, 0x60, 0xe6, 0x23, - 0x01, 0xcc, 0x7c, 0xe4, 0x78, 0x55, 0xff, 0xd1, 0x64, 0x41, 0x4c, 0x1d, 0x17, 0x43, 0x11, 0xe4, - 0xfb, 0x62, 0x20, 0x9c, 0xdf, 0xd6, 0x23, 0x4c, 0xd6, 0xb4, 0x18, 0xab, 0xb2, 0xea, 0xe4, 0xf9, - 0xab, 0x68, 0x7e, 0xb8, 0x32, 0x5e, 0x8f, 0x79, 0xbd, 0x0d, 0x03, 0x2d, 0xbd, 0x21, 0x2c, 0x44, - 0x0f, 0x18, 0xa7, 0x89, 0xa2, 0xaa, 0xdf, 0x8a, 0xf3, 0x29, 0x35, 0x0a, 0x41, 0xb9, 0x82, 0x5f, - 0x92, 0xca, 0x7c, 0xa1, 0x02, 0x4f, 0xfa, 0x1e, 0x64, 0xcd, 0x86, 0xef, 0x3d, 0x64, 0xf5, 0xf0, - 0xab, 0xbe, 0xe7, 0x87, 0xe6, 0x1d, 0x15, 0xa5, 0x23, 0x0d, 0x35, 0x3c, 0x6c, 0xd3, 0x60, 0xa7, - 0x95, 0x89, 0x17, 0x2b, 0xad, 0x67, 0xfd, 0x17, 0x08, 0x7a, 0xb1, 0x4e, 0xb7, 0x80, 0xe7, 0x9b, - 0x78, 0xdc, 0x6f, 0xf0, 0x7d, 0xbf, 0xe1, 0x55, 0xb7, 0x68, 0xb0, 0xed, 0x85, 0x83, 0xb1, 0xfa, - 0xd1, 0x31, 0x10, 0xce, 0x16, 0x9a, 0x8b, 0xe5, 0xbb, 0x9b, 0x8c, 0xc1, 0xec, 0x68, 0xd1, 0xce, - 0x01, 0x32, 0x8f, 0xc7, 0xc2, 0x7f, 0xe5, 0x9a, 0x1f, 0x6d, 0x42, 0xfa, 0xb5, 0x3e, 0x87, 0xaf, - 0x0b, 0x98, 0xef, 0xb2, 0x20, 0xa0, 0x36, 0xdb, 0xa5, 0x41, 0xe0, 0x78, 0xf6, 0x6e, 0xdb, 0x63, - 0x1c, 0xdd, 0x4d, 0x7c, 0xa3, 0xd7, 0x44, 0x20, 0x36, 0x85, 0x2f, 0x7e, 0xd8, 0x82, 0x18, 0x11, - 0x6a, 0xbf, 0xd0, 0x8b, 0x10, 0xee, 0x56, 0xd6, 0xb2, 0xfa, 0xa6, 0x4b, 0xed, 0xf8, 0xfe, 0x14, - 0x5e, 0xfc, 0xa7, 0x73, 0x26, 0x80, 0x7f, 0x8a, 0x5f, 0xa9, 0xa7, 0xc6, 0xa0, 0x70, 0x1a, 0x3d, - 0xf2, 0x3b, 0xed, 0x12, 0xba, 0xcb, 0x0e, 0x77, 0x2b, 0x9f, 0x5d, 0xc3, 0x17, 0x04, 0x08, 0xf2, - 0x04, 0xe1, 0x51, 0xf9, 0xa2, 0x4e, 0xee, 0xf4, 0x58, 0xa3, 0x8b, 0x46, 0xa5, 0xdd, 0xed, 0xcb, - 0x36, 0xa2, 0xad, 0xbf, 0xf5, 0xc9, 0xdf, 0xfe, 0xfb, 0xe9, 0xd0, 0x9b, 0xe4, 0x96, 0x11, 0x9a, - 0x2e, 0x49, 0xaa, 0x64, 0x4b, 0xfa, 0x6b, 0x19, 0x19, 0xc7, 0xf0, 0xd5, 0x3b, 0x31, 0x8e, 0xc5, - 0x77, 0xee, 0x84, 0xfc, 0x0e, 0xe1, 0x31, 0xd9, 0x6f, 0xd9, 0x75, 0xd5, 0xb8, 0x64, 0x2b, 0x55, - 0x6a, 0x5c, 0x72, 0xd4, 0x27, 0x7d, 0x51, 0x70, 0xb9, 0x4e, 0x66, 0x15, 0xb8, 0x90, 0x7f, 0x21, - 0x7c, 0x25, 0x85, 0x1c, 0x04, 0x03, 0x52, 0xee, 0x03, 0x44, 0x52, 0xf5, 0xd0, 0xd6, 0xcf, 0xe2, - 0x02, 0xe8, 0xdc, 0x11, 0x74, 0xde, 0x20, 0x2b, 0x0a, 0x74, 0xc0, 0x16, 0x76, 0xe8, 0x84, 0xfc, - 0x13, 0xe1, 0xaf, 0x48, 0xb7, 0x72, 0x89, 0xdc, 0x77, 0x14, 0x91, 0xe5, 0x2a, 0x3a, 0x5a, 0xf9, - 0x0c, 0x1e, 0x80, 0xda, 0x9a, 0xa0, 0xb6, 0x4a, 0xde, 0xc8, 0xa1, 0xe6, 0x78, 0x39, 0xcc, 0x4c, - 0xa7, 0x7a, 0x42, 0x7e, 0x8b, 0xf0, 0xe5, 0x24, 0x39, 0xe5, 0x9c, 0xcb, 0xd0, 0x56, 0x94, 0x73, - 0x2e, 0x4b, 0x2f, 0xe9, 0x99, 0x73, 0x12, 0x93, 0x80, 0xfc, 0x15, 0x80, 0x4b, 0x77, 0xce, 0x35, - 0xc5, 0xc3, 0x9b, 0x79, 0xf3, 0xd6, 0xde, 0xea, 0xd3, 0x1a, 0xc0, 0x7f, 0x4b, 0x80, 0x5f, 0x21, - 0xaf, 0x77, 0x01, 0xdf, 0x36, 0x33, 0x8e, 0xe3, 0xe7, 0x13, 0xf2, 0x77, 0x84, 0x49, 0xa7, 0x16, - 0x41, 0x94, 0xf0, 0xe4, 0x2a, 0x20, 0xda, 0xb7, 0xfb, 0x35, 0x07, 0x3e, 0x65, 0xc1, 0xe7, 0x2e, - 0xb9, 0x9d, 0xcb, 0x27, 0xfd, 0x1f, 0x26, 0x66, 0x95, 0x72, 0x2a, 0x13, 0xfb, 0x03, 0xc2, 0xe3, - 0xc9, 0x15, 0xc2, 0xf4, 0x5a, 0x3b, 0x45, 0x8a, 0xf4, 0xb9, 0x4b, 0xb9, 0x9a, 0x87, 0xbe, 0x24, - 0x58, 0xcd, 0x91, 0xeb, 0x4a, 0xbb, 0x44, 0x7e, 0x85, 0xda, 0x77, 0x6d, 0xb2, 0xaa, 0x98, 0x20, - 0x29, 0x51, 0x40, 0x7b, 0xf3, 0xd4, 0x76, 0x00, 0xd6, 0x10, 0x60, 0xbf, 0x41, 0xe6, 0x72, 0xc0, - 0xda, 0x60, 0x10, 0xc6, 0xbc, 0xca, 0x9a, 0x27, 0xe4, 0x97, 0x08, 0x8f, 0xc4, 0x5e, 0xc2, 0x50, - 0xaf, 0x2a, 0x06, 0xab, 0x2f, 0xc4, 0x19, 0xd2, 0x84, 0x3e, 0x27, 0x10, 0x5f, 0x23, 0x33, 0x3d, - 0x10, 0x93, 0xcf, 0x11, 0x7e, 0x25, 0xdd, 0x77, 0x11, 0xa5, 0xe2, 0x91, 0xd3, 0x04, 0x6a, 0x6b, - 0xfd, 0x19, 0x2b, 0x86, 0xda, 0x4a, 0x63, 0x7d, 0x82, 0xf0, 0x88, 0xd4, 0x5a, 0x91, 0x7b, 0x2a, - 0xcb, 0xf7, 0x6a, 0xe1, 0xb4, 0xb7, 0xcf, 0xe8, 0x05, 0xd8, 0x2c, 0x08, 0x36, 0x5f, 0x27, 0x7a, - 0x0e, 0x1b, 0xa9, 0x1d, 0x25, 0x4f, 0x51, 0x87, 0xfa, 0x40, 0x54, 0x4b, 0x61, 0xb6, 0x76, 0xa2, - 0x56, 0x7a, 0xf2, 0x75, 0x1f, 0x7d, 0x55, 0xc0, 0x7f, 0x9d, 0x94, 0x72, 0xe0, 0xbb, 0x49, 0xbb, - 0x56, 0xfa, 0xff, 0x09, 0x61, 0x92, 0xf2, 0x19, 0x9e, 0x02, 0xd5, 0x92, 0x71, 0x16, 0x36, 0xf9, - 0xea, 0x8e, 0x5e, 0x12, 0x6c, 0xe6, 0xc9, 0x0d, 0x35, 0x36, 0xe4, 0xe7, 0x08, 0x9f, 0x17, 0xc5, - 0x67, 0x45, 0x31, 0x8c, 0x72, 0x79, 0xbc, 0x79, 0x2a, 0x1b, 0xc5, 0xef, 0xae, 0x05, 0x1f, 0x2c, - 0x11, 0xe4, 0xdf, 0x20, 0x3c, 0x22, 0xa9, 0x3a, 0xe4, 0xf6, 0x29, 0x56, 0x4c, 0x2a, 0x41, 0xfd, - 0x81, 0xbd, 0x25, 0xc0, 0x1a, 0x64, 0xa9, 0x2b, 0xd8, 0x8e, 0xe6, 0xfa, 0x67, 0x08, 0xbf, 0x1c, - 0x7f, 0x81, 0x56, 0x14, 0x77, 0xf4, 0xd4, 0x81, 0x4d, 0x29, 0x3b, 0xfa, 0xac, 0xc0, 0x3a, 0x4d, - 0x5e, 0xeb, 0x82, 0x35, 0xec, 0xc0, 0xc6, 0x52, 0xaa, 0x81, 0x5a, 0x0b, 0x96, 0xad, 0xca, 0xa8, - 0xb5, 0x60, 0x39, 0x02, 0x4c, 0xef, 0xca, 0x21, 0x81, 0xfc, 0x1f, 0xc2, 0xc5, 0xee, 0x72, 0x07, - 0xd9, 0xee, 0x03, 0x4b, 0xb6, 0xee, 0xa2, 0x7d, 0x77, 0x10, 0xae, 0x80, 0xe5, 0x6d, 0xc1, 0xf2, - 0x26, 0x59, 0xee, 0xcd, 0x32, 0xcd, 0x28, 0xec, 0x97, 0x93, 0x7f, 0x2e, 0xa1, 0x76, 0x02, 0x32, - 0xff, 0x00, 0x43, 0xbb, 0xd3, 0x8f, 0xa9, 0x62, 0x2b, 0xf3, 0x38, 0x89, 0x32, 0x04, 0x9e, 0xd4, - 0x5d, 0xd4, 0x80, 0x67, 0x2a, 0x39, 0x6a, 0xc0, 0xb3, 0x65, 0x9e, 0x9e, 0xc0, 0xdd, 0x24, 0xca, - 0xb0, 0x55, 0x48, 0xcb, 0x02, 0x6a, 0xad, 0x42, 0x8e, 0x80, 0xa1, 0xd6, 0x2a, 0xe4, 0x89, 0x1b, - 0x3d, 0x5b, 0x85, 0xb4, 0x54, 0xb1, 0xfe, 0xbd, 0xa7, 0xcf, 0x8a, 0xe8, 0x8b, 0x67, 0x45, 0xf4, - 0x9f, 0x67, 0x45, 0xf4, 0xd3, 0xe7, 0xc5, 0x73, 0x5f, 0x3c, 0x2f, 0x9e, 0xfb, 0xc7, 0xf3, 0xe2, - 0xb9, 0x07, 0xcb, 0xb6, 0xc3, 0x6b, 0x8d, 0xfd, 0x92, 0xe5, 0x1f, 0xc8, 0xce, 0x62, 0x4c, 0x46, - 0x53, 0xf6, 0xcb, 0x8f, 0x0e, 0x59, 0xb0, 0xff, 0x92, 0xf8, 0x76, 0xdf, 0xfc, 0x7f, 0x00, 0x00, - 0x00, 0xff, 0xff, 0xc1, 0x55, 0xea, 0xa0, 0x5c, 0x25, 0x00, 0x00, + // 1957 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xcf, 0x6f, 0xdc, 0xc6, + 0x15, 0xf6, 0x68, 0xad, 0x44, 0x1e, 0xf9, 0x47, 0x3c, 0x96, 0x1d, 0x95, 0xb1, 0xd7, 0x36, 0x55, + 0x5b, 0xaa, 0x5d, 0x2f, 0x63, 0x39, 0x56, 0x6a, 0x5b, 0x29, 0xba, 0x92, 0x63, 0x47, 0xad, 0x92, + 0x28, 0x0b, 0x15, 0x2a, 0x5c, 0x14, 0xc4, 0x88, 0x3b, 0xe1, 0x12, 0xa1, 0x48, 0x85, 0x9c, 0x8d, + 0x56, 0x16, 0x74, 0xf1, 0xa1, 0xe7, 0x02, 0x39, 0xf4, 0xd2, 0x6b, 0xd1, 0x1c, 0x7a, 0xe8, 0xa1, + 0x68, 0x0f, 0x05, 0x52, 0x04, 0x6d, 0x5d, 0x1f, 0x03, 0x14, 0x28, 0x8a, 0x16, 0x28, 0x0a, 0xbb, + 0x7f, 0x41, 0xff, 0x82, 0x82, 0xc3, 0xc7, 0xdd, 0xe1, 0xaf, 0xdd, 0xd1, 0x6a, 0x73, 0xc8, 0xc9, + 0x4b, 0xce, 0xbc, 0x37, 0xdf, 0xf7, 0xde, 0x9b, 0xe1, 0x9b, 0xcf, 0xc2, 0xe7, 0xac, 0xc0, 0x0f, + 0x43, 0xab, 0x45, 0x1d, 0xcf, 0xf8, 0xb8, 0xcd, 0x82, 0xdd, 0xda, 0x76, 0xe0, 0x73, 0x9f, 0x5c, + 0x78, 0xcc, 0x38, 0x15, 0xaf, 0x6b, 0xe2, 0x97, 0x1f, 0xb0, 0x5a, 0x6f, 0xaa, 0x76, 0xcd, 0xf2, + 0xc3, 0x2d, 0x3f, 0x34, 0x36, 0x69, 0xc8, 0x62, 0x3b, 0xe3, 0x93, 0x9b, 0x9b, 0x8c, 0xd3, 0x9b, + 0xc6, 0x36, 0xb5, 0x1d, 0x8f, 0x72, 0xc7, 0xf7, 0x62, 0x57, 0xda, 0x45, 0x69, 0x09, 0xf1, 0xd3, + 0x14, 0xbf, 0x4d, 0xde, 0x81, 0x09, 0x9a, 0x34, 0xc1, 0xa6, 0xa1, 0xb9, 0x1d, 0x38, 0x16, 0x83, + 0xb1, 0x19, 0x69, 0x4c, 0xd8, 0x98, 0x2d, 0x1a, 0xb6, 0x4c, 0xee, 0x9b, 0x96, 0xd5, 0x75, 0x50, + 0xcd, 0x4d, 0xe2, 0x01, 0xb5, 0x3e, 0x62, 0x01, 0x8c, 0xeb, 0xd2, 0xb8, 0x4b, 0x43, 0x6e, 0x6e, + 0xba, 0xbe, 0xf5, 0x91, 0xd9, 0x62, 0x8e, 0xdd, 0xe2, 0x05, 0x28, 0xfd, 0x36, 0xcf, 0x3b, 0x91, + 0x91, 0x04, 0x94, 0x33, 0xd3, 0x75, 0xb6, 0x1c, 0xce, 0x02, 0xf3, 0x43, 0x97, 0xda, 0x21, 0x4c, + 0x9a, 0xb2, 0x7d, 0xdb, 0x17, 0x3f, 0x8d, 0xe8, 0x17, 0xbc, 0x3d, 0x6f, 0xfb, 0xbe, 0xed, 0x32, + 0x83, 0x6e, 0x3b, 0x06, 0xf5, 0x3c, 0x9f, 0x8b, 0xf0, 0x80, 0x8d, 0x7e, 0x1e, 0x6b, 0x1f, 0x44, + 0x11, 0x7c, 0xc4, 0x38, 0xad, 0x5b, 0x96, 0xdf, 0xf6, 0xb8, 0xe3, 0xd9, 0x0d, 0xf6, 0x71, 0x9b, + 0x85, 0x5c, 0x7f, 0x17, 0xbf, 0x56, 0x38, 0x1a, 0x6e, 0xfb, 0x5e, 0xc8, 0x48, 0x0d, 0x9f, 0xa1, + 0x9b, 0x7e, 0xc0, 0x59, 0xd3, 0x8c, 0xf2, 0x64, 0xd2, 0xad, 0x68, 0xc6, 0x34, 0xba, 0x84, 0xe6, + 0x8e, 0x35, 0x4e, 0xc3, 0x90, 0xb0, 0x15, 0x03, 0x5d, 0x77, 0x0f, 0x19, 0x7f, 0xbf, 0xcd, 0xd7, + 0x3b, 0xeb, 0x31, 0x47, 0x58, 0x8d, 0x4c, 0xe3, 0x97, 0x05, 0xc3, 0x95, 0xfb, 0xc2, 0x45, 0xa5, + 0x91, 0x3c, 0x92, 0x29, 0x3c, 0xee, 0xf9, 0x9e, 0xc5, 0xa6, 0xc7, 0x2e, 0xa1, 0xb9, 0xa3, 0x8d, + 0xf8, 0x41, 0x6f, 0xe3, 0xf3, 0xc5, 0xee, 0x00, 0xde, 0x0f, 0xf1, 0x71, 0x5f, 0x7a, 0x2f, 0x9c, + 0x4e, 0xce, 0x5f, 0xaf, 0xf5, 0xad, 0xae, 0x9a, 0xec, 0x6a, 0xe9, 0xe8, 0xb3, 0x7f, 0x5f, 0x3c, + 0xd2, 0x48, 0xb9, 0xd1, 0x19, 0xb0, 0xa8, 0xbb, 0x6e, 0x11, 0x8b, 0x07, 0x18, 0xf7, 0xaa, 0x10, + 0xd6, 0xbc, 0x5a, 0x8b, 0x4b, 0xb6, 0x16, 0x95, 0x6c, 0x2d, 0x2e, 0x75, 0x28, 0xd9, 0xda, 0x1a, + 0xb5, 0x19, 0xd8, 0x36, 0x24, 0x4b, 0xfd, 0x73, 0x04, 0xf4, 0x72, 0xeb, 0x94, 0xd2, 0xab, 0x8c, + 0x80, 0x1e, 0x79, 0x98, 0xc2, 0x3f, 0x26, 0xf0, 0xcf, 0x0e, 0xc4, 0x1f, 0x63, 0x4a, 0x11, 0x78, + 0x82, 0xb0, 0x5e, 0x44, 0x60, 0x69, 0x77, 0x39, 0x42, 0x92, 0xc4, 0x6b, 0x0a, 0x8f, 0x0b, 0x64, + 0x90, 0xf3, 0xf8, 0x21, 0x13, 0xc5, 0xb1, 0xa1, 0xa3, 0xf8, 0x17, 0x84, 0x67, 0xfa, 0x82, 0xf8, + 0x9a, 0x04, 0xf3, 0xa7, 0x08, 0x5f, 0x4e, 0x78, 0xac, 0x78, 0x65, 0xb1, 0xfc, 0x06, 0x9e, 0x88, + 0x8f, 0x37, 0xa7, 0x99, 0xde, 0x42, 0xcd, 0x91, 0x05, 0xf4, 0x0b, 0x29, 0xab, 0x45, 0x40, 0x20, + 0x9e, 0x0d, 0x3c, 0xe9, 0x78, 0xd9, 0x70, 0x5e, 0x1b, 0x10, 0x4e, 0xd9, 0x5f, 0x1c, 0x4d, 0xd9, + 0xc9, 0xe8, 0x82, 0x29, 0xed, 0x60, 0x69, 0xc9, 0x70, 0xd4, 0x3b, 0xf8, 0x0f, 0xd2, 0x0e, 0x4e, + 0xaf, 0xf3, 0x75, 0x08, 0xd2, 0x3d, 0x7c, 0x21, 0x39, 0x5d, 0xa3, 0x25, 0xdf, 0xa1, 0x61, 0x6b, + 0xdd, 0x5f, 0xb6, 0x78, 0x27, 0x09, 0x93, 0x86, 0x27, 0x1c, 0x18, 0x80, 0x23, 0xbf, 0xfb, 0xac, + 0xef, 0xe3, 0x6a, 0x99, 0x31, 0x70, 0xff, 0x31, 0x3e, 0xe9, 0xa4, 0x46, 0x20, 0xd0, 0x37, 0x14, + 0xe8, 0xf7, 0x8c, 0x20, 0x02, 0x19, 0x57, 0xfa, 0x22, 0x2c, 0x9f, 0x9e, 0x7c, 0x9f, 0x72, 0xaa, + 0x02, 0xfe, 0x31, 0xbe, 0x58, 0x6a, 0x0d, 0xe8, 0x37, 0xf0, 0x89, 0xe5, 0x08, 0x93, 0x28, 0xfa, + 0xf5, 0x4e, 0xa8, 0x78, 0x5e, 0xc8, 0x36, 0x00, 0x3d, 0xed, 0x47, 0xb7, 0x21, 0xea, 0x50, 0x32, + 0xf9, 0xa8, 0x8f, 0xaa, 0x38, 0x9f, 0x22, 0x88, 0x51, 0xc1, 0x4a, 0x7d, 0x52, 0x54, 0x19, 0x51, + 0x8a, 0x46, 0x57, 0xa7, 0x06, 0x7e, 0x35, 0x29, 0xb5, 0x87, 0x34, 0x5c, 0x8b, 0xda, 0x37, 0xe9, + 0xd3, 0xe2, 0x78, 0x4d, 0xd6, 0x81, 0x0c, 0xc7, 0x0f, 0xba, 0x89, 0xa7, 0xf3, 0x06, 0x40, 0x79, + 0x19, 0x4f, 0x24, 0xef, 0x20, 0xb6, 0xb3, 0x03, 0xc8, 0x76, 0x5d, 0x74, 0x0d, 0x75, 0x0a, 0x88, + 0xea, 0xae, 0x9b, 0x45, 0x34, 0xaa, 0xec, 0x7d, 0x86, 0x80, 0x44, 0x6a, 0x8d, 0x42, 0x12, 0x95, + 0xa1, 0x48, 0x8c, 0x2e, 0x3f, 0x0b, 0xbd, 0xa3, 0x60, 0x95, 0x86, 0x7c, 0x29, 0xea, 0x7e, 0xdf, + 0x11, 0xcd, 0x6f, 0xff, 0x34, 0xed, 0xc1, 0x2e, 0x2c, 0xb2, 0x03, 0xa2, 0x3f, 0xc2, 0xa7, 0x32, + 0x43, 0x10, 0xd2, 0xda, 0x00, 0xbe, 0x59, 0x87, 0x59, 0x37, 0x7a, 0xab, 0xb7, 0x39, 0x4a, 0x40, + 0x8f, 0x2a, 0x93, 0x7f, 0x46, 0xc0, 0xb3, 0x68, 0xa9, 0x7e, 0x3c, 0x2b, 0x23, 0xe0, 0x39, 0xba, + 0x2c, 0x5f, 0xc7, 0x67, 0x92, 0x6c, 0xc9, 0xa7, 0x55, 0x71, 0x6a, 0x57, 0xe1, 0xd2, 0x01, 0x93, + 0x97, 0x76, 0xdf, 0x8b, 0xfa, 0xf9, 0x61, 0xaf, 0x01, 0x36, 0x9e, 0x4a, 0x2f, 0x0d, 0x51, 0x7b, + 0x1f, 0x1f, 0x97, 0xcf, 0x56, 0xc5, 0xf6, 0x5f, 0x36, 0x69, 0xa4, 0x1c, 0xe8, 0x3f, 0x01, 0x8e, + 0x75, 0xd7, 0xfd, 0x2a, 0x4e, 0xe4, 0xdf, 0x20, 0x20, 0xd2, 0xf5, 0x5f, 0x4a, 0xa4, 0x72, 0x28, + 0x22, 0xa3, 0xcb, 0xfa, 0x7b, 0xd0, 0x48, 0xad, 0x3a, 0x21, 0x5f, 0x63, 0x5e, 0xd3, 0xf1, 0x6c, + 0x39, 0x32, 0x7d, 0xda, 0xd1, 0x29, 0x3c, 0x2e, 0xae, 0xb0, 0x62, 0xf5, 0x13, 0x8d, 0xf8, 0x41, + 0xff, 0x34, 0xe9, 0x98, 0x72, 0x0e, 0xbf, 0xaa, 0x50, 0xe8, 0xf8, 0x38, 0xf7, 0x39, 0x75, 0x61, + 0x31, 0xa8, 0xac, 0xd4, 0x3b, 0x7d, 0x09, 0x5f, 0x2b, 0x02, 0xb5, 0xe1, 0xf0, 0x96, 0xe3, 0x35, + 0x28, 0x67, 0xab, 0x11, 0x78, 0xa9, 0xe4, 0x63, 0x66, 0x48, 0x66, 0xf6, 0xc5, 0x18, 0xbe, 0xae, + 0xe4, 0x04, 0x88, 0x7e, 0x80, 0x4f, 0xa6, 0xe5, 0x8a, 0xa1, 0xa8, 0x5a, 0x32, 0xd5, 0x19, 0x7c, + 0x42, 0xd0, 0x32, 0xb7, 0xcb, 0xb9, 0x92, 0x05, 0xfc, 0xaa, 0xd5, 0x0e, 0x02, 0xe6, 0x71, 0x73, + 0xc7, 0xe1, 0xad, 0x66, 0x40, 0x77, 0xcc, 0x1d, 0xc7, 0x6b, 0xfa, 0x3b, 0xd3, 0x15, 0x91, 0xc1, + 0xb3, 0x30, 0xbc, 0x01, 0xa3, 0x1b, 0x62, 0x90, 0xcc, 0xe3, 0xb3, 0x39, 0xbb, 0x80, 0x72, 0x36, + 0x7d, 0x54, 0x6c, 0xfc, 0x33, 0x19, 0xab, 0x88, 0x30, 0xa9, 0xe1, 0x33, 0x3d, 0x2d, 0xc3, 0x64, + 0x1d, 0x8b, 0xb1, 0x26, 0x6b, 0x4e, 0x8f, 0x5f, 0x42, 0x73, 0x13, 0x8d, 0xd3, 0x41, 0x12, 0x93, + 0xb7, 0x61, 0xa0, 0xab, 0x55, 0x44, 0x87, 0xd8, 0x23, 0xc6, 0x69, 0xea, 0x40, 0xd6, 0x6f, 0x27, + 0xb5, 0x98, 0x19, 0x85, 0x80, 0x9e, 0xc3, 0x2f, 0x49, 0x9f, 0x88, 0x4a, 0x03, 0x9e, 0xf4, 0x75, + 0xa8, 0xb8, 0x65, 0xdf, 0xfb, 0x84, 0x05, 0x51, 0x47, 0xb0, 0xee, 0x47, 0xe6, 0xb9, 0xd3, 0x28, + 0x57, 0xc2, 0x1a, 0x9e, 0xb0, 0x69, 0xb8, 0xda, 0xad, 0xe2, 0x63, 0x8d, 0xee, 0xb3, 0xfe, 0x4b, + 0x04, 0x7d, 0x5c, 0xde, 0x2d, 0xe0, 0xf9, 0x36, 0x3e, 0xed, 0xb7, 0xf9, 0xa6, 0xdf, 0xf6, 0x9a, + 0x0f, 0x69, 0xb8, 0xe2, 0x45, 0x83, 0x89, 0x72, 0x92, 0x1b, 0x88, 0x66, 0x0b, 0xbd, 0xc6, 0xf2, + 0xdd, 0x07, 0x8c, 0xc1, 0xec, 0x78, 0xd1, 0xfc, 0x00, 0x99, 0xc3, 0xa7, 0xa2, 0x7f, 0xe5, 0xef, + 0x45, 0x45, 0xe4, 0x3a, 0xfb, 0x5a, 0x9f, 0xc5, 0x57, 0x04, 0xcc, 0x77, 0x59, 0x18, 0x52, 0x9b, + 0xad, 0xd1, 0x30, 0x74, 0x3c, 0x7b, 0xad, 0xe7, 0x31, 0x89, 0xee, 0x03, 0x7c, 0x75, 0xd0, 0x44, + 0x20, 0x76, 0x1e, 0x1f, 0xfb, 0xb0, 0x0b, 0x31, 0x26, 0xd4, 0x7b, 0xa1, 0x57, 0x21, 0xdc, 0xdd, + 0x8a, 0x67, 0xc1, 0x03, 0x97, 0xda, 0xc9, 0xdd, 0x4b, 0x7f, 0x92, 0x04, 0x2e, 0x3f, 0x01, 0xfc, + 0x53, 0xfc, 0x4a, 0x90, 0x19, 0x83, 0x43, 0xd7, 0x18, 0xb0, 0x37, 0xb2, 0x2e, 0xa1, 0x33, 0xcd, + 0xb9, 0x9b, 0xff, 0xec, 0x32, 0x1e, 0x17, 0x20, 0xc8, 0x53, 0x84, 0x8f, 0xcb, 0x97, 0x7c, 0x72, + 0x77, 0xc0, 0x1a, 0x7d, 0xf4, 0x2d, 0xed, 0xde, 0x50, 0xb6, 0x31, 0x6d, 0xfd, 0xad, 0x27, 0x7f, + 0xfb, 0xef, 0xa7, 0x63, 0x6f, 0x92, 0xdb, 0x46, 0x64, 0x7a, 0x43, 0x52, 0x34, 0xbb, 0xb2, 0x61, + 0xd7, 0xc8, 0xd8, 0x83, 0x2f, 0xe6, 0xbe, 0xb1, 0x27, 0xbe, 0x91, 0xfb, 0xe4, 0xf7, 0x08, 0x9f, + 0x92, 0xfd, 0xd6, 0x5d, 0x57, 0x8d, 0x4b, 0xb1, 0xca, 0xa5, 0xc6, 0xa5, 0x44, 0xb9, 0xd2, 0xaf, + 0x0b, 0x2e, 0x57, 0xc8, 0x8c, 0x02, 0x17, 0xf2, 0x2f, 0x84, 0xcf, 0x65, 0x90, 0x83, 0xd8, 0x40, + 0xea, 0x43, 0x80, 0x48, 0x2b, 0x26, 0xda, 0xd2, 0x61, 0x5c, 0x00, 0x9d, 0xbb, 0x82, 0xce, 0x1b, + 0x64, 0x5e, 0x81, 0x0e, 0xd8, 0x42, 0x86, 0xf6, 0xc9, 0x3f, 0x11, 0x3e, 0x2b, 0xdd, 0xe8, 0x25, + 0x72, 0xdf, 0x53, 0x44, 0x56, 0xaa, 0x06, 0x69, 0xf5, 0x43, 0x78, 0x00, 0x6a, 0x8b, 0x82, 0xda, + 0x02, 0x79, 0xa3, 0x84, 0x9a, 0xe3, 0x95, 0x30, 0x33, 0x9d, 0xe6, 0x3e, 0xf9, 0x1d, 0xc2, 0x27, + 0xd3, 0xe4, 0x94, 0x6b, 0xae, 0x40, 0x97, 0x51, 0xae, 0xb9, 0x22, 0xad, 0x65, 0x60, 0xcd, 0x49, + 0x4c, 0x42, 0xf2, 0x57, 0x00, 0x2e, 0xdd, 0x57, 0x17, 0x15, 0x37, 0x6f, 0xe1, 0xad, 0x5d, 0x7b, + 0x6b, 0x48, 0x6b, 0x00, 0xff, 0x1d, 0x01, 0x7e, 0x9e, 0xbc, 0xde, 0x07, 0x7c, 0xcf, 0xcc, 0xd8, + 0x4b, 0x9e, 0xf7, 0xc9, 0xdf, 0x11, 0x26, 0x79, 0x1d, 0x83, 0x28, 0xe1, 0x29, 0x55, 0x4f, 0xb4, + 0xef, 0x0e, 0x6b, 0x0e, 0x7c, 0xea, 0x82, 0xcf, 0x3d, 0x72, 0xa7, 0x94, 0x4f, 0xf6, 0x3f, 0x5b, + 0xcc, 0x26, 0xe5, 0x54, 0x26, 0xf6, 0x47, 0x84, 0x4f, 0xa7, 0x57, 0x88, 0xca, 0x6b, 0xf1, 0x00, + 0x25, 0x32, 0x64, 0x96, 0x4a, 0xf5, 0x12, 0xfd, 0x86, 0x60, 0x35, 0x4b, 0xae, 0x28, 0x65, 0x89, + 0xfc, 0x1a, 0xf5, 0xee, 0xe9, 0x64, 0x41, 0xb1, 0x40, 0x32, 0x82, 0x82, 0xf6, 0xe6, 0x81, 0xed, + 0x00, 0xac, 0x21, 0xc0, 0x7e, 0x8b, 0xcc, 0x96, 0x80, 0xb5, 0xc1, 0x20, 0x8a, 0x79, 0x93, 0x75, + 0xf6, 0xc9, 0xaf, 0x10, 0x9e, 0x4c, 0xbc, 0x44, 0xa1, 0x5e, 0x50, 0x0c, 0xd6, 0x50, 0x88, 0x0b, + 0x64, 0x0d, 0x7d, 0x56, 0x20, 0xbe, 0x4c, 0x2e, 0x0e, 0x40, 0x4c, 0x3e, 0x47, 0xf8, 0x95, 0x6c, + 0xdf, 0x45, 0x94, 0x0e, 0x8f, 0x92, 0x26, 0x50, 0x5b, 0x1c, 0xce, 0x58, 0x31, 0xd4, 0x56, 0x16, + 0xeb, 0x53, 0x84, 0x27, 0xa5, 0xd6, 0x8a, 0xdc, 0x57, 0x59, 0x7e, 0x50, 0x0b, 0xa7, 0xbd, 0x7d, + 0x48, 0x2f, 0xc0, 0xe6, 0x9a, 0x60, 0xf3, 0x4d, 0xa2, 0x97, 0xb0, 0x91, 0xda, 0x51, 0xf2, 0x0c, + 0xe5, 0x94, 0x0b, 0xa2, 0x7a, 0x14, 0x16, 0xeb, 0x2e, 0x6a, 0x47, 0x4f, 0xb9, 0x66, 0xa4, 0x2f, + 0x08, 0xf8, 0xaf, 0x93, 0x5a, 0x09, 0x7c, 0x37, 0x6d, 0xd7, 0x2d, 0xff, 0x3f, 0x21, 0x4c, 0x32, + 0x3e, 0xa3, 0x5d, 0xa0, 0x7a, 0x64, 0x1c, 0x86, 0x4d, 0xb9, 0x32, 0xa4, 0xd7, 0x04, 0x9b, 0x39, + 0x72, 0x55, 0x8d, 0x0d, 0xf9, 0x05, 0xc2, 0x47, 0xc5, 0xe1, 0x33, 0xaf, 0x18, 0x46, 0xf9, 0x78, + 0xbc, 0x75, 0x20, 0x1b, 0xc5, 0xef, 0xae, 0x05, 0x1f, 0x2c, 0x11, 0xe4, 0xdf, 0x22, 0x3c, 0x29, + 0x29, 0x42, 0xe4, 0xce, 0x01, 0x56, 0x4c, 0xab, 0x48, 0xc3, 0x81, 0xbd, 0x2d, 0xc0, 0x1a, 0xe4, + 0x46, 0x5f, 0xb0, 0xb9, 0xe6, 0xfa, 0xe7, 0x08, 0xbf, 0x9c, 0x7c, 0x81, 0xe6, 0x15, 0x33, 0x7a, + 0xe0, 0xc0, 0x66, 0x54, 0x21, 0x7d, 0x46, 0x60, 0xbd, 0x40, 0x5e, 0xeb, 0x83, 0x35, 0xea, 0xc0, + 0x4e, 0x65, 0x14, 0x07, 0xb5, 0x16, 0xac, 0x58, 0xd1, 0x51, 0x6b, 0xc1, 0x4a, 0xc4, 0x9b, 0xc1, + 0x27, 0x87, 0x04, 0xf2, 0x7f, 0x08, 0x57, 0xfb, 0x4b, 0x25, 0x64, 0x65, 0x08, 0x2c, 0xc5, 0x9a, + 0x8d, 0xf6, 0xfd, 0x51, 0xb8, 0x02, 0x96, 0x77, 0x04, 0xcb, 0x5b, 0xe4, 0xe6, 0x60, 0x96, 0x59, + 0x46, 0x51, 0xbf, 0x9c, 0xfe, 0x53, 0x0b, 0xb5, 0x1d, 0x50, 0xf8, 0xc7, 0x1b, 0xda, 0xdd, 0x61, + 0x4c, 0x15, 0x5b, 0x99, 0xc7, 0x69, 0x94, 0x11, 0xf0, 0xb4, 0xee, 0xa2, 0x06, 0xbc, 0x50, 0xc9, + 0x51, 0x03, 0x5e, 0x2c, 0xf3, 0x0c, 0x04, 0xee, 0xa6, 0x51, 0x46, 0xad, 0x42, 0x56, 0x16, 0x50, + 0x6b, 0x15, 0x4a, 0x04, 0x0c, 0xb5, 0x56, 0xa1, 0x4c, 0xdc, 0x18, 0xd8, 0x2a, 0x64, 0xa5, 0x8a, + 0xa5, 0x1f, 0x3c, 0x7b, 0x5e, 0x45, 0x5f, 0x3e, 0xaf, 0xa2, 0xff, 0x3c, 0xaf, 0xa2, 0x9f, 0xbd, + 0xa8, 0x1e, 0xf9, 0xf2, 0x45, 0xf5, 0xc8, 0x3f, 0x5e, 0x54, 0x8f, 0x3c, 0xba, 0x69, 0x3b, 0xbc, + 0xd5, 0xde, 0xac, 0x59, 0xfe, 0x96, 0xec, 0x2c, 0xc1, 0x64, 0x74, 0x64, 0xbf, 0x7c, 0x77, 0x9b, + 0x85, 0x9b, 0x2f, 0x89, 0x6f, 0xf7, 0xad, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x13, 0xb9, 0xf7, + 0xad, 0x98, 0x25, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -4392,10 +4402,17 @@ func (m *QueryListPendingCctxWithinRateLimitResponse) MarshalToSizedBuffer(dAtA dAtA[i] = 0 } i-- - dAtA[i] = 0x20 + dAtA[i] = 0x28 } - if m.ValueWithinWindow != 0 { - i = encodeVarintQuery(dAtA, i, uint64(m.ValueWithinWindow)) + if len(m.CurrentWithdrawRate) > 0 { + i -= len(m.CurrentWithdrawRate) + copy(dAtA[i:], m.CurrentWithdrawRate) + i = encodeVarintQuery(dAtA, i, uint64(len(m.CurrentWithdrawRate))) + i-- + dAtA[i] = 0x22 + } + if m.CurrentWithdrawWindow != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.CurrentWithdrawWindow)) i-- dAtA[i] = 0x18 } @@ -5184,8 +5201,12 @@ func (m *QueryListPendingCctxWithinRateLimitResponse) Size() (n int) { if m.TotalPending != 0 { n += 1 + sovQuery(uint64(m.TotalPending)) } - if m.ValueWithinWindow != 0 { - n += 1 + sovQuery(uint64(m.ValueWithinWindow)) + if m.CurrentWithdrawWindow != 0 { + n += 1 + sovQuery(uint64(m.CurrentWithdrawWindow)) + } + l = len(m.CurrentWithdrawRate) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) } if m.RateLimitExceeded { n += 2 @@ -8554,9 +8575,9 @@ func (m *QueryListPendingCctxWithinRateLimitResponse) Unmarshal(dAtA []byte) err } case 3: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ValueWithinWindow", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field CurrentWithdrawWindow", wireType) } - m.ValueWithinWindow = 0 + m.CurrentWithdrawWindow = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowQuery @@ -8566,12 +8587,44 @@ func (m *QueryListPendingCctxWithinRateLimitResponse) Unmarshal(dAtA []byte) err } b := dAtA[iNdEx] iNdEx++ - m.ValueWithinWindow |= uint64(b&0x7F) << shift + m.CurrentWithdrawWindow |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentWithdrawRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CurrentWithdrawRate = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RateLimitExceeded", wireType) } diff --git a/zetaclient/interfaces/interfaces.go b/zetaclient/interfaces/interfaces.go index a122a19131..d40ac4689b 100644 --- a/zetaclient/interfaces/interfaces.go +++ b/zetaclient/interfaces/interfaces.go @@ -98,7 +98,7 @@ type ZetaCoreBridger interface { GetZetaBlockHeight() (int64, error) GetLastBlockHeightByChain(chain chains.Chain) (*crosschaintypes.LastBlockHeight, error) ListPendingCctx(chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) - ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, uint64, bool, error) + ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, int64, string, bool, error) GetPendingNoncesByChain(chainID int64) (observertypes.PendingNonces, error) GetCctxByNonce(chainID int64, nonce uint64) (*crosschaintypes.CrossChainTx, error) GetOutTxTracker(chain chains.Chain, nonce uint64) (*crosschaintypes.OutTxTracker, error) diff --git a/zetaclient/testutils/stub/core_bridge.go b/zetaclient/testutils/stub/core_bridge.go index ca3f45a16b..ec72eae7d8 100644 --- a/zetaclient/testutils/stub/core_bridge.go +++ b/zetaclient/testutils/stub/core_bridge.go @@ -121,11 +121,11 @@ func (z *MockZetaCoreBridge) ListPendingCctx(_ int64) ([]*cctxtypes.CrossChainTx return []*cctxtypes.CrossChainTx{}, 0, nil } -func (z *MockZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*cctxtypes.CrossChainTx, uint64, uint64, bool, error) { +func (z *MockZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*cctxtypes.CrossChainTx, uint64, int64, string, bool, error) { if z.paused { - return nil, 0, 0, false, errors.New(ErrMsgPaused) + return nil, 0, 0, "", false, errors.New(ErrMsgPaused) } - return []*cctxtypes.CrossChainTx{}, 0, 0, false, nil + return []*cctxtypes.CrossChainTx{}, 0, 0, "", false, nil } func (z *MockZetaCoreBridge) GetPendingNoncesByChain(_ int64) (observerTypes.PendingNonces, error) { diff --git a/zetaclient/zetabridge/query.go b/zetaclient/zetabridge/query.go index afed840fef..9c313f364a 100644 --- a/zetaclient/zetabridge/query.go +++ b/zetaclient/zetabridge/query.go @@ -139,7 +139,7 @@ func (b *ZetaCoreBridge) ListPendingCctx(chainID int64) ([]*types.CrossChainTx, // ListPendingCctxWithinRatelimit returns a list of pending cctxs that do not exceed the outbound rate limit // - The max size of the list is crosschainkeeper.MaxPendingCctxs // - The returned `rateLimitExceeded` flag indicates if the rate limit is exceeded or not -func (b *ZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*types.CrossChainTx, uint64, uint64, bool, error) { +func (b *ZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*types.CrossChainTx, uint64, int64, string, bool, error) { client := types.NewQueryClient(b.grpcConn) maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) resp, err := client.ListPendingCctxWithinRateLimit( @@ -148,9 +148,9 @@ func (b *ZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*types.CrossChainTx maxSizeOption, ) if err != nil { - return nil, 0, 0, false, err + return nil, 0, 0, "", false, err } - return resp.CrossChainTx, resp.TotalPending, resp.ValueWithinWindow, resp.RateLimitExceeded, nil + return resp.CrossChainTx, resp.TotalPending, resp.CurrentWithdrawWindow, resp.CurrentWithdrawRate, resp.RateLimitExceeded, nil } func (b *ZetaCoreBridge) GetAbortedZetaAmount() (string, error) { diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 230e1ba894..535f741db1 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -134,13 +134,14 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { metrics.HotKeyBurnRate.Set(float64(co.ts.HotKeyBurnRate.GetBurnRate().Int64())) // query pending cctxs across all foreign chains with rate limit - cctxMap, valueWithinWindow, err := co.getAllPendingCctxWithRatelimit() + cctxMap, withdrawWindow, withdrawRate, err := co.getAllPendingCctxWithRatelimit() if err != nil { co.logger.ZetaChainWatcher.Error().Err(err).Msgf("startCctxScheduler: queryPendingCctxWithRatelimit failed") } // print value within rate limiter window every minute if bn%10 == 0 { - co.logger.ZetaChainWatcher.Debug().Msgf("startCctxScheduler: value within rate limiter window is %d ZETA", valueWithinWindow) + co.logger.ZetaChainWatcher.Debug().Msgf( + "startCctxScheduler: withdraw window is %d, withdraw rate is %s", withdrawWindow, withdrawRate) } // schedule keysign for pending cctxs on each chain @@ -194,10 +195,10 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { } // getAllPendingCctxWithRatelimit get pending cctxs across all foreign chains with rate limit -func (co *CoreObserver) getAllPendingCctxWithRatelimit() (map[int64][]*types.CrossChainTx, uint64, error) { - cctxList, totalPending, valueWithinWindow, rateLimitExceeded, err := co.bridge.ListPendingCctxWithinRatelimit() +func (co *CoreObserver) getAllPendingCctxWithRatelimit() (map[int64][]*types.CrossChainTx, int64, string, error) { + cctxList, totalPending, withdrawWindow, withdrawRate, rateLimitExceeded, err := co.bridge.ListPendingCctxWithinRatelimit() if err != nil { - return nil, 0, err + return nil, 0, "", err } if rateLimitExceeded { co.logger.ZetaChainWatcher.Warn().Msgf("rate limit exceeded, fetched %d cctxs out of %d", len(cctxList), totalPending) @@ -213,7 +214,7 @@ func (co *CoreObserver) getAllPendingCctxWithRatelimit() (map[int64][]*types.Cro cctxMap[chainID] = append(cctxMap[chainID], cctx) } - return cctxMap, valueWithinWindow, nil + return cctxMap, withdrawWindow, withdrawRate, nil } // scheduleCctxEVM schedules evm outtx keysign on each ZetaChain block (the ticker) From daba26989032619c1b8a8ee064cd88d477e81d70 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 25 Apr 2024 11:29:23 -0500 Subject: [PATCH 4/4] add commented unit tests back --- .../keeper/grpc_query_cctx_rate_limit_test.go | 374 +++++++++--------- 1 file changed, 187 insertions(+), 187 deletions(-) diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go index 2aa96ca51b..71a31ce2f7 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -310,193 +310,193 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { expectedWithdrawRate string rateLimitExceeded bool }{ - // { - // name: "should use fallback query if rate limiter is disabled", - // fallback: true, - // rateLimitFlags: nil, // no rate limiter flags set in the keeper - // ethMinedCctxs: ethMinedCctxs, - // ethPendingCctxs: ethPendingCctxs, - // ethPendingNonces: observertypes.PendingNonces{ - // ChainId: ethChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // btcMinedCctxs: btcMinedCctxs, - // btcPendingCctxs: btcPendingCctxs, - // btcPendingNonces: observertypes.PendingNonces{ - // ChainId: btcChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // currentHeight: 1199, - // queryLimit: keeper.MaxPendingCctxs, - // expectedCctxs: append(append([]*types.CrossChainTx{}, btcPendingCctxs...), ethPendingCctxs...), - // expectedTotalPending: 400, - // }, - // { - // name: "should use fallback query if rate is 0", - // fallback: true, - // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(0), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - // ethMinedCctxs: ethMinedCctxs, - // ethPendingCctxs: ethPendingCctxs, - // ethPendingNonces: observertypes.PendingNonces{ - // ChainId: ethChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // btcMinedCctxs: btcMinedCctxs, - // btcPendingCctxs: btcPendingCctxs, - // btcPendingNonces: observertypes.PendingNonces{ - // ChainId: btcChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // currentHeight: 1199, - // queryLimit: keeper.MaxPendingCctxs, - // expectedCctxs: append(append([]*types.CrossChainTx{}, btcPendingCctxs...), ethPendingCctxs...), - // expectedTotalPending: 400, - // }, - // { - // name: "can retrieve all pending cctx without exceeding rate limit", - // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(10*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - // ethMinedCctxs: ethMinedCctxs, - // ethPendingCctxs: ethPendingCctxs, - // ethPendingNonces: observertypes.PendingNonces{ - // ChainId: ethChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // btcMinedCctxs: btcMinedCctxs, - // btcPendingCctxs: btcPendingCctxs, - // btcPendingNonces: observertypes.PendingNonces{ - // ChainId: btcChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // currentHeight: 1199, - // queryLimit: keeper.MaxPendingCctxs, - // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...), - // expectedTotalPending: 400, - // expectedWithdrawWindow: 500, // the sliding window - // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block - // rateLimitExceeded: false, - // }, - // { - // name: "can loop backwards all the way to endNonce 0", - // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(10*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - // ethMinedCctxs: ethMinedCctxs, - // ethPendingCctxs: ethPendingCctxs, - // ethPendingNonces: observertypes.PendingNonces{ - // ChainId: ethChainID, - // NonceLow: 999, // endNonce will be set to 0 (NonceLow - 1000 < 0) - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // btcMinedCctxs: btcMinedCctxs, - // btcPendingCctxs: btcPendingCctxs, - // btcPendingNonces: observertypes.PendingNonces{ - // ChainId: btcChainID, - // NonceLow: 999, // endNonce will be set to 0 (NonceLow - 1000 < 0) - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // currentHeight: 1199, - // queryLimit: keeper.MaxPendingCctxs, - // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...), - // expectedTotalPending: 400, - // expectedWithdrawWindow: 500, // the sliding window - // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block - // rateLimitExceeded: false, - // }, - // { - // name: "set a low rate (rate < 2.4 ZETA) to exceed rate limit in backward loop", - // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(2*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - // ethMinedCctxs: ethMinedCctxs, - // ethPendingCctxs: ethPendingCctxs, - // ethPendingNonces: observertypes.PendingNonces{ - // ChainId: ethChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // btcMinedCctxs: btcMinedCctxs, - // btcPendingCctxs: btcPendingCctxs, - // btcPendingNonces: observertypes.PendingNonces{ - // ChainId: btcChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // currentHeight: 1199, - // queryLimit: keeper.MaxPendingCctxs, - // // return missed cctxs only if rate limit is exceeded - // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...), - // expectedTotalPending: 400, - // expectedWithdrawWindow: 500, // the sliding window - // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block - // rateLimitExceeded: true, - // }, - // { - // name: "set a lower gRPC request limit and reach the limit of the query in forward loop", - // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(10*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - // ethMinedCctxs: ethMinedCctxs, - // ethPendingCctxs: ethPendingCctxs, - // ethPendingNonces: observertypes.PendingNonces{ - // ChainId: ethChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // btcMinedCctxs: btcMinedCctxs, - // btcPendingCctxs: btcPendingCctxs, - // btcPendingNonces: observertypes.PendingNonces{ - // ChainId: btcChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // currentHeight: 1199, - // queryLimit: 300, // 300 < keeper.MaxPendingCctxs - // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs...), - // expectedTotalPending: 400, - // expectedWithdrawWindow: 500, // the sliding window - // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block - // rateLimitExceeded: false, - // }, - // { - // name: "set a median rate (2.4 ZETA < rate < 3 ZETA) to exceed rate limit in forward loop", - // rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(26*1e17), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), - // ethMinedCctxs: ethMinedCctxs, - // ethPendingCctxs: ethPendingCctxs, - // ethPendingNonces: observertypes.PendingNonces{ - // ChainId: ethChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // btcMinedCctxs: btcMinedCctxs, - // btcPendingCctxs: btcPendingCctxs, - // btcPendingNonces: observertypes.PendingNonces{ - // ChainId: btcChainID, - // NonceLow: 1099, - // NonceHigh: 1199, - // Tss: tss.TssPubkey, - // }, - // currentHeight: 1199, - // queryLimit: keeper.MaxPendingCctxs, - // // return missed cctxs only if rate limit is exceeded - // expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...), - // expectedTotalPending: 400, - // expectedWithdrawWindow: 500, // the sliding window - // expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block - // rateLimitExceeded: true, - // }, + { + name: "should use fallback query if rate limiter is disabled", + fallback: true, + rateLimitFlags: nil, // no rate limiter flags set in the keeper + ethMinedCctxs: ethMinedCctxs, + ethPendingCctxs: ethPendingCctxs, + ethPendingNonces: observertypes.PendingNonces{ + ChainId: ethChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + btcMinedCctxs: btcMinedCctxs, + btcPendingCctxs: btcPendingCctxs, + btcPendingNonces: observertypes.PendingNonces{ + ChainId: btcChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + currentHeight: 1199, + queryLimit: keeper.MaxPendingCctxs, + expectedCctxs: append(append([]*types.CrossChainTx{}, btcPendingCctxs...), ethPendingCctxs...), + expectedTotalPending: 400, + }, + { + name: "should use fallback query if rate is 0", + fallback: true, + rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(0), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + ethMinedCctxs: ethMinedCctxs, + ethPendingCctxs: ethPendingCctxs, + ethPendingNonces: observertypes.PendingNonces{ + ChainId: ethChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + btcMinedCctxs: btcMinedCctxs, + btcPendingCctxs: btcPendingCctxs, + btcPendingNonces: observertypes.PendingNonces{ + ChainId: btcChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + currentHeight: 1199, + queryLimit: keeper.MaxPendingCctxs, + expectedCctxs: append(append([]*types.CrossChainTx{}, btcPendingCctxs...), ethPendingCctxs...), + expectedTotalPending: 400, + }, + { + name: "can retrieve all pending cctx without exceeding rate limit", + rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(10*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + ethMinedCctxs: ethMinedCctxs, + ethPendingCctxs: ethPendingCctxs, + ethPendingNonces: observertypes.PendingNonces{ + ChainId: ethChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + btcMinedCctxs: btcMinedCctxs, + btcPendingCctxs: btcPendingCctxs, + btcPendingNonces: observertypes.PendingNonces{ + ChainId: btcChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + currentHeight: 1199, + queryLimit: keeper.MaxPendingCctxs, + expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...), + expectedTotalPending: 400, + expectedWithdrawWindow: 500, // the sliding window + expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + rateLimitExceeded: false, + }, + { + name: "can loop backwards all the way to endNonce 0", + rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(10*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + ethMinedCctxs: ethMinedCctxs, + ethPendingCctxs: ethPendingCctxs, + ethPendingNonces: observertypes.PendingNonces{ + ChainId: ethChainID, + NonceLow: 999, // endNonce will be set to 0 (NonceLow - 1000 < 0) + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + btcMinedCctxs: btcMinedCctxs, + btcPendingCctxs: btcPendingCctxs, + btcPendingNonces: observertypes.PendingNonces{ + ChainId: btcChainID, + NonceLow: 999, // endNonce will be set to 0 (NonceLow - 1000 < 0) + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + currentHeight: 1199, + queryLimit: keeper.MaxPendingCctxs, + expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...), + expectedTotalPending: 400, + expectedWithdrawWindow: 500, // the sliding window + expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + rateLimitExceeded: false, + }, + { + name: "set a low rate (rate < 2.4 ZETA) to exceed rate limit in backward loop", + rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(2*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + ethMinedCctxs: ethMinedCctxs, + ethPendingCctxs: ethPendingCctxs, + ethPendingNonces: observertypes.PendingNonces{ + ChainId: ethChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + btcMinedCctxs: btcMinedCctxs, + btcPendingCctxs: btcPendingCctxs, + btcPendingNonces: observertypes.PendingNonces{ + ChainId: btcChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + currentHeight: 1199, + queryLimit: keeper.MaxPendingCctxs, + // return missed cctxs only if rate limit is exceeded + expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...), + expectedTotalPending: 400, + expectedWithdrawWindow: 500, // the sliding window + expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + rateLimitExceeded: true, + }, + { + name: "set a lower gRPC request limit and reach the limit of the query in forward loop", + rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(10*1e18), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + ethMinedCctxs: ethMinedCctxs, + ethPendingCctxs: ethPendingCctxs, + ethPendingNonces: observertypes.PendingNonces{ + ChainId: ethChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + btcMinedCctxs: btcMinedCctxs, + btcPendingCctxs: btcPendingCctxs, + btcPendingNonces: observertypes.PendingNonces{ + ChainId: btcChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + currentHeight: 1199, + queryLimit: 300, // 300 < keeper.MaxPendingCctxs + expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs...), + expectedTotalPending: 400, + expectedWithdrawWindow: 500, // the sliding window + expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + rateLimitExceeded: false, + }, + { + name: "set a median rate (2.4 ZETA < rate < 3 ZETA) to exceed rate limit in forward loop", + rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(26*1e17), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), + ethMinedCctxs: ethMinedCctxs, + ethPendingCctxs: ethPendingCctxs, + ethPendingNonces: observertypes.PendingNonces{ + ChainId: ethChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + btcMinedCctxs: btcMinedCctxs, + btcPendingCctxs: btcPendingCctxs, + btcPendingNonces: observertypes.PendingNonces{ + ChainId: btcChainID, + NonceLow: 1099, + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + currentHeight: 1199, + queryLimit: keeper.MaxPendingCctxs, + // return missed cctxs only if rate limit is exceeded + expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...), + expectedTotalPending: 400, + expectedWithdrawWindow: 500, // the sliding window + expectedWithdrawRate: sdk.NewDec(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block + rateLimitExceeded: true, + }, { // the pending cctxs window is wider than the rate limiter sliding window in this test case. name: "set low rate and narrow window to early exceed rate limit in forward loop",