From 8db24cced7700042808c0b3402bbd0f068cc5ff3 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 17 Apr 2024 21:02:18 -0500 Subject: [PATCH 01/40] initial commit of grpc pending cctx query with rate limiter --- proto/crosschain/query.proto | 10 + proto/crosschain/rate_limiter_flags.proto | 26 + typescript/crosschain/index.d.ts | 1 + typescript/crosschain/query_pb.d.ts | 58 ++ .../crosschain/rate_limiter_flags_pb.d.ts | 91 +++ x/crosschain/keeper/foreign_coins.go | 4 +- x/crosschain/keeper/foreign_coins_test.go | 3 +- x/crosschain/keeper/grpc_query_cctx.go | 219 +++++- x/crosschain/keeper/rate_limiter_flags.go | 87 ++ x/crosschain/types/keys.go | 3 + x/crosschain/types/query.pb.go | 652 ++++++++++++--- x/crosschain/types/rate_limiter_flags.pb.go | 741 ++++++++++++++++++ 12 files changed, 1768 insertions(+), 127 deletions(-) create mode 100644 proto/crosschain/rate_limiter_flags.proto create mode 100644 typescript/crosschain/rate_limiter_flags_pb.d.ts create mode 100644 x/crosschain/keeper/rate_limiter_flags.go create mode 100644 x/crosschain/types/rate_limiter_flags.pb.go diff --git a/proto/crosschain/query.proto b/proto/crosschain/query.proto index 99ad6b4b72..5372c9b0c9 100644 --- a/proto/crosschain/query.proto +++ b/proto/crosschain/query.proto @@ -252,6 +252,16 @@ message QueryListCctxPendingResponse { uint64 totalPending = 2; } +message QueryListCctxPendingWithRateLimitRequest { + uint32 limit = 2; +} + +message QueryListCctxPendingWithRateLimitResponse { + repeated CrossChainTx cross_chain_tx = 1; + uint64 total_pending = 2; + bool rate_limit_exceeded = 3; +} + message QueryLastZetaHeightRequest {} message QueryLastZetaHeightResponse { diff --git a/proto/crosschain/rate_limiter_flags.proto b/proto/crosschain/rate_limiter_flags.proto new file mode 100644 index 0000000000..af9edabcbb --- /dev/null +++ b/proto/crosschain/rate_limiter_flags.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +package zetachain.zetacore.crosschain; + +import "gogoproto/gogo.proto"; +import "pkg/coin/coin.proto"; + +option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types"; + +// ZRC20Rate defines the conversion rate of ZRC20 to ZETA +message ZRC20Rate { + int64 chain_id = 1; + coin.CoinType coin_type = 2; + string asset = 3; + double conversion_rate = 4; +} + +// RateLimiterFlags defines the outbound rate limiter flags +message RateLimiterFlags { + bool is_enabled = 1; + int64 rate_limit_window = 2; + string rate_limit_in_zeta = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; + repeated ZRC20Rate zrc20_rates = 4; +} diff --git a/typescript/crosschain/index.d.ts b/typescript/crosschain/index.d.ts index e357b4b8b3..0f542908cb 100644 --- a/typescript/crosschain/index.d.ts +++ b/typescript/crosschain/index.d.ts @@ -7,4 +7,5 @@ export * from "./in_tx_tracker_pb"; export * from "./last_block_height_pb"; export * from "./out_tx_tracker_pb"; export * from "./query_pb"; +export * from "./rate_limiter_flags_pb"; export * from "./tx_pb"; diff --git a/typescript/crosschain/query_pb.d.ts b/typescript/crosschain/query_pb.d.ts index 370926427e..3f66935a9f 100644 --- a/typescript/crosschain/query_pb.d.ts +++ b/typescript/crosschain/query_pb.d.ts @@ -870,6 +870,64 @@ export declare class QueryListCctxPendingResponse extends Message | undefined, b: QueryListCctxPendingResponse | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitRequest + */ +export declare class QueryListCctxPendingWithRateLimitRequest extends Message { + /** + * @generated from field: uint32 limit = 2; + */ + limit: number; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitRequest"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryListCctxPendingWithRateLimitRequest; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryListCctxPendingWithRateLimitRequest; + + static fromJsonString(jsonString: string, options?: Partial): QueryListCctxPendingWithRateLimitRequest; + + static equals(a: QueryListCctxPendingWithRateLimitRequest | PlainMessage | undefined, b: QueryListCctxPendingWithRateLimitRequest | PlainMessage | undefined): boolean; +} + +/** + * @generated from message zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitResponse + */ +export declare class QueryListCctxPendingWithRateLimitResponse extends Message { + /** + * @generated from field: repeated zetachain.zetacore.crosschain.CrossChainTx cross_chain_tx = 1; + */ + crossChainTx: CrossChainTx[]; + + /** + * @generated from field: uint64 total_pending = 2; + */ + totalPending: bigint; + + /** + * @generated from field: bool rate_limit_exceeded = 3; + */ + rateLimitExceeded: boolean; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryListCctxPendingWithRateLimitResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryListCctxPendingWithRateLimitResponse; + + static fromJsonString(jsonString: string, options?: Partial): QueryListCctxPendingWithRateLimitResponse; + + static equals(a: QueryListCctxPendingWithRateLimitResponse | PlainMessage | undefined, b: QueryListCctxPendingWithRateLimitResponse | PlainMessage | undefined): boolean; +} + /** * @generated from message zetachain.zetacore.crosschain.QueryLastZetaHeightRequest */ diff --git a/typescript/crosschain/rate_limiter_flags_pb.d.ts b/typescript/crosschain/rate_limiter_flags_pb.d.ts new file mode 100644 index 0000000000..b95e353e94 --- /dev/null +++ b/typescript/crosschain/rate_limiter_flags_pb.d.ts @@ -0,0 +1,91 @@ +// @generated by protoc-gen-es v1.3.0 with parameter "target=dts" +// @generated from file crosschain/rate_limiter_flags.proto (package zetachain.zetacore.crosschain, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3 } from "@bufbuild/protobuf"; +import type { CoinType } from "../pkg/coin/coin_pb.js"; + +/** + * ZRC20Rate defines the conversion rate of ZRC20 to ZETA + * + * @generated from message zetachain.zetacore.crosschain.ZRC20Rate + */ +export declare class ZRC20Rate extends Message { + /** + * @generated from field: int64 chain_id = 1; + */ + chainId: bigint; + + /** + * @generated from field: coin.CoinType coin_type = 2; + */ + coinType: CoinType; + + /** + * @generated from field: string asset = 3; + */ + asset: string; + + /** + * @generated from field: double conversion_rate = 4; + */ + conversionRate: number; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.ZRC20Rate"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): ZRC20Rate; + + static fromJson(jsonValue: JsonValue, options?: Partial): ZRC20Rate; + + static fromJsonString(jsonString: string, options?: Partial): ZRC20Rate; + + static equals(a: ZRC20Rate | PlainMessage | undefined, b: ZRC20Rate | PlainMessage | undefined): boolean; +} + +/** + * RateLimiterFlags defines the outbound rate limiter flags + * + * @generated from message zetachain.zetacore.crosschain.RateLimiterFlags + */ +export declare class RateLimiterFlags extends Message { + /** + * @generated from field: bool is_enabled = 1; + */ + isEnabled: boolean; + + /** + * @generated from field: int64 rate_limit_window = 2; + */ + rateLimitWindow: bigint; + + /** + * @generated from field: string rate_limit_in_zeta = 3; + */ + rateLimitInZeta: string; + + /** + * @generated from field: repeated zetachain.zetacore.crosschain.ZRC20Rate zrc20_rates = 4; + */ + zrc20Rates: ZRC20Rate[]; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.RateLimiterFlags"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): RateLimiterFlags; + + static fromJson(jsonValue: JsonValue, options?: Partial): RateLimiterFlags; + + static fromJsonString(jsonString: string, options?: Partial): RateLimiterFlags; + + static equals(a: RateLimiterFlags | PlainMessage | undefined, b: RateLimiterFlags | PlainMessage | undefined): boolean; +} + diff --git a/x/crosschain/keeper/foreign_coins.go b/x/crosschain/keeper/foreign_coins.go index 1ceeb39bf7..15ecd54260 100644 --- a/x/crosschain/keeper/foreign_coins.go +++ b/x/crosschain/keeper/foreign_coins.go @@ -5,11 +5,11 @@ import ( fungibleModuleTypes "github.com/zeta-chain/zetacore/x/fungible/types" ) -func (k Keeper) GetAllForeignCoins(ctx sdk.Context) ([]fungibleModuleTypes.ForeignCoins, error) { +func (k Keeper) GetAllForeignCoins(ctx sdk.Context) []fungibleModuleTypes.ForeignCoins { chains := k.zetaObserverKeeper.GetSupportedChains(ctx) var fCoins []fungibleModuleTypes.ForeignCoins for _, chain := range chains { fCoins = append(fCoins, k.fungibleKeeper.GetAllForeignCoinsForChain(ctx, chain.ChainId)...) } - return fCoins, nil + return fCoins } diff --git a/x/crosschain/keeper/foreign_coins_test.go b/x/crosschain/keeper/foreign_coins_test.go index 5d145b08f1..aaa359cd72 100644 --- a/x/crosschain/keeper/foreign_coins_test.go +++ b/x/crosschain/keeper/foreign_coins_test.go @@ -14,7 +14,6 @@ func TestKeeper_GetAllForeignCoins(t *testing.T) { fc.ForeignChainId = 101 k.GetFungibleKeeper().SetForeignCoins(ctx, fc) - res, err := k.GetAllForeignCoins(ctx) - require.NoError(t, err) + res := k.GetAllForeignCoins(ctx) require.Equal(t, 1, len(res)) } diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index 74bdc49293..1fe7d04e24 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -3,10 +3,13 @@ package keeper import ( "context" "fmt" + "math/big" + "strings" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" + "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -91,15 +94,13 @@ func (k Keeper) CctxByNonce(c context.Context, req *types.QueryGetCctxByNonceReq } // CctxListPending returns a list of pending cctxs and the total number of pending cctxs -// a limit for the number of cctxs to return can be specified -// if no limit is specified, the default is MaxPendingCctxs +// a limit for the number of cctxs to return can be specified or the default is MaxPendingCctxs func (k Keeper) CctxListPending(c context.Context, req *types.QueryListCctxPendingRequest) (*types.QueryListCctxPendingResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } - // check limit - // if no limit specified, default to MaxPendingCctxs + // check limit and use default MaxPendingCctxs if not specified if req.Limit > MaxPendingCctxs { return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("limit exceeds max limit of %d", MaxPendingCctxs)) } @@ -144,11 +145,10 @@ func (k Keeper) CctxListPending(c context.Context, req *types.QueryListCctxPendi if !found { return nil, status.Error(codes.Internal, fmt.Sprintf("cctx not found: index %s", nonceToCctx.CctxIndex)) } - if cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound || cctx.CctxStatus.Status == types.CctxStatus_PendingRevert { - totalPending++ - // we check here if max cctxs is reached because we want to return the total pending cctxs - // even if we have reached the limit + // only take a `limit` number of pending cctxs as result but still count the total pending cctxs + if IsPending(cctx) { + totalPending++ if !maxCCTXsReached() { cctxs = append(cctxs, &cctx) } @@ -177,3 +177,206 @@ func (k Keeper) CctxListPending(c context.Context, req *types.QueryListCctxPendi TotalPending: totalPending, }, nil } + +// CctxListPendingWithinRateLimit returns a list of pending cctxs that do not exceed the outbound rate limit +// a limit for the number of cctxs to return can be specified or the default is MaxPendingCctxs +func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.QueryListCctxPendingWithRateLimitRequest) (*types.QueryListCctxPendingWithRateLimitResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + // check limit and use default MaxPendingCctxs if not specified + if req.Limit > MaxPendingCctxs { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("limit exceeds max limit of %d", MaxPendingCctxs)) + } + limit := req.Limit + if limit == 0 { + limit = MaxPendingCctxs + } + + // get current height and tss + ctx := sdk.UnwrapSDKContext(c) + height := ctx.BlockHeight() + if height <= 0 { + return nil, status.Error(codes.OutOfRange, "height out of range") + } + tss, found := k.zetaObserverKeeper.GetTSS(ctx) + if !found { + return nil, status.Error(codes.Internal, "tss not found") + } + + // check rate limit flags to decide if we should apply rate limit + applyLimit := true + rateLimitFlags, found := k.GetRatelimiterFlags(ctx) + if !found || !rateLimitFlags.IsEnabled { + applyLimit = false + } + + // calculate the rate limiter sliding window left boundary (inclusive) + leftWindowBoundary := height - rateLimitFlags.RateLimitWindow + if leftWindowBoundary < 0 { + leftWindowBoundary = 0 + } + + // get the conversion rates for all foreign coins + var gasCoinRates map[int64]*big.Float + var erc20CoinRates map[string]*big.Float + var rateLimitInZeta *big.Float + if applyLimit { + gasCoinRates, erc20CoinRates = k.GetRatelimiterRates(ctx) + rateLimitInZeta = new(big.Float).SetInt(rateLimitFlags.RateLimitInZeta.BigInt()) + } + + // define a few variables to be used in the below loops + limitExceeded := false + totalPending := uint64(0) + totalCctxValueInZeta := big.NewFloat(0) + cctxs := make([]*types.CrossChainTx, 0) + + // the criteria to stop adding cctxs to the result + maxCCTXsReached := func() bool { + // #nosec G701 len always positive + return uint32(len(cctxs)) >= limit + } + + // query pending cctxs for each supported chain + chains := k.zetaObserverKeeper.GetSupportedChains(ctx) +ChainLoop: + for _, chain := range chains { + // get pending nonces for this chain + pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) + if !found { + return nil, status.Error(codes.Internal, "pending nonces not found") + } + + // 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 + // therefore might mask some lower nonce cctx that is still pending. + startNonce := pendingNonces.NonceLow - 1 + endNonce := pendingNonces.NonceLow - 1000 + if endNonce < 0 { + endNonce = 0 + } + + // query cctx by nonce backwards to the left boundary of the rate limit sliding window + for nonce := startNonce; nonce >= 0; nonce-- { + nonceToCctx, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tss.TssPubkey, chain.ChainId, nonce) + if !found { + return nil, status.Error(codes.Internal, fmt.Sprintf("nonceToCctx not found: chainid %d, nonce %d", chain.ChainId, nonce)) + } + cctx, found := k.GetCrossChainTx(ctx, nonceToCctx.CctxIndex) + if !found { + return nil, status.Error(codes.Internal, fmt.Sprintf("cctx not found: index %s", nonceToCctx.CctxIndex)) + } + + // 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 three criteria to stop scanning backwards: + // criteria #1: if rate limiter is disabled, we should stop exactly on the `endNonce` + if !applyLimit && nonce < endNonce { + break + } + if applyLimit { + // criteria #2: if rate limiter is enabled, we'll stop at the left window boundary if the `endNonce` hasn't hit it yet + // #nosec G701 always positive + if nonce < endNonce && cctx.InboundTxParams.InboundTxObservedExternalHeight < uint64(leftWindowBoundary) { + break + } + // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded + if rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, totalCctxValueInZeta, rateLimitInZeta) { + limitExceeded = true + break ChainLoop + } + } + + // only take a `limit` number of pending cctxs as result but still count the total pending cctxs + if IsPending(cctx) { + totalPending++ + if !maxCCTXsReached() { + cctxs = append(cctxs, &cctx) + } + } + } + + // add the pending nonces to the total pending + // #nosec G701 always in range + totalPending += uint64(pendingNonces.NonceHigh - pendingNonces.NonceLow) + + // now query the pending nonces that we know are pending + for i := pendingNonces.NonceLow; i < pendingNonces.NonceHigh; i++ { + nonceToCctx, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tss.TssPubkey, chain.ChainId, i) + if !found { + return nil, status.Error(codes.Internal, "nonceToCctx not found") + } + cctx, found := k.GetCrossChainTx(ctx, nonceToCctx.CctxIndex) + if !found { + return nil, status.Error(codes.Internal, "cctxIndex not found") + } + + // only take a `limit` number of pending cctxs as result + if maxCCTXsReached() { + break + } + // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded + if applyLimit && rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, totalCctxValueInZeta, rateLimitInZeta) { + limitExceeded = true + break ChainLoop + } + cctxs = append(cctxs, &cctx) + } + } + + return &types.QueryListCctxPendingWithRateLimitResponse{ + CrossChainTx: cctxs, + TotalPending: totalPending, + RateLimitExceeded: limitExceeded, + }, nil +} + +// convertCctxValue converts the value of the cctx in ZETA using given conversion rates +func convertCctxValue( + chainID int64, + cctx *types.CrossChainTx, + gasCoinRates map[int64]*big.Float, + erc20CoinRates map[string]*big.Float, +) *big.Float { + var rate *big.Float + switch cctx.InboundTxParams.CoinType { + case coin.CoinType_Zeta: + // no conversion needed for ZETA + rate = big.NewFloat(1.0) + case coin.CoinType_Gas: + // convert gas coin amount into ZETA + rate = gasCoinRates[chainID] + case coin.CoinType_ERC20: + // convert erc20 coin amount into ZETA + rate = erc20CoinRates[strings.ToLower(cctx.InboundTxParams.Asset)] + default: + // skip CoinType_Cmd + return big.NewFloat(0) + } + if rate == nil || rate.Cmp(big.NewFloat(0)) == 0 { + // should not happen, return 0 to skip this cctx + return big.NewFloat(0) + } + + // convert asset amount into ZETA + amountCctx := new(big.Float).SetInt(cctx.InboundTxParams.Amount.BigInt()) + amountZeta := new(big.Float).Mul(amountCctx, rate) + return amountZeta +} + +// rateLimitExceeded accumulates the cctx value and then checks if the rate limit is exceeded +// returns true if the rate limit is exceeded +func rateLimitExceeded( + chainID int64, + cctx *types.CrossChainTx, + gasCoinRates map[int64]*big.Float, + erc20CoinRates map[string]*big.Float, + currentCctxValue *big.Float, + rateLimitValue *big.Float, +) bool { + amountZeta := convertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates) + currentCctxValue.Add(currentCctxValue, amountZeta) + return currentCctxValue.Cmp(rateLimitValue) > 0 +} diff --git a/x/crosschain/keeper/rate_limiter_flags.go b/x/crosschain/keeper/rate_limiter_flags.go new file mode 100644 index 0000000000..f5cd88cb99 --- /dev/null +++ b/x/crosschain/keeper/rate_limiter_flags.go @@ -0,0 +1,87 @@ +package keeper + +import ( + "math/big" + "strings" + + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// hardcoded rate limiter flags +var rateLimitFlags = types.RateLimiterFlags{ + IsEnabled: true, + RateLimitWindow: 1200, // 1200 zeta blocks, 2 hours + RateLimitInZeta: math.NewUint(2000000), // 2,000,000 ZETA + Zrc20Rates: []*types.ZRC20Rate{ + // ETH + { + ChainId: chains.GoerliLocalnetChain().ChainId, + CoinType: coin.CoinType_Gas, + Asset: "", + ConversionRate: 2500, + }, + // USDT + { + ChainId: chains.GoerliLocalnetChain().ChainId, + CoinType: coin.CoinType_ERC20, + Asset: "0xbD1e64A22B9F92D9Ce81aA9B4b0fFacd80215564", + ConversionRate: 0.8, + }, + // BTC + { + ChainId: chains.BtcRegtestChain().ChainId, + CoinType: coin.CoinType_ERC20, + Asset: "0x8f56682c2b8b2e3d4f6f7f7d6f3c01b3f6f6a7d6", + ConversionRate: 50000, + }, + }, +} + +// SetRatelimiterFlags set the rate limiter flags in the store +func (k Keeper) SetRatelimiterFlags(ctx sdk.Context, crosschainFlags types.RateLimiterFlags) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.RateLimiterFlagsKey)) + b := k.cdc.MustMarshal(&crosschainFlags) + store.Set([]byte{0}, b) +} + +// GetRatelimiterFlags read the rate limiter flags from the store +func (k Keeper) GetRatelimiterFlags(_ sdk.Context) (val types.RateLimiterFlags, found bool) { + return rateLimitFlags, true + // store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.RateLimiterFlagsKey)) + + // b := store.Get([]byte{0}) + // if b == nil { + // return val, false + // } + + // k.cdc.MustUnmarshal(b, &val) + // return val, true +} + +// GetRatelimiterRates returns two maps of foreign coins and their rates +// The 1st map: foreign chain id -> gas coin rate +// The 2nd map: foreign erc20 asset -> erc20 coin rate +func (k Keeper) GetRatelimiterRates(ctx sdk.Context) (map[int64]*big.Float, map[string]*big.Float) { + rateLimitFlags, _ := k.GetRatelimiterFlags(ctx) + + // the result maps + gasCoinRates := make(map[int64]*big.Float) + erc20CoinRates := make(map[string]*big.Float) + + // loop through the rate limiter flags to get the rate + for _, zrc20Rate := range rateLimitFlags.Zrc20Rates { + rate := big.NewFloat(zrc20Rate.ConversionRate) + switch zrc20Rate.CoinType { + case coin.CoinType_Gas: + gasCoinRates[zrc20Rate.ChainId] = rate + case coin.CoinType_ERC20: + erc20CoinRates[strings.ToLower(zrc20Rate.Asset)] = rate + } + } + return gasCoinRates, erc20CoinRates +} diff --git a/x/crosschain/types/keys.go b/x/crosschain/types/keys.go index 719f54687f..9d1c38c882 100644 --- a/x/crosschain/types/keys.go +++ b/x/crosschain/types/keys.go @@ -53,6 +53,9 @@ const ( // #nosec G101: Potential hardcoded credentials (gosec) // ZetaAccountingKey value is used as prefix for storing ZetaAccountingKey ZetaAccountingKey = "ZetaAccounting-value-" + + // RateLimiterFlagsKey is the key for the rate limiter flags + RateLimiterFlagsKey = "RateLimiterFlags-value-" ) // OutTxTrackerKey returns the store key to retrieve a OutTxTracker from the index fields diff --git a/x/crosschain/types/query.pb.go b/x/crosschain/types/query.pb.go index af554543a3..0a8a73f0e4 100644 --- a/x/crosschain/types/query.pb.go +++ b/x/crosschain/types/query.pb.go @@ -1587,6 +1587,116 @@ func (m *QueryListCctxPendingResponse) GetTotalPending() uint64 { return 0 } +type QueryListCctxPendingWithRateLimitRequest struct { + Limit uint32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` +} + +func (m *QueryListCctxPendingWithRateLimitRequest) Reset() { + *m = QueryListCctxPendingWithRateLimitRequest{} +} +func (m *QueryListCctxPendingWithRateLimitRequest) String() string { return proto.CompactTextString(m) } +func (*QueryListCctxPendingWithRateLimitRequest) ProtoMessage() {} +func (*QueryListCctxPendingWithRateLimitRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_65a992045e92a606, []int{33} +} +func (m *QueryListCctxPendingWithRateLimitRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryListCctxPendingWithRateLimitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryListCctxPendingWithRateLimitRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryListCctxPendingWithRateLimitRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListCctxPendingWithRateLimitRequest.Merge(m, src) +} +func (m *QueryListCctxPendingWithRateLimitRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryListCctxPendingWithRateLimitRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListCctxPendingWithRateLimitRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryListCctxPendingWithRateLimitRequest proto.InternalMessageInfo + +func (m *QueryListCctxPendingWithRateLimitRequest) GetLimit() uint32 { + if m != nil { + return m.Limit + } + return 0 +} + +type QueryListCctxPendingWithRateLimitResponse 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"` + RateLimitExceeded bool `protobuf:"varint,3,opt,name=rate_limit_exceeded,json=rateLimitExceeded,proto3" json:"rate_limit_exceeded,omitempty"` +} + +func (m *QueryListCctxPendingWithRateLimitResponse) Reset() { + *m = QueryListCctxPendingWithRateLimitResponse{} +} +func (m *QueryListCctxPendingWithRateLimitResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryListCctxPendingWithRateLimitResponse) ProtoMessage() {} +func (*QueryListCctxPendingWithRateLimitResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_65a992045e92a606, []int{34} +} +func (m *QueryListCctxPendingWithRateLimitResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryListCctxPendingWithRateLimitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryListCctxPendingWithRateLimitResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryListCctxPendingWithRateLimitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListCctxPendingWithRateLimitResponse.Merge(m, src) +} +func (m *QueryListCctxPendingWithRateLimitResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryListCctxPendingWithRateLimitResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListCctxPendingWithRateLimitResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryListCctxPendingWithRateLimitResponse proto.InternalMessageInfo + +func (m *QueryListCctxPendingWithRateLimitResponse) GetCrossChainTx() []*CrossChainTx { + if m != nil { + return m.CrossChainTx + } + return nil +} + +func (m *QueryListCctxPendingWithRateLimitResponse) GetTotalPending() uint64 { + if m != nil { + return m.TotalPending + } + return 0 +} + +func (m *QueryListCctxPendingWithRateLimitResponse) GetRateLimitExceeded() bool { + if m != nil { + return m.RateLimitExceeded + } + return false +} + type QueryLastZetaHeightRequest struct { } @@ -1594,7 +1704,7 @@ func (m *QueryLastZetaHeightRequest) Reset() { *m = QueryLastZetaHeightR func (m *QueryLastZetaHeightRequest) String() string { return proto.CompactTextString(m) } func (*QueryLastZetaHeightRequest) ProtoMessage() {} func (*QueryLastZetaHeightRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{33} + return fileDescriptor_65a992045e92a606, []int{35} } func (m *QueryLastZetaHeightRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1631,7 +1741,7 @@ func (m *QueryLastZetaHeightResponse) Reset() { *m = QueryLastZetaHeight func (m *QueryLastZetaHeightResponse) String() string { return proto.CompactTextString(m) } func (*QueryLastZetaHeightResponse) ProtoMessage() {} func (*QueryLastZetaHeightResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{34} + return fileDescriptor_65a992045e92a606, []int{36} } func (m *QueryLastZetaHeightResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1676,7 +1786,7 @@ func (m *QueryConvertGasToZetaRequest) Reset() { *m = QueryConvertGasToZ func (m *QueryConvertGasToZetaRequest) String() string { return proto.CompactTextString(m) } func (*QueryConvertGasToZetaRequest) ProtoMessage() {} func (*QueryConvertGasToZetaRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{35} + return fileDescriptor_65a992045e92a606, []int{37} } func (m *QueryConvertGasToZetaRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1729,7 +1839,7 @@ func (m *QueryConvertGasToZetaResponse) Reset() { *m = QueryConvertGasTo func (m *QueryConvertGasToZetaResponse) String() string { return proto.CompactTextString(m) } func (*QueryConvertGasToZetaResponse) ProtoMessage() {} func (*QueryConvertGasToZetaResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{36} + return fileDescriptor_65a992045e92a606, []int{38} } func (m *QueryConvertGasToZetaResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1786,7 +1896,7 @@ func (m *QueryMessagePassingProtocolFeeRequest) Reset() { *m = QueryMess func (m *QueryMessagePassingProtocolFeeRequest) String() string { return proto.CompactTextString(m) } func (*QueryMessagePassingProtocolFeeRequest) ProtoMessage() {} func (*QueryMessagePassingProtocolFeeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{37} + return fileDescriptor_65a992045e92a606, []int{39} } func (m *QueryMessagePassingProtocolFeeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1825,7 +1935,7 @@ func (m *QueryMessagePassingProtocolFeeResponse) Reset() { func (m *QueryMessagePassingProtocolFeeResponse) String() string { return proto.CompactTextString(m) } func (*QueryMessagePassingProtocolFeeResponse) ProtoMessage() {} func (*QueryMessagePassingProtocolFeeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{38} + return fileDescriptor_65a992045e92a606, []int{40} } func (m *QueryMessagePassingProtocolFeeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1895,6 +2005,8 @@ func init() { proto.RegisterType((*QueryAllCctxResponse)(nil), "zetachain.zetacore.crosschain.QueryAllCctxResponse") proto.RegisterType((*QueryListCctxPendingRequest)(nil), "zetachain.zetacore.crosschain.QueryListCctxPendingRequest") proto.RegisterType((*QueryListCctxPendingResponse)(nil), "zetachain.zetacore.crosschain.QueryListCctxPendingResponse") + proto.RegisterType((*QueryListCctxPendingWithRateLimitRequest)(nil), "zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitRequest") + proto.RegisterType((*QueryListCctxPendingWithRateLimitResponse)(nil), "zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitResponse") proto.RegisterType((*QueryLastZetaHeightRequest)(nil), "zetachain.zetacore.crosschain.QueryLastZetaHeightRequest") proto.RegisterType((*QueryLastZetaHeightResponse)(nil), "zetachain.zetacore.crosschain.QueryLastZetaHeightResponse") proto.RegisterType((*QueryConvertGasToZetaRequest)(nil), "zetachain.zetacore.crosschain.QueryConvertGasToZetaRequest") @@ -1906,115 +2018,120 @@ func init() { func init() { proto.RegisterFile("crosschain/query.proto", fileDescriptor_65a992045e92a606) } var fileDescriptor_65a992045e92a606 = []byte{ - // 1715 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xd1, 0x6f, 0x14, 0x45, - 0x18, 0xef, 0xf4, 0x28, 0x94, 0x69, 0xa1, 0x32, 0x54, 0xac, 0x4b, 0x7b, 0x85, 0xad, 0xd0, 0x0a, - 0xf6, 0x16, 0x0a, 0x14, 0x81, 0x62, 0xbc, 0x16, 0x29, 0xc4, 0x02, 0xf5, 0x52, 0xa3, 0xc1, 0x98, - 0xcb, 0x74, 0x6f, 0xdd, 0xdb, 0xb0, 0xdd, 0x29, 0xb7, 0x7b, 0xa4, 0xa5, 0xe9, 0x0b, 0x0f, 0x3e, - 0x9b, 0xf0, 0xe0, 0x8b, 0xaf, 0x46, 0x1f, 0x7c, 0xf0, 0xc1, 0xe8, 0x83, 0x09, 0xc6, 0xa8, 0xc8, - 0x23, 0x89, 0x89, 0x31, 0x9a, 0x18, 0x03, 0xfe, 0x05, 0xfe, 0x05, 0x66, 0x67, 0xbf, 0xbd, 0x9b, - 0xdd, 0xdb, 0xbd, 0x9b, 0x5e, 0x8f, 0x07, 0x9e, 0x7a, 0xbb, 0x33, 0xdf, 0x37, 0xbf, 0xdf, 0x6f, - 0xbe, 0xf9, 0xf6, 0xfb, 0xa6, 0xf8, 0x80, 0x5e, 0x61, 0xae, 0xab, 0x97, 0xa9, 0xe5, 0x68, 0xb7, - 0xab, 0x46, 0x65, 0x3d, 0xb7, 0x5a, 0x61, 0x1e, 0x23, 0x23, 0x77, 0x0d, 0x8f, 0xf2, 0xd7, 0x39, - 0xfe, 0x8b, 0x55, 0x8c, 0x5c, 0x7d, 0xaa, 0x72, 0x4c, 0x67, 0xee, 0x0a, 0x73, 0xb5, 0x65, 0xea, - 0x1a, 0x81, 0x9d, 0x76, 0xe7, 0xe4, 0xb2, 0xe1, 0xd1, 0x93, 0xda, 0x2a, 0x35, 0x2d, 0x87, 0x7a, - 0x16, 0x73, 0x02, 0x57, 0xca, 0xa8, 0xb0, 0x04, 0xff, 0x59, 0xe4, 0xbf, 0x8b, 0xde, 0x1a, 0x4c, - 0x50, 0x84, 0x09, 0x26, 0x75, 0x8b, 0xab, 0x15, 0x4b, 0x37, 0x60, 0x6c, 0x4c, 0x18, 0xe3, 0x36, - 0xc5, 0x32, 0x75, 0xcb, 0x45, 0x8f, 0x15, 0x75, 0xbd, 0xe6, 0x20, 0xdb, 0x30, 0xc9, 0xab, 0x50, - 0xfd, 0x96, 0x51, 0x81, 0x71, 0x55, 0x18, 0xb7, 0xa9, 0xeb, 0x15, 0x97, 0x6d, 0xa6, 0xdf, 0x2a, - 0x96, 0x0d, 0xcb, 0x2c, 0x7b, 0x09, 0x28, 0x59, 0xd5, 0x6b, 0x74, 0x32, 0x68, 0x32, 0x93, 0xf1, - 0x9f, 0x9a, 0xff, 0x0b, 0xde, 0x0e, 0x9b, 0x8c, 0x99, 0xb6, 0xa1, 0xd1, 0x55, 0x4b, 0xa3, 0x8e, - 0xc3, 0x3c, 0xce, 0xdc, 0x0d, 0x46, 0xd5, 0x61, 0xac, 0xbc, 0xe3, 0x8b, 0x73, 0xd3, 0xf0, 0x68, - 0x5e, 0xd7, 0x59, 0xd5, 0xf1, 0x2c, 0xc7, 0x2c, 0x18, 0xb7, 0xab, 0x86, 0xeb, 0xa9, 0xd7, 0xf0, - 0xc1, 0xc4, 0x51, 0x77, 0x95, 0x39, 0xae, 0x41, 0x72, 0x78, 0x3f, 0x5d, 0x66, 0x15, 0xcf, 0x28, - 0x15, 0xfd, 0x2d, 0x28, 0xd2, 0x15, 0x7f, 0xc6, 0x10, 0x3a, 0x84, 0x26, 0x76, 0x17, 0xf6, 0xc1, - 0x10, 0xb7, 0xe5, 0x03, 0x35, 0x77, 0xf3, 0x86, 0x77, 0xa3, 0xea, 0x2d, 0xad, 0x2d, 0x05, 0xf0, - 0x61, 0x35, 0x32, 0x84, 0x77, 0x71, 0x76, 0x57, 0x2f, 0x71, 0x17, 0x99, 0x42, 0xf8, 0x48, 0x06, - 0x71, 0x8f, 0xc3, 0x1c, 0xdd, 0x18, 0xea, 0x3e, 0x84, 0x26, 0x76, 0x14, 0x82, 0x07, 0xb5, 0x8a, - 0x87, 0x93, 0xdd, 0x01, 0xbc, 0x77, 0x71, 0x3f, 0x13, 0xde, 0x73, 0xa7, 0x7d, 0x53, 0xc7, 0x73, - 0x4d, 0x03, 0x27, 0x27, 0xba, 0x9a, 0xdd, 0xf1, 0xe8, 0xef, 0xd1, 0xae, 0x42, 0xc4, 0x8d, 0x6a, - 0x00, 0x8b, 0xbc, 0x6d, 0x27, 0xb1, 0xb8, 0x8c, 0x71, 0x3d, 0xc0, 0x60, 0xcd, 0xa3, 0xb9, 0x20, - 0x1a, 0x73, 0x7e, 0x34, 0xe6, 0x82, 0x28, 0x86, 0x68, 0xcc, 0x2d, 0x52, 0xd3, 0x00, 0xdb, 0x82, - 0x60, 0xa9, 0x3e, 0x40, 0x40, 0xaf, 0x61, 0x9d, 0x54, 0x7a, 0x99, 0x0e, 0xd0, 0x23, 0xf3, 0x11, - 0xfc, 0xdd, 0x1c, 0xff, 0x78, 0x4b, 0xfc, 0x01, 0xa6, 0x08, 0x81, 0x7b, 0x08, 0xab, 0x49, 0x04, - 0x66, 0xd7, 0xe7, 0x7c, 0x24, 0xa1, 0x5e, 0x83, 0xb8, 0x87, 0x23, 0x83, 0x3d, 0x0f, 0x1e, 0x62, - 0x2a, 0x76, 0xb7, 0xad, 0xe2, 0x2f, 0x08, 0x8f, 0x35, 0x05, 0xf1, 0x9c, 0x88, 0xf9, 0x31, 0xc2, - 0x87, 0x43, 0x1e, 0x57, 0x9d, 0x34, 0x2d, 0x5f, 0xc6, 0xbd, 0x41, 0xe6, 0xb2, 0x4a, 0xd1, 0x23, - 0x54, 0xea, 0x98, 0xa0, 0x3f, 0x0a, 0xbb, 0x9a, 0x04, 0x04, 0xf4, 0x2c, 0xe0, 0x3e, 0xcb, 0x89, - 0xcb, 0x79, 0xac, 0x85, 0x9c, 0xa2, 0xbf, 0x40, 0x4d, 0xd1, 0x49, 0xe7, 0xc4, 0x14, 0x4e, 0xb0, - 0xb0, 0xa4, 0xdb, 0xe9, 0x13, 0xfc, 0xbd, 0x70, 0x82, 0xa3, 0xeb, 0x3c, 0x0f, 0x22, 0x5d, 0xc0, - 0x23, 0x61, 0x76, 0xf5, 0x97, 0xbc, 0x42, 0xdd, 0xf2, 0x12, 0x9b, 0xd3, 0xbd, 0xb5, 0x50, 0x26, - 0x05, 0xf7, 0x5a, 0x30, 0x00, 0x29, 0xbf, 0xf6, 0xac, 0x6e, 0xe2, 0x6c, 0x9a, 0x31, 0x70, 0xff, - 0x00, 0xef, 0xb5, 0x22, 0x23, 0x20, 0xf4, 0xa4, 0x04, 0xfd, 0xba, 0x11, 0x28, 0x10, 0x73, 0xa5, - 0xce, 0xc0, 0xf2, 0xd1, 0xc9, 0x97, 0xa8, 0x47, 0x65, 0xc0, 0xdf, 0xc5, 0xa3, 0xa9, 0xd6, 0x80, - 0xfe, 0x3d, 0xbc, 0x67, 0xce, 0xc7, 0xc4, 0x83, 0x7e, 0x69, 0xcd, 0x95, 0xcc, 0x17, 0xa2, 0x0d, - 0x40, 0x8f, 0xfa, 0x51, 0x4d, 0x50, 0x1d, 0x42, 0xa6, 0x51, 0xf5, 0x4e, 0x05, 0xe7, 0x43, 0x04, - 0x1a, 0x25, 0xac, 0xd4, 0x64, 0x8b, 0x32, 0x1d, 0xda, 0xa2, 0xce, 0xc5, 0xa9, 0x86, 0x5f, 0x0a, - 0x43, 0x6d, 0x9e, 0xba, 0x8b, 0x7e, 0x65, 0x26, 0x7c, 0x5a, 0x2c, 0xa7, 0x64, 0xac, 0xc1, 0x0e, - 0x07, 0x0f, 0x6a, 0x11, 0x0f, 0x35, 0x1a, 0x00, 0xe5, 0x39, 0xdc, 0x1b, 0xbe, 0x03, 0x6d, 0xc7, - 0x5b, 0x90, 0xad, 0xb9, 0xa8, 0x19, 0xaa, 0x14, 0x10, 0xe5, 0x6d, 0x3b, 0x8e, 0xa8, 0x53, 0xbb, - 0xf7, 0x25, 0x02, 0x12, 0x91, 0x35, 0x12, 0x49, 0x64, 0xda, 0x22, 0xd1, 0xb9, 0xfd, 0x99, 0xae, - 0xa7, 0x82, 0x05, 0xea, 0x7a, 0xb3, 0x7e, 0x61, 0x7b, 0x85, 0xd7, 0xb5, 0xcd, 0xb7, 0x69, 0x03, - 0x4e, 0x61, 0x92, 0x1d, 0x10, 0x7d, 0x1f, 0x0f, 0xc4, 0x86, 0x40, 0xd2, 0x5c, 0x0b, 0xbe, 0x71, - 0x87, 0x71, 0x37, 0x6a, 0xb9, 0x7e, 0x38, 0x52, 0x40, 0x77, 0x6a, 0x27, 0x7f, 0x46, 0xc0, 0x33, - 0x69, 0xa9, 0x66, 0x3c, 0x33, 0x1d, 0xe0, 0xd9, 0xb9, 0x5d, 0x3e, 0x8e, 0xf7, 0x87, 0xbb, 0x25, - 0x66, 0xab, 0xe4, 0xad, 0x5d, 0x80, 0xa6, 0x03, 0x26, 0xcf, 0xae, 0x5f, 0xf7, 0xeb, 0xf9, 0x76, - 0xdb, 0x00, 0x13, 0x0f, 0x46, 0x97, 0x06, 0xd5, 0x6e, 0xe0, 0x7e, 0x31, 0xb7, 0x4a, 0x96, 0xff, - 0xa2, 0x49, 0x21, 0xe2, 0x40, 0xfd, 0x10, 0x38, 0xe6, 0x6d, 0xfb, 0x59, 0x64, 0xe4, 0xaf, 0x11, - 0x10, 0xa9, 0xf9, 0x4f, 0x25, 0x92, 0xd9, 0x16, 0x91, 0xce, 0xed, 0xfa, 0x75, 0x28, 0xa4, 0x16, - 0x2c, 0x97, 0x6b, 0xbf, 0x68, 0x38, 0xa5, 0x7a, 0xfb, 0xd8, 0xac, 0x1c, 0x1d, 0xc4, 0x3d, 0xb6, - 0xb5, 0x62, 0x79, 0x7c, 0xf5, 0x3d, 0x85, 0xe0, 0x41, 0xbd, 0x1f, 0x56, 0x4c, 0x0d, 0x0e, 0x9f, - 0x95, 0x14, 0x2a, 0xee, 0xf7, 0x98, 0x47, 0x6d, 0x58, 0x08, 0x22, 0x2b, 0xf2, 0xae, 0xd6, 0x23, - 0xfb, 0x87, 0xc7, 0xef, 0x66, 0x23, 0x89, 0x40, 0x3d, 0x13, 0x6a, 0x10, 0x1b, 0x05, 0xc4, 0x07, - 0xf0, 0x4e, 0x21, 0x35, 0x65, 0x0a, 0xf0, 0xa4, 0x2e, 0x01, 0xd3, 0x39, 0xe6, 0xdc, 0x31, 0x2a, - 0xfe, 0x97, 0x68, 0x89, 0xf9, 0xe6, 0x0d, 0xa7, 0xa0, 0x41, 0x3a, 0x05, 0xf7, 0x9a, 0xd4, 0x5d, - 0xa8, 0xa9, 0xb7, 0xbb, 0x50, 0x7b, 0x56, 0x3f, 0x47, 0x50, 0x3f, 0x34, 0xba, 0x05, 0x3c, 0xaf, - 0xe1, 0x7d, 0xac, 0xea, 0x2d, 0xb3, 0xaa, 0x53, 0x9a, 0xa7, 0xee, 0x55, 0xc7, 0x1f, 0x0c, 0x3b, - 0xf6, 0x86, 0x01, 0x7f, 0x36, 0xbf, 0x27, 0xd0, 0x99, 0x7d, 0xd9, 0x30, 0x60, 0x76, 0xb0, 0x68, - 0xe3, 0x00, 0x99, 0xc0, 0x03, 0xfe, 0x5f, 0x31, 0x4f, 0x65, 0xb8, 0x9e, 0xf1, 0xd7, 0xea, 0x38, - 0x3e, 0xc2, 0x61, 0x5e, 0x33, 0x5c, 0x97, 0x9a, 0xc6, 0x22, 0x75, 0x5d, 0xcb, 0x31, 0x17, 0xeb, - 0x1e, 0x43, 0x75, 0x2f, 0xe3, 0xa3, 0xad, 0x26, 0x02, 0xb1, 0x61, 0xbc, 0xfb, 0xa3, 0x1a, 0xc4, - 0x80, 0x50, 0xfd, 0xc5, 0xd4, 0x7f, 0x23, 0xb8, 0x87, 0x3b, 0x22, 0x0f, 0x11, 0xee, 0x17, 0xfb, - 0x36, 0x72, 0xbe, 0x45, 0xf4, 0x34, 0xb9, 0xb2, 0x50, 0x2e, 0xb4, 0x65, 0x1b, 0x20, 0x56, 0x2f, - 0xde, 0xfb, 0xed, 0xdf, 0xfb, 0xdd, 0x67, 0xc9, 0x19, 0xcd, 0x37, 0x9d, 0x14, 0xee, 0x9f, 0x6a, - 0x97, 0x3c, 0x35, 0x23, 0x6d, 0x03, 0x92, 0xe0, 0xa6, 0xb6, 0xc1, 0xd3, 0xde, 0x26, 0xf9, 0x0e, - 0xe1, 0x01, 0xd1, 0x6f, 0xde, 0xb6, 0xe5, 0xb8, 0x24, 0x5f, 0x5c, 0xc8, 0x71, 0x49, 0xb9, 0x8c, - 0x50, 0x8f, 0x73, 0x2e, 0x47, 0xc8, 0x98, 0x04, 0x17, 0xf2, 0x17, 0xc2, 0x07, 0x62, 0xc8, 0xa1, - 0x7f, 0x24, 0xf9, 0x36, 0x40, 0x44, 0x9b, 0x60, 0x65, 0x76, 0x3b, 0x2e, 0x80, 0xce, 0x79, 0x4e, - 0xe7, 0x34, 0x99, 0x92, 0xa0, 0x03, 0xb6, 0xb0, 0x43, 0x9b, 0xe4, 0x4f, 0x84, 0x5f, 0x14, 0x9a, - 0x34, 0x81, 0xdc, 0x9b, 0x92, 0xc8, 0x52, 0x1b, 0x7c, 0x25, 0xbf, 0x0d, 0x0f, 0x40, 0x6d, 0x86, - 0x53, 0x9b, 0x26, 0xa7, 0x53, 0xa8, 0x59, 0x4e, 0x0a, 0xb3, 0xa2, 0x55, 0xda, 0x24, 0xdf, 0x22, - 0xbc, 0x37, 0x4a, 0x4e, 0x3a, 0xe6, 0x12, 0x5a, 0x6d, 0xe9, 0x98, 0x4b, 0x6a, 0x9f, 0x5b, 0xc6, - 0x9c, 0xc0, 0xc4, 0x25, 0xbf, 0x02, 0x70, 0xa1, 0x05, 0x99, 0x91, 0x3c, 0xbc, 0x89, 0x8d, 0x98, - 0x72, 0xb1, 0x4d, 0x6b, 0x00, 0xff, 0x3a, 0x07, 0x3f, 0x45, 0x4e, 0x34, 0x01, 0x5f, 0x37, 0xd3, - 0x36, 0xc2, 0xe7, 0x4d, 0xf2, 0x3b, 0xc2, 0xa4, 0xb1, 0x35, 0x25, 0x52, 0x78, 0x52, 0x1b, 0x62, - 0xe5, 0x8d, 0x76, 0xcd, 0x81, 0x4f, 0x9e, 0xf3, 0xb9, 0x40, 0xce, 0xa5, 0xf2, 0x89, 0x5f, 0x8d, - 0x17, 0x4b, 0xd4, 0xa3, 0x22, 0xb1, 0x1f, 0x10, 0xde, 0x17, 0x5d, 0xc1, 0x0f, 0xaf, 0x99, 0x2d, - 0x84, 0x48, 0x9b, 0xbb, 0x94, 0xda, 0x02, 0xab, 0x93, 0x9c, 0xd5, 0x38, 0x39, 0x22, 0xb5, 0x4b, - 0xe4, 0x2b, 0x54, 0x6f, 0xbd, 0xc8, 0xb4, 0x64, 0x80, 0xc4, 0x7a, 0x44, 0xe5, 0xec, 0x96, 0xed, - 0x00, 0xac, 0xc6, 0xc1, 0xbe, 0x4a, 0xc6, 0x53, 0xc0, 0x9a, 0x60, 0xe0, 0x6b, 0x5e, 0x32, 0xd6, - 0x36, 0xc9, 0x17, 0x08, 0xf7, 0x85, 0x5e, 0x7c, 0xa9, 0xa7, 0x25, 0xc5, 0x6a, 0x0b, 0x71, 0x42, - 0xa7, 0xaa, 0x8e, 0x73, 0xc4, 0x87, 0xc9, 0x68, 0x0b, 0xc4, 0xe4, 0x01, 0xc2, 0x2f, 0xc4, 0x4b, - 0x1a, 0x22, 0x95, 0x3c, 0x52, 0xea, 0x2b, 0x65, 0xa6, 0x3d, 0x63, 0x49, 0xa9, 0xf5, 0x38, 0xd6, - 0x87, 0x08, 0xf7, 0x09, 0x55, 0x0b, 0xb9, 0x24, 0xb3, 0x7c, 0xab, 0xea, 0x48, 0x79, 0x6b, 0x9b, - 0x5e, 0x80, 0xcd, 0x31, 0xce, 0xe6, 0x15, 0xa2, 0xa6, 0xb0, 0x11, 0x2a, 0x3d, 0xf2, 0x08, 0x35, - 0x34, 0xa3, 0x44, 0x36, 0x15, 0x26, 0xb7, 0xd2, 0x72, 0xa9, 0x27, 0xfd, 0x1a, 0x40, 0x9d, 0xe6, - 0xf0, 0x4f, 0x90, 0x5c, 0x0a, 0x7c, 0x3b, 0x6a, 0x57, 0x0b, 0xff, 0x9f, 0x10, 0x26, 0x31, 0x9f, - 0xfe, 0x29, 0x90, 0x4d, 0x19, 0xdb, 0x61, 0x93, 0xde, 0xec, 0xab, 0x39, 0xce, 0x66, 0x82, 0x1c, - 0x95, 0x63, 0x43, 0x3e, 0x43, 0x78, 0x07, 0x4f, 0x3e, 0x53, 0x92, 0x32, 0x8a, 0xe9, 0xf1, 0xd4, - 0x96, 0x6c, 0x24, 0xbf, 0xbb, 0x3a, 0x7c, 0xb0, 0xb8, 0xc8, 0xdf, 0x20, 0xdc, 0x27, 0x34, 0xf9, - 0xe4, 0xdc, 0x16, 0x56, 0x8c, 0x5e, 0x0c, 0xb4, 0x07, 0xf6, 0x0c, 0x07, 0xab, 0x91, 0xc9, 0xa6, - 0x60, 0x1b, 0x8a, 0xeb, 0x4f, 0x11, 0xde, 0x15, 0x7e, 0x81, 0xa6, 0x24, 0x77, 0x74, 0xcb, 0xc2, - 0xc6, 0x1a, 0x7d, 0x75, 0x8c, 0x63, 0x1d, 0x21, 0x07, 0x9b, 0x60, 0xf5, 0x2b, 0xb0, 0x01, 0xdf, - 0xca, 0x6f, 0x91, 0xa1, 0x43, 0x95, 0x2b, 0xc1, 0x92, 0x9b, 0x74, 0xb9, 0x12, 0x2c, 0xa5, 0x1f, - 0x6f, 0x99, 0x39, 0xf4, 0xba, 0x0d, 0x2f, 0x1d, 0xa3, 0xff, 0x48, 0x96, 0x0b, 0x86, 0xc4, 0x7f, - 0x4d, 0x2b, 0xe7, 0xdb, 0x31, 0x95, 0xfc, 0xaa, 0xdf, 0x8d, 0xa2, 0xf4, 0x81, 0x47, 0xbb, 0x7b, - 0x39, 0xe0, 0x89, 0xf7, 0x05, 0x72, 0xc0, 0x93, 0x2f, 0x13, 0x5a, 0x02, 0xb7, 0x23, 0x66, 0xb3, - 0x6f, 0x3f, 0x7a, 0x92, 0x45, 0x8f, 0x9f, 0x64, 0xd1, 0x3f, 0x4f, 0xb2, 0xe8, 0x93, 0xa7, 0xd9, - 0xae, 0xc7, 0x4f, 0xb3, 0x5d, 0x7f, 0x3c, 0xcd, 0x76, 0xdd, 0x3c, 0x69, 0x5a, 0x5e, 0xb9, 0xba, - 0x9c, 0xd3, 0xd9, 0x8a, 0xe8, 0x2a, 0xc4, 0xa3, 0xad, 0x89, 0x5e, 0xbd, 0xf5, 0x55, 0xc3, 0x5d, - 0xde, 0xc9, 0xbf, 0x02, 0xa7, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xda, 0xf6, 0xfb, 0xb3, 0x90, - 0x21, 0x00, 0x00, + // 1797 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0x41, 0x6f, 0x14, 0xc7, + 0x12, 0x76, 0x7b, 0x31, 0x98, 0xb6, 0xc1, 0xcf, 0x8d, 0x1f, 0xcf, 0x6f, 0xb1, 0xd7, 0x30, 0x7e, + 0x60, 0x03, 0xcf, 0xbb, 0x60, 0xc0, 0x3c, 0xc0, 0x3c, 0xb1, 0x36, 0x60, 0x50, 0x0c, 0x98, 0x95, + 0x23, 0x22, 0xa2, 0x68, 0xd4, 0x9e, 0xed, 0xcc, 0x8e, 0x18, 0xcf, 0x98, 0x9d, 0x59, 0xb4, 0xc6, + 0xf2, 0x85, 0x43, 0xce, 0x91, 0x38, 0xe4, 0x92, 0x6b, 0x94, 0x1c, 0x72, 0xc8, 0x21, 0x4a, 0x0e, + 0x91, 0x88, 0xa2, 0x24, 0x84, 0x23, 0x12, 0x52, 0x14, 0x25, 0x52, 0x14, 0x41, 0x7e, 0x41, 0x7e, + 0x41, 0x34, 0x3d, 0x35, 0xbb, 0x3d, 0xb3, 0x33, 0xbb, 0xed, 0xf5, 0x72, 0xe0, 0xe4, 0x9d, 0xe9, + 0xae, 0xea, 0xef, 0xab, 0xaa, 0xa9, 0xae, 0x2a, 0xe3, 0xfd, 0x5a, 0xd9, 0x76, 0x1c, 0xad, 0x44, + 0x0d, 0x2b, 0x77, 0xbf, 0xc2, 0xca, 0xeb, 0xd9, 0xb5, 0xb2, 0xed, 0xda, 0x64, 0xf4, 0x21, 0x73, + 0x29, 0x7f, 0x9d, 0xe5, 0xbf, 0xec, 0x32, 0xcb, 0xd6, 0xb7, 0xa6, 0x8f, 0x69, 0xb6, 0xb3, 0x6a, + 0x3b, 0xb9, 0x15, 0xea, 0x30, 0x5f, 0x2e, 0xf7, 0xe0, 0xe4, 0x0a, 0x73, 0xe9, 0xc9, 0xdc, 0x1a, + 0xd5, 0x0d, 0x8b, 0xba, 0x86, 0x6d, 0xf9, 0xaa, 0xd2, 0x63, 0xc2, 0x11, 0xfc, 0xa7, 0xca, 0x7f, + 0xab, 0x6e, 0x15, 0x36, 0xa4, 0x85, 0x0d, 0x3a, 0x75, 0xd4, 0xb5, 0xb2, 0xa1, 0x31, 0x58, 0x1b, + 0x17, 0xd6, 0xb8, 0x8c, 0x5a, 0xa2, 0x4e, 0x49, 0x75, 0x6d, 0x55, 0xd3, 0x6a, 0x0a, 0x32, 0x0d, + 0x9b, 0xdc, 0x32, 0xd5, 0xee, 0xb1, 0x32, 0xac, 0x2b, 0xc2, 0xba, 0x49, 0x1d, 0x57, 0x5d, 0x31, + 0x6d, 0xed, 0x9e, 0x5a, 0x62, 0x86, 0x5e, 0x72, 0x63, 0x50, 0xda, 0x15, 0xb7, 0x51, 0xc9, 0x90, + 0x6e, 0xeb, 0x36, 0xff, 0x99, 0xf3, 0x7e, 0xc1, 0xdb, 0x11, 0xdd, 0xb6, 0x75, 0x93, 0xe5, 0xe8, + 0x9a, 0x91, 0xa3, 0x96, 0x65, 0xbb, 0x9c, 0xb9, 0xe3, 0xaf, 0x2a, 0x23, 0x38, 0x7d, 0xdb, 0x33, + 0xce, 0x5d, 0xe6, 0xd2, 0xbc, 0xa6, 0xd9, 0x15, 0xcb, 0x35, 0x2c, 0xbd, 0xc0, 0xee, 0x57, 0x98, + 0xe3, 0x2a, 0x37, 0xf0, 0x81, 0xd8, 0x55, 0x67, 0xcd, 0xb6, 0x1c, 0x46, 0xb2, 0x78, 0x1f, 0x5d, + 0xb1, 0xcb, 0x2e, 0x2b, 0xaa, 0x9e, 0x0b, 0x54, 0xba, 0xea, 0xed, 0x18, 0x46, 0x07, 0xd1, 0xe4, + 0xee, 0xc2, 0x20, 0x2c, 0x71, 0x59, 0xbe, 0x50, 0x53, 0xb7, 0xc0, 0xdc, 0x5b, 0x15, 0x77, 0xb9, + 0xba, 0xec, 0xc3, 0x87, 0xd3, 0xc8, 0x30, 0xde, 0xc5, 0xd9, 0x5d, 0xbf, 0xcc, 0x55, 0xa4, 0x0a, + 0xc1, 0x23, 0x19, 0xc2, 0x3d, 0x96, 0x6d, 0x69, 0x6c, 0xb8, 0xfb, 0x20, 0x9a, 0xdc, 0x51, 0xf0, + 0x1f, 0x94, 0x0a, 0x1e, 0x89, 0x57, 0x07, 0xf0, 0xde, 0xc6, 0xfd, 0xb6, 0xf0, 0x9e, 0x2b, 0xed, + 0x9b, 0x3e, 0x9e, 0x6d, 0x1a, 0x38, 0x59, 0x51, 0xd5, 0xdc, 0x8e, 0x67, 0xbf, 0x8f, 0x75, 0x15, + 0x42, 0x6a, 0x14, 0x06, 0x2c, 0xf2, 0xa6, 0x19, 0xc7, 0xe2, 0x2a, 0xc6, 0xf5, 0x00, 0x83, 0x33, + 0x8f, 0x64, 0xfd, 0x68, 0xcc, 0x7a, 0xd1, 0x98, 0xf5, 0xa3, 0x18, 0xa2, 0x31, 0xbb, 0x44, 0x75, + 0x06, 0xb2, 0x05, 0x41, 0x52, 0x79, 0x82, 0x80, 0x5e, 0xc3, 0x39, 0x89, 0xf4, 0x52, 0x1d, 0xa0, + 0x47, 0x16, 0x42, 0xf8, 0xbb, 0x39, 0xfe, 0x89, 0x96, 0xf8, 0x7d, 0x4c, 0x21, 0x02, 0x8f, 0x10, + 0x56, 0xe2, 0x08, 0xcc, 0xad, 0xcf, 0x7b, 0x48, 0x02, 0x7b, 0x0d, 0xe1, 0x1e, 0x8e, 0x0c, 0x7c, + 0xee, 0x3f, 0x44, 0xac, 0xd8, 0xdd, 0xb6, 0x15, 0x7f, 0x44, 0x78, 0xbc, 0x29, 0x88, 0x37, 0xc4, + 0x98, 0x1f, 0x20, 0x7c, 0x28, 0xe0, 0x71, 0xdd, 0x4a, 0xb2, 0xe5, 0xbf, 0x71, 0xaf, 0x9f, 0xb9, + 0x8c, 0x62, 0xf8, 0x13, 0x2a, 0x76, 0xcc, 0xa0, 0xdf, 0x09, 0x5e, 0x8d, 0x03, 0x02, 0xf6, 0x2c, + 0xe0, 0x3e, 0xc3, 0x8a, 0x9a, 0xf3, 0x58, 0x0b, 0x73, 0x8a, 0xfa, 0x7c, 0x6b, 0x8a, 0x4a, 0x3a, + 0x67, 0x4c, 0xe1, 0x0b, 0x16, 0x8e, 0x74, 0x3a, 0xfd, 0x05, 0x7f, 0x23, 0x7c, 0xc1, 0xe1, 0x73, + 0xde, 0x04, 0x23, 0x5d, 0xc0, 0xa3, 0x41, 0x76, 0xf5, 0x8e, 0xbc, 0x46, 0x9d, 0xd2, 0xb2, 0x3d, + 0xaf, 0xb9, 0xd5, 0xc0, 0x4c, 0x69, 0xdc, 0x6b, 0xc0, 0x02, 0xa4, 0xfc, 0xda, 0xb3, 0xb2, 0x89, + 0x33, 0x49, 0xc2, 0xc0, 0xfd, 0x5d, 0xbc, 0xd7, 0x08, 0xad, 0x80, 0xa1, 0xa7, 0x24, 0xe8, 0xd7, + 0x85, 0xc0, 0x02, 0x11, 0x55, 0xca, 0x2c, 0x1c, 0x1f, 0xde, 0x7c, 0x99, 0xba, 0x54, 0x06, 0xfc, + 0x43, 0x3c, 0x96, 0x28, 0x0d, 0xe8, 0xef, 0xe0, 0x3d, 0xf3, 0x1e, 0x26, 0x1e, 0xf4, 0xcb, 0x55, + 0x47, 0x32, 0x5f, 0x88, 0x32, 0x00, 0x3d, 0xac, 0x47, 0xd1, 0xc1, 0xea, 0x10, 0x32, 0x8d, 0x56, + 0xef, 0x54, 0x70, 0x3e, 0x45, 0x60, 0xa3, 0x98, 0x93, 0x9a, 0xb8, 0x28, 0xd5, 0x21, 0x17, 0x75, + 0x2e, 0x4e, 0x73, 0xf8, 0x5f, 0x41, 0xa8, 0x2d, 0x50, 0x67, 0xc9, 0xab, 0xcc, 0x84, 0xab, 0xc5, + 0xb0, 0x8a, 0xac, 0x0a, 0x1e, 0xf6, 0x1f, 0x14, 0x15, 0x0f, 0x37, 0x0a, 0x00, 0xe5, 0x79, 0xdc, + 0x1b, 0xbc, 0x03, 0xdb, 0x4e, 0xb4, 0x20, 0x5b, 0x53, 0x51, 0x13, 0x54, 0x28, 0x20, 0xca, 0x9b, + 0x66, 0x14, 0x51, 0xa7, 0xbc, 0xf7, 0x19, 0x02, 0x12, 0xa1, 0x33, 0x62, 0x49, 0xa4, 0xda, 0x22, + 0xd1, 0x39, 0xff, 0xcc, 0xd4, 0x53, 0xc1, 0x22, 0x75, 0xdc, 0x39, 0xaf, 0xb0, 0xbd, 0xc6, 0xeb, + 0xda, 0xe6, 0x6e, 0xda, 0x80, 0xaf, 0x30, 0x4e, 0x0e, 0x88, 0xbe, 0x83, 0x07, 0x22, 0x4b, 0x60, + 0xd2, 0x6c, 0x0b, 0xbe, 0x51, 0x85, 0x51, 0x35, 0x4a, 0xa9, 0xfe, 0x71, 0x24, 0x80, 0xee, 0x94, + 0x27, 0x7f, 0x40, 0xc0, 0x33, 0xee, 0xa8, 0x66, 0x3c, 0x53, 0x1d, 0xe0, 0xd9, 0x39, 0x2f, 0x1f, + 0xc7, 0xfb, 0x02, 0x6f, 0x89, 0xd9, 0x2a, 0xde, 0xb5, 0x8b, 0xd0, 0x74, 0xc0, 0xe6, 0xb9, 0xf5, + 0x9b, 0x5e, 0x3d, 0xdf, 0x6e, 0x1b, 0xa0, 0xe3, 0xa1, 0xf0, 0xd1, 0x60, 0xb5, 0x5b, 0xb8, 0x5f, + 0xcc, 0xad, 0x92, 0xe5, 0xbf, 0x28, 0x52, 0x08, 0x29, 0x50, 0xde, 0x03, 0x8e, 0x79, 0xd3, 0x7c, + 0x1d, 0x19, 0xf9, 0x0b, 0x04, 0x44, 0x6a, 0xfa, 0x13, 0x89, 0xa4, 0xb6, 0x45, 0xa4, 0x73, 0x5e, + 0xbf, 0x09, 0x85, 0xd4, 0xa2, 0xe1, 0x70, 0xdb, 0x2f, 0x31, 0xab, 0x58, 0x6f, 0x1f, 0x9b, 0x95, + 0xa3, 0x43, 0xb8, 0xc7, 0x34, 0x56, 0x0d, 0x97, 0x9f, 0xbe, 0xa7, 0xe0, 0x3f, 0x28, 0x8f, 0x83, + 0x8a, 0xa9, 0x41, 0xe1, 0xeb, 0x32, 0x85, 0x82, 0xfb, 0x5d, 0xdb, 0xa5, 0x26, 0x1c, 0x04, 0x91, + 0x15, 0x7a, 0xa7, 0x5c, 0xc2, 0x93, 0x71, 0xa0, 0xee, 0x18, 0x6e, 0xa9, 0x40, 0x5d, 0xb6, 0xe8, + 0x41, 0x17, 0x02, 0x3e, 0x86, 0xd7, 0x0b, 0x84, 0x8f, 0x4a, 0xa8, 0x00, 0x92, 0xb7, 0xf1, 0xde, + 0xf0, 0x14, 0xa2, 0x2d, 0x9a, 0x9a, 0x48, 0x73, 0x1c, 0xef, 0xe1, 0x94, 0xd4, 0xb5, 0x64, 0x9e, + 0x5e, 0x3b, 0x5f, 0xa6, 0x2e, 0x53, 0x39, 0x66, 0x95, 0x55, 0x35, 0xc6, 0x8a, 0xac, 0x38, 0x9c, + 0x3a, 0x88, 0x26, 0x7b, 0x0b, 0x83, 0xe5, 0x00, 0xe7, 0x15, 0x58, 0xa8, 0xcd, 0x0e, 0xbc, 0xa4, + 0xe2, 0x75, 0xf9, 0xa1, 0x04, 0xa9, 0x9c, 0x09, 0x62, 0x23, 0xb2, 0x0a, 0x24, 0xf7, 0xe3, 0x9d, + 0x42, 0xca, 0x4e, 0x15, 0xe0, 0x49, 0x59, 0x86, 0x08, 0x98, 0xb7, 0xad, 0x07, 0xac, 0xec, 0xdd, + 0xd0, 0xcb, 0xb6, 0x27, 0xde, 0x90, 0x1d, 0x1a, 0x42, 0x2a, 0x8d, 0x7b, 0x75, 0xea, 0x2c, 0xd6, + 0xac, 0xbf, 0xbb, 0x50, 0x7b, 0x56, 0x3e, 0x41, 0x50, 0x57, 0x35, 0xaa, 0x05, 0x3c, 0xff, 0xc5, + 0x83, 0x76, 0xc5, 0x5d, 0xb1, 0x2b, 0x56, 0x71, 0x81, 0x3a, 0xd7, 0x2d, 0x6f, 0x31, 0x98, 0x64, + 0x34, 0x2c, 0x78, 0xbb, 0xf9, 0xfc, 0x44, 0xb3, 0xcd, 0xab, 0x8c, 0xc1, 0x6e, 0xff, 0xd0, 0xc6, + 0x05, 0x32, 0x89, 0x07, 0xbc, 0xbf, 0x62, 0xfe, 0x4e, 0x71, 0xfb, 0x47, 0x5f, 0x2b, 0x13, 0xf8, + 0x30, 0x87, 0x79, 0x83, 0x39, 0x0e, 0xd5, 0xd9, 0x12, 0x75, 0x1c, 0xc3, 0xd2, 0x97, 0xea, 0x1a, + 0x03, 0xeb, 0x5e, 0xc5, 0x47, 0x5a, 0x6d, 0x04, 0x62, 0x23, 0x78, 0xf7, 0xfb, 0x35, 0x88, 0x3e, + 0xa1, 0xfa, 0x8b, 0xe9, 0xbf, 0x46, 0x71, 0x0f, 0x57, 0x44, 0x9e, 0x22, 0xdc, 0x2f, 0xf6, 0xb3, + 0xe4, 0x7c, 0x8b, 0x70, 0x6b, 0x32, 0xca, 0x49, 0x5f, 0x68, 0x4b, 0xd6, 0x47, 0xac, 0x5c, 0x7c, + 0xf4, 0xe2, 0xcf, 0xc7, 0xdd, 0x67, 0xc9, 0x99, 0x9c, 0x27, 0x3a, 0x25, 0xcc, 0xe5, 0x6a, 0xc3, + 0xaf, 0x9a, 0x50, 0x6e, 0x03, 0x2e, 0x87, 0xcd, 0xdc, 0x06, 0xbf, 0x0e, 0x36, 0xc9, 0xd7, 0x08, + 0x0f, 0x88, 0x7a, 0xf3, 0xa6, 0x29, 0xc7, 0x25, 0x7e, 0xa0, 0x23, 0xc7, 0x25, 0x61, 0x48, 0xa3, + 0x1c, 0xe7, 0x5c, 0x0e, 0x93, 0x71, 0x09, 0x2e, 0xe4, 0x37, 0x84, 0xf7, 0x47, 0x90, 0x43, 0x5f, + 0x4d, 0xf2, 0x6d, 0x80, 0x08, 0x0f, 0x07, 0xd2, 0x73, 0xdb, 0x51, 0x01, 0x74, 0xce, 0x73, 0x3a, + 0xa7, 0xc9, 0xb4, 0x04, 0x1d, 0x90, 0x05, 0x0f, 0x6d, 0x92, 0x5f, 0x11, 0xfe, 0xa7, 0xd0, 0xbc, + 0x0a, 0xe4, 0x2e, 0x49, 0x22, 0x4b, 0x1c, 0x7c, 0xa4, 0xf3, 0xdb, 0xd0, 0x00, 0xd4, 0x66, 0x39, + 0xb5, 0x19, 0x72, 0x3a, 0x81, 0x9a, 0x61, 0x25, 0x30, 0x53, 0x8d, 0xe2, 0x26, 0xf9, 0x0a, 0xe1, + 0xbd, 0x61, 0x72, 0xd2, 0x31, 0x17, 0x33, 0x82, 0x90, 0x8e, 0xb9, 0xb8, 0xb1, 0x42, 0xcb, 0x98, + 0x13, 0x98, 0x38, 0xe4, 0x27, 0x00, 0x2e, 0xb4, 0x66, 0xb3, 0x92, 0x1f, 0x6f, 0x6c, 0x83, 0x9a, + 0xbe, 0xd8, 0xa6, 0x34, 0x80, 0xff, 0x1f, 0x07, 0x3f, 0x4d, 0x4e, 0x34, 0x01, 0x5f, 0x17, 0xcb, + 0x6d, 0x04, 0xcf, 0x9b, 0xe4, 0x67, 0x84, 0x49, 0x63, 0xcb, 0x4e, 0xa4, 0xf0, 0x24, 0x0e, 0x0a, + 0xd2, 0xff, 0x6f, 0x57, 0x1c, 0xf8, 0xe4, 0x39, 0x9f, 0x0b, 0xe4, 0x5c, 0x22, 0x9f, 0xe8, 0xbf, + 0x0c, 0xd4, 0x22, 0x75, 0xa9, 0x48, 0xec, 0x5b, 0x84, 0x07, 0xc3, 0x27, 0x78, 0xe1, 0x35, 0xbb, + 0x85, 0x10, 0x69, 0xd3, 0x4b, 0x89, 0xa3, 0x01, 0x65, 0x8a, 0xb3, 0x9a, 0x20, 0x87, 0xa5, 0xbc, + 0x44, 0x3e, 0x47, 0xf5, 0x96, 0x94, 0xcc, 0x48, 0x06, 0x48, 0xa4, 0x77, 0x4e, 0x9f, 0xdd, 0xb2, + 0x1c, 0x80, 0xcd, 0x71, 0xb0, 0x47, 0xc9, 0x44, 0x02, 0x58, 0x1d, 0x04, 0x3c, 0x9b, 0x17, 0x59, + 0x75, 0x93, 0x7c, 0x8a, 0x70, 0x5f, 0xa0, 0xc5, 0x33, 0xf5, 0x8c, 0xa4, 0xb1, 0xda, 0x42, 0x1c, + 0xd3, 0xc1, 0x2b, 0x13, 0x1c, 0xf1, 0x21, 0x32, 0xd6, 0x02, 0x31, 0x79, 0x82, 0xf0, 0x3f, 0xa2, + 0x25, 0x0d, 0x91, 0x4a, 0x1e, 0x09, 0xf5, 0x55, 0x7a, 0xb6, 0x3d, 0x61, 0x49, 0x53, 0x6b, 0x51, + 0xac, 0x4f, 0x11, 0xee, 0x13, 0xaa, 0x16, 0x72, 0x59, 0xe6, 0xf8, 0x56, 0xd5, 0x51, 0xfa, 0xca, + 0x36, 0xb5, 0x00, 0x9b, 0x63, 0x9c, 0xcd, 0x7f, 0x88, 0x92, 0xc0, 0x46, 0xa8, 0xf4, 0xc8, 0x33, + 0xd4, 0xd0, 0xa4, 0x13, 0xd9, 0x54, 0x18, 0x3f, 0x62, 0x90, 0x4b, 0x3d, 0xc9, 0xe3, 0x11, 0x65, + 0x86, 0xc3, 0x3f, 0x41, 0xb2, 0x09, 0xf0, 0xcd, 0xb0, 0x5c, 0x2d, 0xfc, 0xbf, 0x47, 0x98, 0x44, + 0x74, 0x7a, 0x5f, 0x81, 0x6c, 0xca, 0xd8, 0x0e, 0x9b, 0xe4, 0x21, 0x88, 0x92, 0xe5, 0x6c, 0x26, + 0xc9, 0x11, 0x39, 0x36, 0xe4, 0x63, 0x84, 0x77, 0xf0, 0xe4, 0x33, 0x2d, 0x69, 0x46, 0x31, 0x3d, + 0x9e, 0xda, 0x92, 0x8c, 0xe4, 0xbd, 0xab, 0xc1, 0x85, 0xc5, 0x8d, 0xfc, 0x25, 0xc2, 0x7d, 0xc2, + 0xf0, 0x83, 0x9c, 0xdb, 0xc2, 0x89, 0xe1, 0x81, 0x49, 0x7b, 0x60, 0xcf, 0x70, 0xb0, 0x39, 0x32, + 0xd5, 0x14, 0x6c, 0x43, 0x71, 0xfd, 0x11, 0xc2, 0xbb, 0x82, 0x1b, 0x68, 0x5a, 0xd2, 0xa3, 0x5b, + 0x36, 0x6c, 0x64, 0x00, 0xa2, 0x8c, 0x73, 0xac, 0xa3, 0xe4, 0x40, 0x13, 0xac, 0x5e, 0x05, 0x36, + 0xe0, 0x49, 0x79, 0x2d, 0x76, 0xd0, 0xd1, 0x4a, 0x95, 0x60, 0xf1, 0xc3, 0x0b, 0xb9, 0x12, 0x2c, + 0x61, 0x4e, 0xd1, 0x32, 0x73, 0x68, 0x75, 0x19, 0x5e, 0x3a, 0x86, 0xff, 0xc1, 0x2e, 0x17, 0x0c, + 0xb1, 0xff, 0xb2, 0x4f, 0x9f, 0x6f, 0x47, 0x54, 0xf2, 0x56, 0x7f, 0x18, 0x46, 0xe9, 0x01, 0x0f, + 0x77, 0xf7, 0x72, 0xc0, 0x63, 0xe7, 0x05, 0x72, 0xc0, 0xe3, 0x87, 0x09, 0x2d, 0x81, 0x9b, 0x21, + 0xb1, 0xb9, 0xb7, 0x9e, 0xbd, 0xcc, 0xa0, 0xe7, 0x2f, 0x33, 0xe8, 0x8f, 0x97, 0x19, 0xf4, 0xe1, + 0xab, 0x4c, 0xd7, 0xf3, 0x57, 0x99, 0xae, 0x5f, 0x5e, 0x65, 0xba, 0xee, 0x9e, 0xd4, 0x0d, 0xb7, + 0x54, 0x59, 0xc9, 0x6a, 0xf6, 0xaa, 0xa8, 0x2a, 0xc0, 0x93, 0xab, 0x8a, 0x5a, 0xdd, 0xf5, 0x35, + 0xe6, 0xac, 0xec, 0xe4, 0xb7, 0xc0, 0xa9, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x68, 0x29, 0xb5, + 0x4c, 0xa8, 0x22, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -4043,6 +4160,86 @@ func (m *QueryListCctxPendingResponse) MarshalToSizedBuffer(dAtA []byte) (int, e return len(dAtA) - i, nil } +func (m *QueryListCctxPendingWithRateLimitRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryListCctxPendingWithRateLimitRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryListCctxPendingWithRateLimitRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Limit != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x10 + } + return len(dAtA) - i, nil +} + +func (m *QueryListCctxPendingWithRateLimitResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryListCctxPendingWithRateLimitResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryListCctxPendingWithRateLimitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.RateLimitExceeded { + i-- + if m.RateLimitExceeded { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.TotalPending != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.TotalPending)) + i-- + dAtA[i] = 0x10 + } + if len(m.CrossChainTx) > 0 { + for iNdEx := len(m.CrossChainTx) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CrossChainTx[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *QueryLastZetaHeightRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -4723,6 +4920,39 @@ func (m *QueryListCctxPendingResponse) Size() (n int) { return n } +func (m *QueryListCctxPendingWithRateLimitRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Limit != 0 { + n += 1 + sovQuery(uint64(m.Limit)) + } + return n +} + +func (m *QueryListCctxPendingWithRateLimitResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.CrossChainTx) > 0 { + for _, e := range m.CrossChainTx { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.TotalPending != 0 { + n += 1 + sovQuery(uint64(m.TotalPending)) + } + if m.RateLimitExceeded { + n += 2 + } + return n +} + func (m *QueryLastZetaHeightRequest) Size() (n int) { if m == nil { return 0 @@ -7911,6 +8141,198 @@ func (m *QueryListCctxPendingResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryListCctxPendingWithRateLimitRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryListCctxPendingWithRateLimitRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryListCctxPendingWithRateLimitRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryListCctxPendingWithRateLimitResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryListCctxPendingWithRateLimitResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryListCctxPendingWithRateLimitResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CrossChainTx", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CrossChainTx = append(m.CrossChainTx, &CrossChainTx{}) + if err := m.CrossChainTx[len(m.CrossChainTx)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPending", wireType) + } + m.TotalPending = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPending |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RateLimitExceeded", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.RateLimitExceeded = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryLastZetaHeightRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/crosschain/types/rate_limiter_flags.pb.go b/x/crosschain/types/rate_limiter_flags.pb.go new file mode 100644 index 0000000000..ce24f8baf3 --- /dev/null +++ b/x/crosschain/types/rate_limiter_flags.pb.go @@ -0,0 +1,741 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: crosschain/rate_limiter_flags.proto + +package types + +import ( + encoding_binary "encoding/binary" + fmt "fmt" + io "io" + math "math" + math_bits "math/bits" + + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/gogo/protobuf/proto" + coin "github.com/zeta-chain/zetacore/pkg/coin" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ZRC20Rate defines the conversion rate of ZRC20 to ZETA +type ZRC20Rate struct { + ChainId int64 `protobuf:"varint,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + CoinType coin.CoinType `protobuf:"varint,2,opt,name=coin_type,json=coinType,proto3,enum=coin.CoinType" json:"coin_type,omitempty"` + Asset string `protobuf:"bytes,3,opt,name=asset,proto3" json:"asset,omitempty"` + ConversionRate float64 `protobuf:"fixed64,4,opt,name=conversion_rate,json=conversionRate,proto3" json:"conversion_rate,omitempty"` +} + +func (m *ZRC20Rate) Reset() { *m = ZRC20Rate{} } +func (m *ZRC20Rate) String() string { return proto.CompactTextString(m) } +func (*ZRC20Rate) ProtoMessage() {} +func (*ZRC20Rate) Descriptor() ([]byte, []int) { + return fileDescriptor_b17ae80d5af4e97e, []int{0} +} +func (m *ZRC20Rate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ZRC20Rate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ZRC20Rate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ZRC20Rate) XXX_Merge(src proto.Message) { + xxx_messageInfo_ZRC20Rate.Merge(m, src) +} +func (m *ZRC20Rate) XXX_Size() int { + return m.Size() +} +func (m *ZRC20Rate) XXX_DiscardUnknown() { + xxx_messageInfo_ZRC20Rate.DiscardUnknown(m) +} + +var xxx_messageInfo_ZRC20Rate proto.InternalMessageInfo + +func (m *ZRC20Rate) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *ZRC20Rate) GetCoinType() coin.CoinType { + if m != nil { + return m.CoinType + } + return coin.CoinType_Zeta +} + +func (m *ZRC20Rate) GetAsset() string { + if m != nil { + return m.Asset + } + return "" +} + +func (m *ZRC20Rate) GetConversionRate() float64 { + if m != nil { + return m.ConversionRate + } + return 0 +} + +// RateLimiterFlags defines the outbound rate limiter flags +type RateLimiterFlags struct { + IsEnabled bool `protobuf:"varint,1,opt,name=is_enabled,json=isEnabled,proto3" json:"is_enabled,omitempty"` + RateLimitWindow int64 `protobuf:"varint,2,opt,name=rate_limit_window,json=rateLimitWindow,proto3" json:"rate_limit_window,omitempty"` + RateLimitInZeta github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,3,opt,name=rate_limit_in_zeta,json=rateLimitInZeta,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"rate_limit_in_zeta"` + Zrc20Rates []*ZRC20Rate `protobuf:"bytes,4,rep,name=zrc20_rates,json=zrc20Rates,proto3" json:"zrc20_rates,omitempty"` +} + +func (m *RateLimiterFlags) Reset() { *m = RateLimiterFlags{} } +func (m *RateLimiterFlags) String() string { return proto.CompactTextString(m) } +func (*RateLimiterFlags) ProtoMessage() {} +func (*RateLimiterFlags) Descriptor() ([]byte, []int) { + return fileDescriptor_b17ae80d5af4e97e, []int{1} +} +func (m *RateLimiterFlags) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RateLimiterFlags) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RateLimiterFlags.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RateLimiterFlags) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimiterFlags.Merge(m, src) +} +func (m *RateLimiterFlags) XXX_Size() int { + return m.Size() +} +func (m *RateLimiterFlags) XXX_DiscardUnknown() { + xxx_messageInfo_RateLimiterFlags.DiscardUnknown(m) +} + +var xxx_messageInfo_RateLimiterFlags proto.InternalMessageInfo + +func (m *RateLimiterFlags) GetIsEnabled() bool { + if m != nil { + return m.IsEnabled + } + return false +} + +func (m *RateLimiterFlags) GetRateLimitWindow() int64 { + if m != nil { + return m.RateLimitWindow + } + return 0 +} + +func (m *RateLimiterFlags) GetZrc20Rates() []*ZRC20Rate { + if m != nil { + return m.Zrc20Rates + } + return nil +} + +func init() { + proto.RegisterType((*ZRC20Rate)(nil), "zetachain.zetacore.crosschain.ZRC20Rate") + proto.RegisterType((*RateLimiterFlags)(nil), "zetachain.zetacore.crosschain.RateLimiterFlags") +} + +func init() { + proto.RegisterFile("crosschain/rate_limiter_flags.proto", fileDescriptor_b17ae80d5af4e97e) +} + +var fileDescriptor_b17ae80d5af4e97e = []byte{ + // 416 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x41, 0x6b, 0x14, 0x31, + 0x14, 0xde, 0x74, 0xab, 0xee, 0xa6, 0xb0, 0xd5, 0xd8, 0xc3, 0x58, 0xe8, 0x74, 0xa8, 0x87, 0x0e, + 0x4a, 0x93, 0x3a, 0xfe, 0x83, 0x16, 0x85, 0x45, 0x4f, 0x41, 0x11, 0x16, 0x21, 0x64, 0x33, 0x71, + 0x1a, 0xba, 0x9b, 0x0c, 0x49, 0xb4, 0xb6, 0x3f, 0xc0, 0xb3, 0xf8, 0xab, 0x7a, 0xec, 0x51, 0x3c, + 0x14, 0xd9, 0xfd, 0x23, 0x92, 0xa4, 0xeb, 0xce, 0xa9, 0x97, 0x99, 0x2f, 0x8f, 0xf7, 0x7d, 0x7c, + 0xdf, 0x7b, 0x0f, 0x3e, 0x17, 0xd6, 0x38, 0x27, 0xce, 0xb8, 0xd2, 0xc4, 0x72, 0x2f, 0xd9, 0x4c, + 0xcd, 0x95, 0x97, 0x96, 0x7d, 0x99, 0xf1, 0xc6, 0xe1, 0xd6, 0x1a, 0x6f, 0xd0, 0xde, 0x95, 0xf4, + 0x3c, 0xf6, 0xe0, 0x88, 0x8c, 0x95, 0x78, 0xcd, 0xdb, 0xdd, 0x69, 0x4c, 0x63, 0x62, 0x27, 0x09, + 0x28, 0x91, 0x76, 0x9f, 0xb6, 0xe7, 0x0d, 0x11, 0x46, 0xe9, 0xf8, 0x49, 0xc5, 0x83, 0x5f, 0x00, + 0x0e, 0x27, 0xf4, 0xb4, 0x3a, 0xa6, 0xdc, 0x4b, 0xf4, 0x0c, 0x0e, 0xa2, 0x02, 0x53, 0x75, 0x06, + 0x0a, 0x50, 0xf6, 0xe9, 0xa3, 0xf8, 0x1e, 0xd7, 0xe8, 0x25, 0x1c, 0x06, 0x1a, 0xf3, 0x97, 0xad, + 0xcc, 0x36, 0x0a, 0x50, 0x8e, 0xaa, 0x11, 0x8e, 0x42, 0xa7, 0x46, 0xe9, 0x0f, 0x97, 0xad, 0xa4, + 0x03, 0x71, 0x87, 0xd0, 0x0e, 0x7c, 0xc0, 0x9d, 0x93, 0x3e, 0xeb, 0x17, 0xa0, 0x1c, 0xd2, 0xf4, + 0x40, 0x87, 0x70, 0x5b, 0x18, 0xfd, 0x4d, 0x5a, 0xa7, 0x8c, 0x66, 0x21, 0x5c, 0xb6, 0x59, 0x80, + 0x12, 0xd0, 0xd1, 0xba, 0x1c, 0x6c, 0x1c, 0xfc, 0xd8, 0x80, 0x8f, 0x03, 0x78, 0x9f, 0xa2, 0xbf, + 0x0d, 0xc9, 0xd1, 0x1e, 0x84, 0xca, 0x31, 0xa9, 0xf9, 0x74, 0x26, 0x93, 0xbb, 0x01, 0x1d, 0x2a, + 0xf7, 0x26, 0x15, 0xd0, 0x0b, 0xf8, 0x64, 0x3d, 0x2e, 0x76, 0xa1, 0x74, 0x6d, 0x2e, 0xa2, 0xcf, + 0x3e, 0xdd, 0xb6, 0x2b, 0xad, 0x4f, 0xb1, 0x8c, 0x3e, 0x43, 0xd4, 0xe9, 0x55, 0x9a, 0x85, 0x21, + 0x26, 0xaf, 0x27, 0xe4, 0xfa, 0x76, 0xbf, 0xf7, 0xe7, 0x76, 0xff, 0xb0, 0x51, 0xfe, 0xec, 0xeb, + 0x14, 0x0b, 0x33, 0x27, 0xc2, 0xb8, 0xb9, 0x71, 0x77, 0xbf, 0x23, 0x57, 0x9f, 0x93, 0x30, 0x05, + 0x87, 0x3f, 0x2a, 0xed, 0x3b, 0xea, 0x63, 0x3d, 0x91, 0x9e, 0xa3, 0x31, 0xdc, 0xba, 0xb2, 0xa2, + 0x3a, 0x8e, 0x09, 0x5d, 0xb6, 0x59, 0xf4, 0xcb, 0xad, 0xaa, 0xc4, 0xf7, 0xae, 0x0c, 0xff, 0xdf, + 0x01, 0x85, 0x91, 0x1c, 0xa0, 0x3b, 0x79, 0x77, 0xbd, 0xc8, 0xc1, 0xcd, 0x22, 0x07, 0x7f, 0x17, + 0x39, 0xf8, 0xb9, 0xcc, 0x7b, 0x37, 0xcb, 0xbc, 0xf7, 0x7b, 0x99, 0xf7, 0x26, 0xaf, 0x3a, 0xf6, + 0x82, 0xde, 0x51, 0xba, 0x98, 0x95, 0x34, 0xf9, 0x4e, 0x3a, 0x77, 0x14, 0xdd, 0x4e, 0x1f, 0xc6, + 0x8d, 0xbf, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0xba, 0xde, 0xfb, 0xd3, 0x62, 0x02, 0x00, 0x00, +} + +func (m *ZRC20Rate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ZRC20Rate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ZRC20Rate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ConversionRate != 0 { + i -= 8 + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.ConversionRate)))) + i-- + dAtA[i] = 0x21 + } + if len(m.Asset) > 0 { + i -= len(m.Asset) + copy(dAtA[i:], m.Asset) + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(len(m.Asset))) + i-- + dAtA[i] = 0x1a + } + if m.CoinType != 0 { + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.CoinType)) + i-- + dAtA[i] = 0x10 + } + if m.ChainId != 0 { + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *RateLimiterFlags) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RateLimiterFlags) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RateLimiterFlags) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Zrc20Rates) > 0 { + for iNdEx := len(m.Zrc20Rates) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Zrc20Rates[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + { + size := m.RateLimitInZeta.Size() + i -= size + if _, err := m.RateLimitInZeta.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.RateLimitWindow != 0 { + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.RateLimitWindow)) + i-- + dAtA[i] = 0x10 + } + if m.IsEnabled { + i-- + if m.IsEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintRateLimiterFlags(dAtA []byte, offset int, v uint64) int { + offset -= sovRateLimiterFlags(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ZRC20Rate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ChainId != 0 { + n += 1 + sovRateLimiterFlags(uint64(m.ChainId)) + } + if m.CoinType != 0 { + n += 1 + sovRateLimiterFlags(uint64(m.CoinType)) + } + l = len(m.Asset) + if l > 0 { + n += 1 + l + sovRateLimiterFlags(uint64(l)) + } + if m.ConversionRate != 0 { + n += 9 + } + return n +} + +func (m *RateLimiterFlags) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.IsEnabled { + n += 2 + } + if m.RateLimitWindow != 0 { + n += 1 + sovRateLimiterFlags(uint64(m.RateLimitWindow)) + } + l = m.RateLimitInZeta.Size() + n += 1 + l + sovRateLimiterFlags(uint64(l)) + if len(m.Zrc20Rates) > 0 { + for _, e := range m.Zrc20Rates { + l = e.Size() + n += 1 + l + sovRateLimiterFlags(uint64(l)) + } + } + return n +} + +func sovRateLimiterFlags(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozRateLimiterFlags(x uint64) (n int) { + return sovRateLimiterFlags(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ZRC20Rate) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ZRC20Rate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ZRC20Rate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CoinType", wireType) + } + m.CoinType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CoinType |= coin.CoinType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Asset", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + 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 ErrInvalidLengthRateLimiterFlags + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRateLimiterFlags + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Asset = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field ConversionRate", wireType) + } + var v uint64 + if (iNdEx + 8) > l { + return io.ErrUnexpectedEOF + } + v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) + iNdEx += 8 + m.ConversionRate = float64(math.Float64frombits(v)) + default: + iNdEx = preIndex + skippy, err := skipRateLimiterFlags(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRateLimiterFlags + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RateLimiterFlags) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RateLimiterFlags: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RateLimiterFlags: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsEnabled = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RateLimitWindow", wireType) + } + m.RateLimitWindow = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RateLimitWindow |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RateLimitInZeta", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + 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 ErrInvalidLengthRateLimiterFlags + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRateLimiterFlags + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.RateLimitInZeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Zrc20Rates", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRateLimiterFlags + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRateLimiterFlags + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Zrc20Rates = append(m.Zrc20Rates, &ZRC20Rate{}) + if err := m.Zrc20Rates[len(m.Zrc20Rates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRateLimiterFlags(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRateLimiterFlags + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipRateLimiterFlags(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthRateLimiterFlags + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupRateLimiterFlags + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthRateLimiterFlags + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthRateLimiterFlags = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowRateLimiterFlags = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupRateLimiterFlags = fmt.Errorf("proto: unexpected end of group") +) From a873173ecdc96f6e839c515f55593bdb8b33cc65 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 18 Apr 2024 13:03:52 -0500 Subject: [PATCH 02/40] replace big.Float with sdk.Dec and update mock rate limiter flags --- docs/openapi/openapi.swagger.yaml | 34 ++ proto/crosschain/query.proto | 5 + proto/crosschain/rate_limiter_flags.proto | 32 +- testutil/keeper/mocks/crosschain/fungible.go | 20 + .../crosschain/rate_limiter_flags_pb.d.ts | 77 ++- x/crosschain/keeper/grpc_query_cctx.go | 101 ++-- x/crosschain/keeper/grpc_query_cctx_test.go | 30 ++ x/crosschain/keeper/rate_limiter_flags.go | 55 +-- x/crosschain/types/expected_keepers.go | 1 + x/crosschain/types/query.pb.go | 268 ++++++----- x/crosschain/types/query.pb.gw.go | 83 ++++ x/crosschain/types/rate_limiter_flags.pb.go | 439 ++++++++---------- x/fungible/keeper/foreign_coins.go | 18 + 13 files changed, 685 insertions(+), 478 deletions(-) diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 1d1042551f..c39cbb7644 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -26608,6 +26608,27 @@ paths: format: int64 tags: - Query + /zeta-chain/crosschain/cctxPendingWithRateLimit: + get: + summary: Queries a list of pending cctxs with rate limit. + operationId: Query_CctxListPendingWithinRateLimit + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/crosschainQueryListCctxPendingWithRateLimitResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: limit + in: query + required: false + type: integer + format: int64 + tags: + - Query /zeta-chain/crosschain/convertGasToZeta: get: operationId: Query_ConvertGasToZeta @@ -53901,6 +53922,19 @@ definitions: totalPending: type: string format: uint64 + crosschainQueryListCctxPendingWithRateLimitResponse: + type: object + properties: + cross_chain_tx: + type: array + items: + type: object + $ref: '#/definitions/crosschainCrossChainTx' + total_pending: + type: string + format: uint64 + rate_limit_exceeded: + type: boolean crosschainQueryMessagePassingProtocolFeeResponse: type: object properties: diff --git a/proto/crosschain/query.proto b/proto/crosschain/query.proto index 5372c9b0c9..89f80247da 100644 --- a/proto/crosschain/query.proto +++ b/proto/crosschain/query.proto @@ -98,6 +98,11 @@ service Query { option (google.api.http).get = "/zeta-chain/crosschain/cctxPending"; } + // Queries a list of pending cctxs with rate limit. + rpc CctxListPendingWithinRateLimit(QueryListCctxPendingWithRateLimitRequest) returns (QueryListCctxPendingWithRateLimitResponse) { + option (google.api.http).get = "/zeta-chain/crosschain/cctxPendingWithRateLimit"; + } + rpc ZetaAccounting(QueryZetaAccountingRequest) returns (QueryZetaAccountingResponse) { option (google.api.http).get = "/zeta-chain/crosschain/zetaAccounting"; } diff --git a/proto/crosschain/rate_limiter_flags.proto b/proto/crosschain/rate_limiter_flags.proto index af9edabcbb..0e8185713b 100644 --- a/proto/crosschain/rate_limiter_flags.proto +++ b/proto/crosschain/rate_limiter_flags.proto @@ -2,25 +2,29 @@ syntax = "proto3"; package zetachain.zetacore.crosschain; import "gogoproto/gogo.proto"; -import "pkg/coin/coin.proto"; option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types"; -// ZRC20Rate defines the conversion rate of ZRC20 to ZETA -message ZRC20Rate { - int64 chain_id = 1; - coin.CoinType coin_type = 2; - string asset = 3; - double conversion_rate = 4; -} - -// RateLimiterFlags defines the outbound rate limiter flags message RateLimiterFlags { - bool is_enabled = 1; - int64 rate_limit_window = 2; - string rate_limit_in_zeta = 3 [ + bool enabled = 1; + + // window in blocks + int64 window = 2; + + // rate in azeta per block + string rate = 3 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", (gogoproto.nullable) = false ]; - repeated ZRC20Rate zrc20_rates = 4; + + // conversion in azeta per token + repeated Conversion conversions = 4 [(gogoproto.nullable) = false]; +} + +message Conversion { + string zrc20 = 1; + string rate = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; } diff --git a/testutil/keeper/mocks/crosschain/fungible.go b/testutil/keeper/mocks/crosschain/fungible.go index 54b7e54a03..f1e624ca9b 100644 --- a/testutil/keeper/mocks/crosschain/fungible.go +++ b/testutil/keeper/mocks/crosschain/fungible.go @@ -254,6 +254,26 @@ func (_m *CrosschainFungibleKeeper) GetAllForeignCoinsForChain(ctx types.Context return r0 } +// GetAllForeignERC20CoinMap provides a mock function with given fields: ctx +func (_m *CrosschainFungibleKeeper) GetAllForeignERC20CoinMap(ctx types.Context) map[int64]map[string]fungibletypes.ForeignCoins { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetAllForeignERC20CoinMap") + } + + var r0 map[int64]map[string]fungibletypes.ForeignCoins + if rf, ok := ret.Get(0).(func(types.Context) map[int64]map[string]fungibletypes.ForeignCoins); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[int64]map[string]fungibletypes.ForeignCoins) + } + } + + return r0 +} + // GetForeignCoinFromAsset provides a mock function with given fields: ctx, asset, chainID func (_m *CrosschainFungibleKeeper) GetForeignCoinFromAsset(ctx types.Context, asset string, chainID int64) (fungibletypes.ForeignCoins, bool) { ret := _m.Called(ctx, asset, chainID) diff --git a/typescript/crosschain/rate_limiter_flags_pb.d.ts b/typescript/crosschain/rate_limiter_flags_pb.d.ts index b95e353e94..02997cd099 100644 --- a/typescript/crosschain/rate_limiter_flags_pb.d.ts +++ b/typescript/crosschain/rate_limiter_flags_pb.d.ts @@ -5,87 +5,78 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; -import type { CoinType } from "../pkg/coin/coin_pb.js"; /** - * ZRC20Rate defines the conversion rate of ZRC20 to ZETA - * - * @generated from message zetachain.zetacore.crosschain.ZRC20Rate + * @generated from message zetachain.zetacore.crosschain.RateLimiterFlags */ -export declare class ZRC20Rate extends Message { +export declare class RateLimiterFlags extends Message { /** - * @generated from field: int64 chain_id = 1; + * @generated from field: bool enabled = 1; */ - chainId: bigint; + enabled: boolean; /** - * @generated from field: coin.CoinType coin_type = 2; + * window in blocks + * + * @generated from field: int64 window = 2; */ - coinType: CoinType; + window: bigint; /** - * @generated from field: string asset = 3; + * rate in azeta per block + * + * @generated from field: string rate = 3; */ - asset: string; + rate: string; /** - * @generated from field: double conversion_rate = 4; + * conversion in azeta per token + * + * @generated from field: repeated zetachain.zetacore.crosschain.Conversion conversions = 4; */ - conversionRate: number; + conversions: Conversion[]; - constructor(data?: PartialMessage); + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.ZRC20Rate"; + static readonly typeName = "zetachain.zetacore.crosschain.RateLimiterFlags"; static readonly fields: FieldList; - static fromBinary(bytes: Uint8Array, options?: Partial): ZRC20Rate; + static fromBinary(bytes: Uint8Array, options?: Partial): RateLimiterFlags; - static fromJson(jsonValue: JsonValue, options?: Partial): ZRC20Rate; + static fromJson(jsonValue: JsonValue, options?: Partial): RateLimiterFlags; - static fromJsonString(jsonString: string, options?: Partial): ZRC20Rate; + static fromJsonString(jsonString: string, options?: Partial): RateLimiterFlags; - static equals(a: ZRC20Rate | PlainMessage | undefined, b: ZRC20Rate | PlainMessage | undefined): boolean; + static equals(a: RateLimiterFlags | PlainMessage | undefined, b: RateLimiterFlags | PlainMessage | undefined): boolean; } /** - * RateLimiterFlags defines the outbound rate limiter flags - * - * @generated from message zetachain.zetacore.crosschain.RateLimiterFlags + * @generated from message zetachain.zetacore.crosschain.Conversion */ -export declare class RateLimiterFlags extends Message { - /** - * @generated from field: bool is_enabled = 1; - */ - isEnabled: boolean; - - /** - * @generated from field: int64 rate_limit_window = 2; - */ - rateLimitWindow: bigint; - +export declare class Conversion extends Message { /** - * @generated from field: string rate_limit_in_zeta = 3; + * @generated from field: string zrc20 = 1; */ - rateLimitInZeta: string; + zrc20: string; /** - * @generated from field: repeated zetachain.zetacore.crosschain.ZRC20Rate zrc20_rates = 4; + * @generated from field: string rate = 2; */ - zrc20Rates: ZRC20Rate[]; + rate: string; - constructor(data?: PartialMessage); + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.RateLimiterFlags"; + static readonly typeName = "zetachain.zetacore.crosschain.Conversion"; static readonly fields: FieldList; - static fromBinary(bytes: Uint8Array, options?: Partial): RateLimiterFlags; + static fromBinary(bytes: Uint8Array, options?: Partial): Conversion; - static fromJson(jsonValue: JsonValue, options?: Partial): RateLimiterFlags; + static fromJson(jsonValue: JsonValue, options?: Partial): Conversion; - static fromJsonString(jsonString: string, options?: Partial): RateLimiterFlags; + static fromJsonString(jsonString: string, options?: Partial): Conversion; - static equals(a: RateLimiterFlags | PlainMessage | undefined, b: RateLimiterFlags | PlainMessage | undefined): boolean; + static equals(a: Conversion | PlainMessage | undefined, b: Conversion | PlainMessage | undefined): boolean; } diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index 1fe7d04e24..ac3d2da63a 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -3,7 +3,6 @@ package keeper import ( "context" "fmt" - "math/big" "strings" "github.com/cosmos/cosmos-sdk/store/prefix" @@ -11,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/query" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -208,29 +208,32 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que // check rate limit flags to decide if we should apply rate limit applyLimit := true rateLimitFlags, found := k.GetRatelimiterFlags(ctx) - if !found || !rateLimitFlags.IsEnabled { + if !found || !rateLimitFlags.Enabled { applyLimit = false } // calculate the rate limiter sliding window left boundary (inclusive) - leftWindowBoundary := height - rateLimitFlags.RateLimitWindow + leftWindowBoundary := height - rateLimitFlags.Window if leftWindowBoundary < 0 { leftWindowBoundary = 0 } // get the conversion rates for all foreign coins - var gasCoinRates map[int64]*big.Float - var erc20CoinRates map[string]*big.Float - var rateLimitInZeta *big.Float + var gasCoinRates map[int64]sdk.Dec + var erc20CoinRates map[int64]map[string]sdk.Dec + var erc20Coins map[int64]map[string]fungibletypes.ForeignCoins + var rateLimitInZeta sdk.Dec if applyLimit { gasCoinRates, erc20CoinRates = k.GetRatelimiterRates(ctx) - rateLimitInZeta = new(big.Float).SetInt(rateLimitFlags.RateLimitInZeta.BigInt()) + erc20Coins = k.fungibleKeeper.GetAllForeignERC20CoinMap(ctx) + //rateLimitInZeta = new(big.Float).SetInt(rateLimitFlags.RateLimitInZeta.BigInt()) + rateLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()) } // define a few variables to be used in the below loops limitExceeded := false totalPending := uint64(0) - totalCctxValueInZeta := big.NewFloat(0) + totalCctxValueInZeta := sdk.NewDec(0) cctxs := make([]*types.CrossChainTx, 0) // the criteria to stop adding cctxs to the result @@ -243,6 +246,11 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que chains := k.zetaObserverKeeper.GetSupportedChains(ctx) ChainLoop: for _, chain := range chains { + // skip zeta chain + if chain.IsZetaChain() { + continue + } + // get pending nonces for this chain pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) if !found { @@ -283,7 +291,7 @@ ChainLoop: break } // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded - if rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, totalCctxValueInZeta, rateLimitInZeta) { + if rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true break ChainLoop } @@ -315,10 +323,10 @@ ChainLoop: // only take a `limit` number of pending cctxs as result if maxCCTXsReached() { - break + break ChainLoop } // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded - if applyLimit && rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, totalCctxValueInZeta, rateLimitInZeta) { + if applyLimit && rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true break ChainLoop } @@ -337,32 +345,60 @@ ChainLoop: func convertCctxValue( chainID int64, cctx *types.CrossChainTx, - gasCoinRates map[int64]*big.Float, - erc20CoinRates map[string]*big.Float, -) *big.Float { - var rate *big.Float + gasCoinRates map[int64]sdk.Dec, + erc20CoinRates map[int64]map[string]sdk.Dec, + erc20Coins map[int64]map[string]fungibletypes.ForeignCoins, +) sdk.Dec { + var rate sdk.Dec + var decimals uint64 switch cctx.InboundTxParams.CoinType { case coin.CoinType_Zeta: // no conversion needed for ZETA - rate = big.NewFloat(1.0) + rate = sdk.NewDec(1) case coin.CoinType_Gas: - // convert gas coin amount into ZETA rate = gasCoinRates[chainID] case coin.CoinType_ERC20: - // convert erc20 coin amount into ZETA - rate = erc20CoinRates[strings.ToLower(cctx.InboundTxParams.Asset)] + // get the ERC20 coin decimals + _, found := erc20Coins[chainID] + if !found { + // skip if no coin found for this chainID + return sdk.NewDec(0) + } + fCoin, found := erc20Coins[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] + if !found { + // skip if no coin found for this Asset + return sdk.NewDec(0) + } + // #nosec G701 always in range + decimals = uint64(fCoin.Decimals) + + // get the ERC20 coin rate + _, found = erc20CoinRates[chainID] + if !found { + // skip if no rate found for this chainID + return sdk.NewDec(0) + } + rate = erc20CoinRates[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] default: // skip CoinType_Cmd - return big.NewFloat(0) + return sdk.NewDec(0) } - if rate == nil || rate.Cmp(big.NewFloat(0)) == 0 { - // should not happen, return 0 to skip this cctx - return big.NewFloat(0) + + // should not happen, return 0 to skip if it happens + if rate.LTE(sdk.NewDec(0)) { + return sdk.NewDec(0) } + // calculate the reciprocal of `rate` as the amount of zrc20 needed to buy 1 ZETA + // for example, given rate = 0.8, the reciprocal is 1.25, which means 1.25 ZRC20 can buy 1 ZETA + // given decimals = 6, below calculation is equivalent to 1,000,000 / 1.25 = 800,000 + // which means 800,000 ZRC20 can buy 1 ZETA + oneZrc20 := sdk.NewDec(1).Power(decimals) + oneZeta := oneZrc20.Quo(rate) + // convert asset amount into ZETA - amountCctx := new(big.Float).SetInt(cctx.InboundTxParams.Amount.BigInt()) - amountZeta := new(big.Float).Mul(amountCctx, rate) + amountCctx := sdk.NewDecFromBigInt(cctx.InboundTxParams.Amount.BigInt()) + amountZeta := amountCctx.Quo(oneZeta) return amountZeta } @@ -371,12 +407,13 @@ func convertCctxValue( func rateLimitExceeded( chainID int64, cctx *types.CrossChainTx, - gasCoinRates map[int64]*big.Float, - erc20CoinRates map[string]*big.Float, - currentCctxValue *big.Float, - rateLimitValue *big.Float, + gasCoinRates map[int64]sdk.Dec, + erc20CoinRates map[int64]map[string]sdk.Dec, + erc20Coins map[int64]map[string]fungibletypes.ForeignCoins, + currentCctxValue *sdk.Dec, + rateLimitValue sdk.Dec, ) bool { - amountZeta := convertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates) - currentCctxValue.Add(currentCctxValue, amountZeta) - return currentCctxValue.Cmp(rateLimitValue) > 0 + amountZeta := convertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates, erc20Coins) + *currentCctxValue = currentCctxValue.Add(amountZeta) + return currentCctxValue.GT(rateLimitValue) } diff --git a/x/crosschain/keeper/grpc_query_cctx_test.go b/x/crosschain/keeper/grpc_query_cctx_test.go index 59b0b68863..883315c0a0 100644 --- a/x/crosschain/keeper/grpc_query_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_test.go @@ -185,6 +185,36 @@ func TestKeeper_CctxListPending(t *testing.T) { }) } +func TestKeeper_CctxListPendingWithRateLimit(t *testing.T) { + t.Run("should fail for empty req", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + _, err := k.CctxListPendingWithinRateLimit(ctx, nil) + require.ErrorContains(t, err, "invalid request") + }) + + t.Run("should fail if limit is too high", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: keeper.MaxPendingCctxs + 1}) + require.ErrorContains(t, err, "limit exceeds max limit of") + }) + + t.Run("should fail if no TSS", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: 1}) + require.ErrorContains(t, err, "tss not found") + }) + + t.Run("should return empty list if no nonces", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // set TSS + zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) + + _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: 1}) + require.ErrorContains(t, err, "pending nonces not found") + }) +} + func TestKeeper_ZetaAccounting(t *testing.T) { t.Run("should error if zeta accounting not found", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) diff --git a/x/crosschain/keeper/rate_limiter_flags.go b/x/crosschain/keeper/rate_limiter_flags.go index f5cd88cb99..6af2908d6f 100644 --- a/x/crosschain/keeper/rate_limiter_flags.go +++ b/x/crosschain/keeper/rate_limiter_flags.go @@ -1,43 +1,36 @@ package keeper import ( - "math/big" "strings" "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" ) // hardcoded rate limiter flags var rateLimitFlags = types.RateLimiterFlags{ - IsEnabled: true, - RateLimitWindow: 1200, // 1200 zeta blocks, 2 hours - RateLimitInZeta: math.NewUint(2000000), // 2,000,000 ZETA - Zrc20Rates: []*types.ZRC20Rate{ + Enabled: true, + Window: 100, // 100 zeta blocks, 10 minutes + Rate: math.NewUint(2000000), // 2000 ZETA + Conversions: []types.Conversion{ // ETH { - ChainId: chains.GoerliLocalnetChain().ChainId, - CoinType: coin.CoinType_Gas, - Asset: "", - ConversionRate: 2500, + Zrc20: "0x13A0c5930C028511Dc02665E7285134B6d11A5f4", + Rate: sdk.NewDec(2500), }, // USDT { - ChainId: chains.GoerliLocalnetChain().ChainId, - CoinType: coin.CoinType_ERC20, - Asset: "0xbD1e64A22B9F92D9Ce81aA9B4b0fFacd80215564", - ConversionRate: 0.8, + Zrc20: "0xbD1e64A22B9F92D9Ce81aA9B4b0fFacd80215564", + Rate: sdk.MustNewDecFromStr("0.8"), + //sdk.NewDec(0.8), }, // BTC { - ChainId: chains.BtcRegtestChain().ChainId, - CoinType: coin.CoinType_ERC20, - Asset: "0x8f56682c2b8b2e3d4f6f7f7d6f3c01b3f6f6a7d6", - ConversionRate: 50000, + Zrc20: "0x8f56682c2b8b2e3d4f6f7f7d6f3c01b3f6f6a7d6", + Rate: sdk.NewDec(50000), }, }, } @@ -65,22 +58,30 @@ func (k Keeper) GetRatelimiterFlags(_ sdk.Context) (val types.RateLimiterFlags, // GetRatelimiterRates returns two maps of foreign coins and their rates // The 1st map: foreign chain id -> gas coin rate -// The 2nd map: foreign erc20 asset -> erc20 coin rate -func (k Keeper) GetRatelimiterRates(ctx sdk.Context) (map[int64]*big.Float, map[string]*big.Float) { +// The 2nd map: foreign chain id -> erc20 asset -> erc20 coin rate +func (k Keeper) GetRatelimiterRates(ctx sdk.Context) (map[int64]sdk.Dec, map[int64]map[string]sdk.Dec) { rateLimitFlags, _ := k.GetRatelimiterFlags(ctx) // the result maps - gasCoinRates := make(map[int64]*big.Float) - erc20CoinRates := make(map[string]*big.Float) + gasCoinRates := make(map[int64]sdk.Dec) + erc20CoinRates := make(map[int64]map[string]sdk.Dec) // loop through the rate limiter flags to get the rate - for _, zrc20Rate := range rateLimitFlags.Zrc20Rates { - rate := big.NewFloat(zrc20Rate.ConversionRate) - switch zrc20Rate.CoinType { + for _, conversion := range rateLimitFlags.Conversions { + fCoin, found := k.fungibleKeeper.GetForeignCoins(ctx, conversion.Zrc20) + if !found { + continue + } + + chainID := fCoin.ForeignChainId + switch fCoin.CoinType { case coin.CoinType_Gas: - gasCoinRates[zrc20Rate.ChainId] = rate + gasCoinRates[chainID] = conversion.Rate case coin.CoinType_ERC20: - erc20CoinRates[strings.ToLower(zrc20Rate.Asset)] = rate + if _, found := erc20CoinRates[chainID]; !found { + erc20CoinRates[chainID] = make(map[string]sdk.Dec) + } + erc20CoinRates[chainID][strings.ToLower(fCoin.Asset)] = conversion.Rate } } return gasCoinRates, erc20CoinRates diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index c985a01915..2e02f3bb32 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -96,6 +96,7 @@ type ObserverKeeper interface { type FungibleKeeper interface { GetForeignCoins(ctx sdk.Context, zrc20Addr string) (val fungibletypes.ForeignCoins, found bool) GetAllForeignCoins(ctx sdk.Context) (list []fungibletypes.ForeignCoins) + GetAllForeignERC20CoinMap(ctx sdk.Context) map[int64]map[string]fungibletypes.ForeignCoins SetForeignCoins(ctx sdk.Context, foreignCoins fungibletypes.ForeignCoins) GetAllForeignCoinsForChain(ctx sdk.Context, foreignChainID int64) (list []fungibletypes.ForeignCoins) GetForeignCoinFromAsset(ctx sdk.Context, asset string, chainID int64) (fungibletypes.ForeignCoins, bool) diff --git a/x/crosschain/types/query.pb.go b/x/crosschain/types/query.pb.go index 0a8a73f0e4..a5c1d6db31 100644 --- a/x/crosschain/types/query.pb.go +++ b/x/crosschain/types/query.pb.go @@ -2018,120 +2018,122 @@ func init() { func init() { proto.RegisterFile("crosschain/query.proto", fileDescriptor_65a992045e92a606) } var fileDescriptor_65a992045e92a606 = []byte{ - // 1797 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0x41, 0x6f, 0x14, 0xc7, - 0x12, 0x76, 0x7b, 0x31, 0x98, 0xb6, 0xc1, 0xcf, 0x8d, 0x1f, 0xcf, 0x6f, 0xb1, 0xd7, 0x30, 0x7e, - 0x60, 0x03, 0xcf, 0xbb, 0x60, 0xc0, 0x3c, 0xc0, 0x3c, 0xb1, 0x36, 0x60, 0x50, 0x0c, 0x98, 0x95, - 0x23, 0x22, 0xa2, 0x68, 0xd4, 0x9e, 0xed, 0xcc, 0x8e, 0x18, 0xcf, 0x98, 0x9d, 0x59, 0xb4, 0xc6, - 0xf2, 0x85, 0x43, 0xce, 0x91, 0x38, 0xe4, 0x92, 0x6b, 0x94, 0x1c, 0x72, 0xc8, 0x21, 0x4a, 0x0e, - 0x91, 0x88, 0xa2, 0x24, 0x84, 0x23, 0x12, 0x52, 0x14, 0x25, 0x52, 0x14, 0x41, 0x7e, 0x41, 0x7e, - 0x41, 0x34, 0x3d, 0x35, 0xbb, 0x3d, 0xb3, 0x33, 0xbb, 0xed, 0xf5, 0x72, 0xe0, 0xe4, 0x9d, 0xe9, - 0xae, 0xea, 0xef, 0xab, 0xaa, 0xa9, 0xae, 0x2a, 0xe3, 0xfd, 0x5a, 0xd9, 0x76, 0x1c, 0xad, 0x44, - 0x0d, 0x2b, 0x77, 0xbf, 0xc2, 0xca, 0xeb, 0xd9, 0xb5, 0xb2, 0xed, 0xda, 0x64, 0xf4, 0x21, 0x73, - 0x29, 0x7f, 0x9d, 0xe5, 0xbf, 0xec, 0x32, 0xcb, 0xd6, 0xb7, 0xa6, 0x8f, 0x69, 0xb6, 0xb3, 0x6a, - 0x3b, 0xb9, 0x15, 0xea, 0x30, 0x5f, 0x2e, 0xf7, 0xe0, 0xe4, 0x0a, 0x73, 0xe9, 0xc9, 0xdc, 0x1a, - 0xd5, 0x0d, 0x8b, 0xba, 0x86, 0x6d, 0xf9, 0xaa, 0xd2, 0x63, 0xc2, 0x11, 0xfc, 0xa7, 0xca, 0x7f, - 0xab, 0x6e, 0x15, 0x36, 0xa4, 0x85, 0x0d, 0x3a, 0x75, 0xd4, 0xb5, 0xb2, 0xa1, 0x31, 0x58, 0x1b, - 0x17, 0xd6, 0xb8, 0x8c, 0x5a, 0xa2, 0x4e, 0x49, 0x75, 0x6d, 0x55, 0xd3, 0x6a, 0x0a, 0x32, 0x0d, - 0x9b, 0xdc, 0x32, 0xd5, 0xee, 0xb1, 0x32, 0xac, 0x2b, 0xc2, 0xba, 0x49, 0x1d, 0x57, 0x5d, 0x31, - 0x6d, 0xed, 0x9e, 0x5a, 0x62, 0x86, 0x5e, 0x72, 0x63, 0x50, 0xda, 0x15, 0xb7, 0x51, 0xc9, 0x90, - 0x6e, 0xeb, 0x36, 0xff, 0x99, 0xf3, 0x7e, 0xc1, 0xdb, 0x11, 0xdd, 0xb6, 0x75, 0x93, 0xe5, 0xe8, - 0x9a, 0x91, 0xa3, 0x96, 0x65, 0xbb, 0x9c, 0xb9, 0xe3, 0xaf, 0x2a, 0x23, 0x38, 0x7d, 0xdb, 0x33, - 0xce, 0x5d, 0xe6, 0xd2, 0xbc, 0xa6, 0xd9, 0x15, 0xcb, 0x35, 0x2c, 0xbd, 0xc0, 0xee, 0x57, 0x98, - 0xe3, 0x2a, 0x37, 0xf0, 0x81, 0xd8, 0x55, 0x67, 0xcd, 0xb6, 0x1c, 0x46, 0xb2, 0x78, 0x1f, 0x5d, - 0xb1, 0xcb, 0x2e, 0x2b, 0xaa, 0x9e, 0x0b, 0x54, 0xba, 0xea, 0xed, 0x18, 0x46, 0x07, 0xd1, 0xe4, - 0xee, 0xc2, 0x20, 0x2c, 0x71, 0x59, 0xbe, 0x50, 0x53, 0xb7, 0xc0, 0xdc, 0x5b, 0x15, 0x77, 0xb9, - 0xba, 0xec, 0xc3, 0x87, 0xd3, 0xc8, 0x30, 0xde, 0xc5, 0xd9, 0x5d, 0xbf, 0xcc, 0x55, 0xa4, 0x0a, - 0xc1, 0x23, 0x19, 0xc2, 0x3d, 0x96, 0x6d, 0x69, 0x6c, 0xb8, 0xfb, 0x20, 0x9a, 0xdc, 0x51, 0xf0, - 0x1f, 0x94, 0x0a, 0x1e, 0x89, 0x57, 0x07, 0xf0, 0xde, 0xc6, 0xfd, 0xb6, 0xf0, 0x9e, 0x2b, 0xed, - 0x9b, 0x3e, 0x9e, 0x6d, 0x1a, 0x38, 0x59, 0x51, 0xd5, 0xdc, 0x8e, 0x67, 0xbf, 0x8f, 0x75, 0x15, - 0x42, 0x6a, 0x14, 0x06, 0x2c, 0xf2, 0xa6, 0x19, 0xc7, 0xe2, 0x2a, 0xc6, 0xf5, 0x00, 0x83, 0x33, - 0x8f, 0x64, 0xfd, 0x68, 0xcc, 0x7a, 0xd1, 0x98, 0xf5, 0xa3, 0x18, 0xa2, 0x31, 0xbb, 0x44, 0x75, - 0x06, 0xb2, 0x05, 0x41, 0x52, 0x79, 0x82, 0x80, 0x5e, 0xc3, 0x39, 0x89, 0xf4, 0x52, 0x1d, 0xa0, - 0x47, 0x16, 0x42, 0xf8, 0xbb, 0x39, 0xfe, 0x89, 0x96, 0xf8, 0x7d, 0x4c, 0x21, 0x02, 0x8f, 0x10, - 0x56, 0xe2, 0x08, 0xcc, 0xad, 0xcf, 0x7b, 0x48, 0x02, 0x7b, 0x0d, 0xe1, 0x1e, 0x8e, 0x0c, 0x7c, - 0xee, 0x3f, 0x44, 0xac, 0xd8, 0xdd, 0xb6, 0x15, 0x7f, 0x44, 0x78, 0xbc, 0x29, 0x88, 0x37, 0xc4, - 0x98, 0x1f, 0x20, 0x7c, 0x28, 0xe0, 0x71, 0xdd, 0x4a, 0xb2, 0xe5, 0xbf, 0x71, 0xaf, 0x9f, 0xb9, - 0x8c, 0x62, 0xf8, 0x13, 0x2a, 0x76, 0xcc, 0xa0, 0xdf, 0x09, 0x5e, 0x8d, 0x03, 0x02, 0xf6, 0x2c, - 0xe0, 0x3e, 0xc3, 0x8a, 0x9a, 0xf3, 0x58, 0x0b, 0x73, 0x8a, 0xfa, 0x7c, 0x6b, 0x8a, 0x4a, 0x3a, - 0x67, 0x4c, 0xe1, 0x0b, 0x16, 0x8e, 0x74, 0x3a, 0xfd, 0x05, 0x7f, 0x23, 0x7c, 0xc1, 0xe1, 0x73, - 0xde, 0x04, 0x23, 0x5d, 0xc0, 0xa3, 0x41, 0x76, 0xf5, 0x8e, 0xbc, 0x46, 0x9d, 0xd2, 0xb2, 0x3d, - 0xaf, 0xb9, 0xd5, 0xc0, 0x4c, 0x69, 0xdc, 0x6b, 0xc0, 0x02, 0xa4, 0xfc, 0xda, 0xb3, 0xb2, 0x89, - 0x33, 0x49, 0xc2, 0xc0, 0xfd, 0x5d, 0xbc, 0xd7, 0x08, 0xad, 0x80, 0xa1, 0xa7, 0x24, 0xe8, 0xd7, - 0x85, 0xc0, 0x02, 0x11, 0x55, 0xca, 0x2c, 0x1c, 0x1f, 0xde, 0x7c, 0x99, 0xba, 0x54, 0x06, 0xfc, - 0x43, 0x3c, 0x96, 0x28, 0x0d, 0xe8, 0xef, 0xe0, 0x3d, 0xf3, 0x1e, 0x26, 0x1e, 0xf4, 0xcb, 0x55, - 0x47, 0x32, 0x5f, 0x88, 0x32, 0x00, 0x3d, 0xac, 0x47, 0xd1, 0xc1, 0xea, 0x10, 0x32, 0x8d, 0x56, - 0xef, 0x54, 0x70, 0x3e, 0x45, 0x60, 0xa3, 0x98, 0x93, 0x9a, 0xb8, 0x28, 0xd5, 0x21, 0x17, 0x75, - 0x2e, 0x4e, 0x73, 0xf8, 0x5f, 0x41, 0xa8, 0x2d, 0x50, 0x67, 0xc9, 0xab, 0xcc, 0x84, 0xab, 0xc5, - 0xb0, 0x8a, 0xac, 0x0a, 0x1e, 0xf6, 0x1f, 0x14, 0x15, 0x0f, 0x37, 0x0a, 0x00, 0xe5, 0x79, 0xdc, - 0x1b, 0xbc, 0x03, 0xdb, 0x4e, 0xb4, 0x20, 0x5b, 0x53, 0x51, 0x13, 0x54, 0x28, 0x20, 0xca, 0x9b, - 0x66, 0x14, 0x51, 0xa7, 0xbc, 0xf7, 0x19, 0x02, 0x12, 0xa1, 0x33, 0x62, 0x49, 0xa4, 0xda, 0x22, - 0xd1, 0x39, 0xff, 0xcc, 0xd4, 0x53, 0xc1, 0x22, 0x75, 0xdc, 0x39, 0xaf, 0xb0, 0xbd, 0xc6, 0xeb, - 0xda, 0xe6, 0x6e, 0xda, 0x80, 0xaf, 0x30, 0x4e, 0x0e, 0x88, 0xbe, 0x83, 0x07, 0x22, 0x4b, 0x60, - 0xd2, 0x6c, 0x0b, 0xbe, 0x51, 0x85, 0x51, 0x35, 0x4a, 0xa9, 0xfe, 0x71, 0x24, 0x80, 0xee, 0x94, - 0x27, 0x7f, 0x40, 0xc0, 0x33, 0xee, 0xa8, 0x66, 0x3c, 0x53, 0x1d, 0xe0, 0xd9, 0x39, 0x2f, 0x1f, - 0xc7, 0xfb, 0x02, 0x6f, 0x89, 0xd9, 0x2a, 0xde, 0xb5, 0x8b, 0xd0, 0x74, 0xc0, 0xe6, 0xb9, 0xf5, - 0x9b, 0x5e, 0x3d, 0xdf, 0x6e, 0x1b, 0xa0, 0xe3, 0xa1, 0xf0, 0xd1, 0x60, 0xb5, 0x5b, 0xb8, 0x5f, - 0xcc, 0xad, 0x92, 0xe5, 0xbf, 0x28, 0x52, 0x08, 0x29, 0x50, 0xde, 0x03, 0x8e, 0x79, 0xd3, 0x7c, - 0x1d, 0x19, 0xf9, 0x0b, 0x04, 0x44, 0x6a, 0xfa, 0x13, 0x89, 0xa4, 0xb6, 0x45, 0xa4, 0x73, 0x5e, - 0xbf, 0x09, 0x85, 0xd4, 0xa2, 0xe1, 0x70, 0xdb, 0x2f, 0x31, 0xab, 0x58, 0x6f, 0x1f, 0x9b, 0x95, - 0xa3, 0x43, 0xb8, 0xc7, 0x34, 0x56, 0x0d, 0x97, 0x9f, 0xbe, 0xa7, 0xe0, 0x3f, 0x28, 0x8f, 0x83, - 0x8a, 0xa9, 0x41, 0xe1, 0xeb, 0x32, 0x85, 0x82, 0xfb, 0x5d, 0xdb, 0xa5, 0x26, 0x1c, 0x04, 0x91, - 0x15, 0x7a, 0xa7, 0x5c, 0xc2, 0x93, 0x71, 0xa0, 0xee, 0x18, 0x6e, 0xa9, 0x40, 0x5d, 0xb6, 0xe8, - 0x41, 0x17, 0x02, 0x3e, 0x86, 0xd7, 0x0b, 0x84, 0x8f, 0x4a, 0xa8, 0x00, 0x92, 0xb7, 0xf1, 0xde, - 0xf0, 0x14, 0xa2, 0x2d, 0x9a, 0x9a, 0x48, 0x73, 0x1c, 0xef, 0xe1, 0x94, 0xd4, 0xb5, 0x64, 0x9e, - 0x5e, 0x3b, 0x5f, 0xa6, 0x2e, 0x53, 0x39, 0x66, 0x95, 0x55, 0x35, 0xc6, 0x8a, 0xac, 0x38, 0x9c, - 0x3a, 0x88, 0x26, 0x7b, 0x0b, 0x83, 0xe5, 0x00, 0xe7, 0x15, 0x58, 0xa8, 0xcd, 0x0e, 0xbc, 0xa4, - 0xe2, 0x75, 0xf9, 0xa1, 0x04, 0xa9, 0x9c, 0x09, 0x62, 0x23, 0xb2, 0x0a, 0x24, 0xf7, 0xe3, 0x9d, - 0x42, 0xca, 0x4e, 0x15, 0xe0, 0x49, 0x59, 0x86, 0x08, 0x98, 0xb7, 0xad, 0x07, 0xac, 0xec, 0xdd, - 0xd0, 0xcb, 0xb6, 0x27, 0xde, 0x90, 0x1d, 0x1a, 0x42, 0x2a, 0x8d, 0x7b, 0x75, 0xea, 0x2c, 0xd6, - 0xac, 0xbf, 0xbb, 0x50, 0x7b, 0x56, 0x3e, 0x41, 0x50, 0x57, 0x35, 0xaa, 0x05, 0x3c, 0xff, 0xc5, - 0x83, 0x76, 0xc5, 0x5d, 0xb1, 0x2b, 0x56, 0x71, 0x81, 0x3a, 0xd7, 0x2d, 0x6f, 0x31, 0x98, 0x64, - 0x34, 0x2c, 0x78, 0xbb, 0xf9, 0xfc, 0x44, 0xb3, 0xcd, 0xab, 0x8c, 0xc1, 0x6e, 0xff, 0xd0, 0xc6, - 0x05, 0x32, 0x89, 0x07, 0xbc, 0xbf, 0x62, 0xfe, 0x4e, 0x71, 0xfb, 0x47, 0x5f, 0x2b, 0x13, 0xf8, - 0x30, 0x87, 0x79, 0x83, 0x39, 0x0e, 0xd5, 0xd9, 0x12, 0x75, 0x1c, 0xc3, 0xd2, 0x97, 0xea, 0x1a, - 0x03, 0xeb, 0x5e, 0xc5, 0x47, 0x5a, 0x6d, 0x04, 0x62, 0x23, 0x78, 0xf7, 0xfb, 0x35, 0x88, 0x3e, - 0xa1, 0xfa, 0x8b, 0xe9, 0xbf, 0x46, 0x71, 0x0f, 0x57, 0x44, 0x9e, 0x22, 0xdc, 0x2f, 0xf6, 0xb3, - 0xe4, 0x7c, 0x8b, 0x70, 0x6b, 0x32, 0xca, 0x49, 0x5f, 0x68, 0x4b, 0xd6, 0x47, 0xac, 0x5c, 0x7c, - 0xf4, 0xe2, 0xcf, 0xc7, 0xdd, 0x67, 0xc9, 0x99, 0x9c, 0x27, 0x3a, 0x25, 0xcc, 0xe5, 0x6a, 0xc3, - 0xaf, 0x9a, 0x50, 0x6e, 0x03, 0x2e, 0x87, 0xcd, 0xdc, 0x06, 0xbf, 0x0e, 0x36, 0xc9, 0xd7, 0x08, - 0x0f, 0x88, 0x7a, 0xf3, 0xa6, 0x29, 0xc7, 0x25, 0x7e, 0xa0, 0x23, 0xc7, 0x25, 0x61, 0x48, 0xa3, - 0x1c, 0xe7, 0x5c, 0x0e, 0x93, 0x71, 0x09, 0x2e, 0xe4, 0x37, 0x84, 0xf7, 0x47, 0x90, 0x43, 0x5f, - 0x4d, 0xf2, 0x6d, 0x80, 0x08, 0x0f, 0x07, 0xd2, 0x73, 0xdb, 0x51, 0x01, 0x74, 0xce, 0x73, 0x3a, - 0xa7, 0xc9, 0xb4, 0x04, 0x1d, 0x90, 0x05, 0x0f, 0x6d, 0x92, 0x5f, 0x11, 0xfe, 0xa7, 0xd0, 0xbc, - 0x0a, 0xe4, 0x2e, 0x49, 0x22, 0x4b, 0x1c, 0x7c, 0xa4, 0xf3, 0xdb, 0xd0, 0x00, 0xd4, 0x66, 0x39, - 0xb5, 0x19, 0x72, 0x3a, 0x81, 0x9a, 0x61, 0x25, 0x30, 0x53, 0x8d, 0xe2, 0x26, 0xf9, 0x0a, 0xe1, - 0xbd, 0x61, 0x72, 0xd2, 0x31, 0x17, 0x33, 0x82, 0x90, 0x8e, 0xb9, 0xb8, 0xb1, 0x42, 0xcb, 0x98, - 0x13, 0x98, 0x38, 0xe4, 0x27, 0x00, 0x2e, 0xb4, 0x66, 0xb3, 0x92, 0x1f, 0x6f, 0x6c, 0x83, 0x9a, - 0xbe, 0xd8, 0xa6, 0x34, 0x80, 0xff, 0x1f, 0x07, 0x3f, 0x4d, 0x4e, 0x34, 0x01, 0x5f, 0x17, 0xcb, - 0x6d, 0x04, 0xcf, 0x9b, 0xe4, 0x67, 0x84, 0x49, 0x63, 0xcb, 0x4e, 0xa4, 0xf0, 0x24, 0x0e, 0x0a, - 0xd2, 0xff, 0x6f, 0x57, 0x1c, 0xf8, 0xe4, 0x39, 0x9f, 0x0b, 0xe4, 0x5c, 0x22, 0x9f, 0xe8, 0xbf, - 0x0c, 0xd4, 0x22, 0x75, 0xa9, 0x48, 0xec, 0x5b, 0x84, 0x07, 0xc3, 0x27, 0x78, 0xe1, 0x35, 0xbb, - 0x85, 0x10, 0x69, 0xd3, 0x4b, 0x89, 0xa3, 0x01, 0x65, 0x8a, 0xb3, 0x9a, 0x20, 0x87, 0xa5, 0xbc, - 0x44, 0x3e, 0x47, 0xf5, 0x96, 0x94, 0xcc, 0x48, 0x06, 0x48, 0xa4, 0x77, 0x4e, 0x9f, 0xdd, 0xb2, - 0x1c, 0x80, 0xcd, 0x71, 0xb0, 0x47, 0xc9, 0x44, 0x02, 0x58, 0x1d, 0x04, 0x3c, 0x9b, 0x17, 0x59, - 0x75, 0x93, 0x7c, 0x8a, 0x70, 0x5f, 0xa0, 0xc5, 0x33, 0xf5, 0x8c, 0xa4, 0xb1, 0xda, 0x42, 0x1c, - 0xd3, 0xc1, 0x2b, 0x13, 0x1c, 0xf1, 0x21, 0x32, 0xd6, 0x02, 0x31, 0x79, 0x82, 0xf0, 0x3f, 0xa2, - 0x25, 0x0d, 0x91, 0x4a, 0x1e, 0x09, 0xf5, 0x55, 0x7a, 0xb6, 0x3d, 0x61, 0x49, 0x53, 0x6b, 0x51, - 0xac, 0x4f, 0x11, 0xee, 0x13, 0xaa, 0x16, 0x72, 0x59, 0xe6, 0xf8, 0x56, 0xd5, 0x51, 0xfa, 0xca, - 0x36, 0xb5, 0x00, 0x9b, 0x63, 0x9c, 0xcd, 0x7f, 0x88, 0x92, 0xc0, 0x46, 0xa8, 0xf4, 0xc8, 0x33, - 0xd4, 0xd0, 0xa4, 0x13, 0xd9, 0x54, 0x18, 0x3f, 0x62, 0x90, 0x4b, 0x3d, 0xc9, 0xe3, 0x11, 0x65, - 0x86, 0xc3, 0x3f, 0x41, 0xb2, 0x09, 0xf0, 0xcd, 0xb0, 0x5c, 0x2d, 0xfc, 0xbf, 0x47, 0x98, 0x44, - 0x74, 0x7a, 0x5f, 0x81, 0x6c, 0xca, 0xd8, 0x0e, 0x9b, 0xe4, 0x21, 0x88, 0x92, 0xe5, 0x6c, 0x26, - 0xc9, 0x11, 0x39, 0x36, 0xe4, 0x63, 0x84, 0x77, 0xf0, 0xe4, 0x33, 0x2d, 0x69, 0x46, 0x31, 0x3d, - 0x9e, 0xda, 0x92, 0x8c, 0xe4, 0xbd, 0xab, 0xc1, 0x85, 0xc5, 0x8d, 0xfc, 0x25, 0xc2, 0x7d, 0xc2, - 0xf0, 0x83, 0x9c, 0xdb, 0xc2, 0x89, 0xe1, 0x81, 0x49, 0x7b, 0x60, 0xcf, 0x70, 0xb0, 0x39, 0x32, - 0xd5, 0x14, 0x6c, 0x43, 0x71, 0xfd, 0x11, 0xc2, 0xbb, 0x82, 0x1b, 0x68, 0x5a, 0xd2, 0xa3, 0x5b, - 0x36, 0x6c, 0x64, 0x00, 0xa2, 0x8c, 0x73, 0xac, 0xa3, 0xe4, 0x40, 0x13, 0xac, 0x5e, 0x05, 0x36, - 0xe0, 0x49, 0x79, 0x2d, 0x76, 0xd0, 0xd1, 0x4a, 0x95, 0x60, 0xf1, 0xc3, 0x0b, 0xb9, 0x12, 0x2c, - 0x61, 0x4e, 0xd1, 0x32, 0x73, 0x68, 0x75, 0x19, 0x5e, 0x3a, 0x86, 0xff, 0xc1, 0x2e, 0x17, 0x0c, - 0xb1, 0xff, 0xb2, 0x4f, 0x9f, 0x6f, 0x47, 0x54, 0xf2, 0x56, 0x7f, 0x18, 0x46, 0xe9, 0x01, 0x0f, - 0x77, 0xf7, 0x72, 0xc0, 0x63, 0xe7, 0x05, 0x72, 0xc0, 0xe3, 0x87, 0x09, 0x2d, 0x81, 0x9b, 0x21, - 0xb1, 0xb9, 0xb7, 0x9e, 0xbd, 0xcc, 0xa0, 0xe7, 0x2f, 0x33, 0xe8, 0x8f, 0x97, 0x19, 0xf4, 0xe1, - 0xab, 0x4c, 0xd7, 0xf3, 0x57, 0x99, 0xae, 0x5f, 0x5e, 0x65, 0xba, 0xee, 0x9e, 0xd4, 0x0d, 0xb7, - 0x54, 0x59, 0xc9, 0x6a, 0xf6, 0xaa, 0xa8, 0x2a, 0xc0, 0x93, 0xab, 0x8a, 0x5a, 0xdd, 0xf5, 0x35, - 0xe6, 0xac, 0xec, 0xe4, 0xb7, 0xc0, 0xa9, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x68, 0x29, 0xb5, - 0x4c, 0xa8, 0x22, 0x00, 0x00, + // 1833 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0x41, 0x6f, 0xd4, 0xd6, + 0x16, 0xce, 0xcd, 0x10, 0x08, 0x37, 0x81, 0xbc, 0x5c, 0xf2, 0x78, 0x79, 0x26, 0x4c, 0xc0, 0x79, + 0x90, 0x00, 0x2f, 0x63, 0x12, 0x20, 0x3c, 0x20, 0x3c, 0x31, 0x09, 0x10, 0xd0, 0x0b, 0x10, 0x46, + 0x79, 0xa2, 0xa2, 0xaa, 0x2c, 0xc7, 0x73, 0xeb, 0xb1, 0x70, 0xec, 0x30, 0xf6, 0xa0, 0x09, 0x51, + 0x36, 0x2c, 0xba, 0xae, 0xc4, 0xa2, 0x9b, 0x6e, 0xab, 0x76, 0xd1, 0x45, 0x17, 0x55, 0xbb, 0xa8, + 0x44, 0x55, 0xb5, 0xa5, 0x2c, 0x91, 0x90, 0xaa, 0xaa, 0x95, 0xaa, 0x16, 0xba, 0xec, 0x8f, 0xa8, + 0x7c, 0x7d, 0x3c, 0x73, 0xed, 0xb1, 0x67, 0x6e, 0x26, 0xc3, 0x82, 0x15, 0x73, 0x7d, 0xef, 0x39, + 0xe7, 0xfb, 0xce, 0x39, 0x3e, 0x3e, 0xf7, 0x10, 0xbc, 0x5f, 0x2f, 0x3b, 0xae, 0xab, 0x97, 0x34, + 0xd3, 0x56, 0xee, 0x57, 0x68, 0x79, 0x3d, 0xb7, 0x56, 0x76, 0x3c, 0x87, 0x1c, 0x7c, 0x48, 0x3d, + 0x8d, 0x3d, 0xce, 0xb1, 0x5f, 0x4e, 0x99, 0xe6, 0xea, 0x47, 0xa5, 0xe3, 0xba, 0xe3, 0xae, 0x3a, + 0xae, 0xb2, 0xa2, 0xb9, 0x34, 0x90, 0x53, 0x1e, 0x4c, 0xad, 0x50, 0x4f, 0x9b, 0x52, 0xd6, 0x34, + 0xc3, 0xb4, 0x35, 0xcf, 0x74, 0xec, 0x40, 0x95, 0x34, 0xca, 0x99, 0x60, 0x3f, 0x55, 0xf6, 0x5b, + 0xf5, 0xaa, 0x70, 0x40, 0xe2, 0x0e, 0x18, 0x9a, 0xab, 0xae, 0x95, 0x4d, 0x9d, 0xc2, 0xde, 0x18, + 0xb7, 0xc7, 0x64, 0xd4, 0x92, 0xe6, 0x96, 0x54, 0xcf, 0x51, 0x75, 0xbd, 0xa6, 0x20, 0xdb, 0x70, + 0xc8, 0x2b, 0x6b, 0xfa, 0x3d, 0x5a, 0x86, 0x7d, 0x99, 0xdb, 0xb7, 0x34, 0xd7, 0x53, 0x57, 0x2c, + 0x47, 0xbf, 0xa7, 0x96, 0xa8, 0x69, 0x94, 0xbc, 0x04, 0x94, 0x4e, 0xc5, 0x6b, 0x54, 0x32, 0x64, + 0x38, 0x86, 0xc3, 0x7e, 0x2a, 0xfe, 0x2f, 0x78, 0x3a, 0x62, 0x38, 0x8e, 0x61, 0x51, 0x45, 0x5b, + 0x33, 0x15, 0xcd, 0xb6, 0x1d, 0x8f, 0x31, 0x77, 0x83, 0x5d, 0x79, 0x04, 0x4b, 0xb7, 0x7d, 0xe7, + 0xdc, 0xa5, 0x9e, 0x96, 0xd7, 0x75, 0xa7, 0x62, 0x7b, 0xa6, 0x6d, 0x14, 0xe8, 0xfd, 0x0a, 0x75, + 0x3d, 0xf9, 0x06, 0x3e, 0x90, 0xb8, 0xeb, 0xae, 0x39, 0xb6, 0x4b, 0x49, 0x0e, 0xef, 0xd3, 0x56, + 0x9c, 0xb2, 0x47, 0x8b, 0xaa, 0x1f, 0x02, 0x55, 0x5b, 0xf5, 0x4f, 0x0c, 0xa3, 0x43, 0x68, 0x62, + 0x77, 0x61, 0x10, 0xb6, 0x98, 0x2c, 0xdb, 0xa8, 0xa9, 0x5b, 0xa0, 0xde, 0xad, 0x8a, 0xb7, 0x5c, + 0x5d, 0x0e, 0xe0, 0x83, 0x35, 0x32, 0x8c, 0x77, 0x31, 0x76, 0xd7, 0x2f, 0x33, 0x15, 0x99, 0x42, + 0xb8, 0x24, 0x43, 0xb8, 0xc7, 0x76, 0x6c, 0x9d, 0x0e, 0x77, 0x1f, 0x42, 0x13, 0x3b, 0x0a, 0xc1, + 0x42, 0xae, 0xe0, 0x91, 0x64, 0x75, 0x00, 0xef, 0xff, 0xb8, 0xdf, 0xe1, 0x9e, 0x33, 0xa5, 0x7d, + 0xd3, 0x27, 0x72, 0x4d, 0x13, 0x27, 0xc7, 0xab, 0x9a, 0xdb, 0xf1, 0xec, 0xd7, 0xd1, 0xae, 0x42, + 0x44, 0x8d, 0x4c, 0x81, 0x45, 0xde, 0xb2, 0x92, 0x58, 0x5c, 0xc5, 0xb8, 0x9e, 0x60, 0x60, 0xf3, + 0x68, 0x2e, 0xc8, 0xc6, 0x9c, 0x9f, 0x8d, 0xb9, 0x20, 0x8b, 0x21, 0x1b, 0x73, 0x4b, 0x9a, 0x41, + 0x41, 0xb6, 0xc0, 0x49, 0xca, 0x4f, 0x10, 0xd0, 0x6b, 0xb0, 0x93, 0x4a, 0x2f, 0xd3, 0x01, 0x7a, + 0x64, 0x21, 0x82, 0xbf, 0x9b, 0xe1, 0x1f, 0x6f, 0x89, 0x3f, 0xc0, 0x14, 0x21, 0xf0, 0x08, 0x61, + 0x39, 0x89, 0xc0, 0xdc, 0xfa, 0xbc, 0x8f, 0x24, 0xf4, 0xd7, 0x10, 0xee, 0x61, 0xc8, 0x20, 0xe6, + 0xc1, 0x22, 0xe6, 0xc5, 0xee, 0xb6, 0xbd, 0xf8, 0x3d, 0xc2, 0x63, 0x4d, 0x41, 0xbc, 0x21, 0xce, + 0x7c, 0x0f, 0xe1, 0xc3, 0x21, 0x8f, 0xeb, 0x76, 0x9a, 0x2f, 0xff, 0x89, 0x7b, 0x83, 0xca, 0x65, + 0x16, 0xa3, 0xaf, 0x50, 0xb1, 0x63, 0x0e, 0xfd, 0x86, 0x8b, 0x6a, 0x12, 0x10, 0xf0, 0x67, 0x01, + 0xf7, 0x99, 0x76, 0xdc, 0x9d, 0xc7, 0x5b, 0xb8, 0x93, 0xd7, 0x17, 0x78, 0x93, 0x57, 0xd2, 0x39, + 0x67, 0x72, 0x6f, 0x30, 0x67, 0xd2, 0xed, 0xf4, 0x1b, 0xfc, 0x15, 0xf7, 0x06, 0x47, 0xed, 0xbc, + 0x09, 0x4e, 0xba, 0x80, 0x0f, 0x86, 0xd5, 0xd5, 0x37, 0x79, 0x4d, 0x73, 0x4b, 0xcb, 0xce, 0xbc, + 0xee, 0x55, 0x43, 0x37, 0x49, 0xb8, 0xd7, 0x84, 0x0d, 0x28, 0xf9, 0xb5, 0xb5, 0xbc, 0x89, 0xb3, + 0x69, 0xc2, 0xc0, 0xfd, 0x6d, 0xbc, 0xd7, 0x8c, 0xec, 0x80, 0xa3, 0x27, 0x05, 0xe8, 0xd7, 0x85, + 0xc0, 0x03, 0x31, 0x55, 0xf2, 0x2c, 0x98, 0x8f, 0x1e, 0xbe, 0xac, 0x79, 0x9a, 0x08, 0xf8, 0x87, + 0x78, 0x34, 0x55, 0x1a, 0xd0, 0xdf, 0xc1, 0x7b, 0xe6, 0x7d, 0x4c, 0x2c, 0xe9, 0x97, 0xab, 0xae, + 0x60, 0xbd, 0xe0, 0x65, 0x00, 0x7a, 0x54, 0x8f, 0x6c, 0x80, 0xd7, 0x21, 0x65, 0x1a, 0xbd, 0xde, + 0xa9, 0xe4, 0x7c, 0x8a, 0xc0, 0x47, 0x09, 0x96, 0x9a, 0x84, 0x28, 0xd3, 0xa1, 0x10, 0x75, 0x2e, + 0x4f, 0x15, 0xfc, 0x8f, 0x30, 0xd5, 0x16, 0x34, 0x77, 0xc9, 0xef, 0xcc, 0xb8, 0x4f, 0x8b, 0x69, + 0x17, 0x69, 0x15, 0x22, 0x1c, 0x2c, 0x64, 0x15, 0x0f, 0x37, 0x0a, 0x00, 0xe5, 0x79, 0xdc, 0x1b, + 0x3e, 0x03, 0xdf, 0x8e, 0xb7, 0x20, 0x5b, 0x53, 0x51, 0x13, 0x94, 0x35, 0x40, 0x94, 0xb7, 0xac, + 0x38, 0xa2, 0x4e, 0x45, 0xef, 0x13, 0x04, 0x24, 0x22, 0x36, 0x12, 0x49, 0x64, 0xda, 0x22, 0xd1, + 0xb9, 0xf8, 0xcc, 0xd4, 0x4b, 0xc1, 0xa2, 0xe6, 0x7a, 0x73, 0x7e, 0x63, 0x7b, 0x8d, 0xf5, 0xb5, + 0xcd, 0xc3, 0xb4, 0x01, 0x6f, 0x61, 0x92, 0x1c, 0x10, 0x7d, 0x0b, 0x0f, 0xc4, 0xb6, 0xc0, 0xa5, + 0xb9, 0x16, 0x7c, 0xe3, 0x0a, 0xe3, 0x6a, 0xe4, 0x52, 0xfd, 0xe5, 0x48, 0x01, 0xdd, 0xa9, 0x48, + 0x7e, 0x87, 0x80, 0x67, 0x92, 0xa9, 0x66, 0x3c, 0x33, 0x1d, 0xe0, 0xd9, 0xb9, 0x28, 0x9f, 0xc0, + 0xfb, 0xc2, 0x68, 0xf1, 0xd5, 0x2a, 0x39, 0xb4, 0x8b, 0x70, 0xe9, 0x80, 0xc3, 0x73, 0xeb, 0x37, + 0xfd, 0x7e, 0xbe, 0xdd, 0x6b, 0x80, 0x81, 0x87, 0xa2, 0xa6, 0xc1, 0x6b, 0xb7, 0x70, 0x3f, 0x5f, + 0x5b, 0x05, 0xdb, 0x7f, 0x5e, 0xa4, 0x10, 0x51, 0x20, 0xbf, 0x03, 0x1c, 0xf3, 0x96, 0xf5, 0x3a, + 0x2a, 0xf2, 0x67, 0x08, 0x88, 0xd4, 0xf4, 0xa7, 0x12, 0xc9, 0x6c, 0x8b, 0x48, 0xe7, 0xa2, 0x7e, + 0x13, 0x1a, 0xa9, 0x45, 0xd3, 0x65, 0xbe, 0x5f, 0xa2, 0x76, 0xb1, 0x7e, 0x7d, 0x6c, 0xd6, 0x8e, + 0x0e, 0xe1, 0x1e, 0xcb, 0x5c, 0x35, 0x3d, 0x66, 0x7d, 0x4f, 0x21, 0x58, 0xc8, 0x8f, 0xc3, 0x8e, + 0xa9, 0x41, 0xe1, 0xeb, 0x72, 0x85, 0x8c, 0xfb, 0x3d, 0xc7, 0xd3, 0x2c, 0x30, 0x04, 0x99, 0x15, + 0x79, 0x26, 0x5f, 0xc2, 0x13, 0x49, 0xa0, 0xee, 0x98, 0x5e, 0xa9, 0xa0, 0x79, 0x74, 0xd1, 0x87, + 0xce, 0x25, 0x7c, 0x02, 0xaf, 0x17, 0x08, 0x1f, 0x13, 0x50, 0x01, 0x24, 0x6f, 0xe3, 0xbd, 0xd1, + 0x29, 0x44, 0x5b, 0x34, 0x75, 0x9e, 0xe6, 0x18, 0xde, 0xc3, 0x28, 0xa9, 0x6b, 0xe9, 0x3c, 0xfd, + 0xeb, 0x7c, 0x59, 0xf3, 0xa8, 0xca, 0x30, 0xab, 0xb4, 0xaa, 0x53, 0x5a, 0xa4, 0xc5, 0xe1, 0xcc, + 0x21, 0x34, 0xd1, 0x5b, 0x18, 0x2c, 0x87, 0x38, 0xaf, 0xc0, 0x46, 0x6d, 0x76, 0xe0, 0x17, 0x15, + 0xff, 0x96, 0x1f, 0x29, 0x90, 0xf2, 0x99, 0x30, 0x37, 0x62, 0xbb, 0x40, 0x72, 0x3f, 0xde, 0xc9, + 0x95, 0xec, 0x4c, 0x01, 0x56, 0xf2, 0x32, 0x64, 0xc0, 0xbc, 0x63, 0x3f, 0xa0, 0x65, 0xff, 0x0b, + 0xbd, 0xec, 0xf8, 0xe2, 0x0d, 0xd5, 0xa1, 0x21, 0xa5, 0x24, 0xdc, 0x6b, 0x68, 0xee, 0x62, 0xcd, + 0xfb, 0xbb, 0x0b, 0xb5, 0xb5, 0xfc, 0x11, 0x82, 0xbe, 0xaa, 0x51, 0x2d, 0xe0, 0xf9, 0x37, 0x1e, + 0x74, 0x2a, 0xde, 0x8a, 0x53, 0xb1, 0x8b, 0x0b, 0x9a, 0x7b, 0xdd, 0xf6, 0x37, 0xc3, 0x49, 0x46, + 0xc3, 0x86, 0x7f, 0x9a, 0xcd, 0x4f, 0x74, 0xc7, 0xba, 0x4a, 0x29, 0x9c, 0x0e, 0x8c, 0x36, 0x6e, + 0x90, 0x09, 0x3c, 0xe0, 0xff, 0xcb, 0xd7, 0xef, 0x0c, 0xf3, 0x7f, 0xfc, 0xb1, 0x3c, 0x8e, 0x8f, + 0x30, 0x98, 0x37, 0xa8, 0xeb, 0x6a, 0x06, 0x5d, 0xd2, 0x5c, 0xd7, 0xb4, 0x8d, 0xa5, 0xba, 0xc6, + 0xd0, 0xbb, 0x57, 0xf1, 0xd1, 0x56, 0x07, 0x81, 0xd8, 0x08, 0xde, 0xfd, 0x6e, 0x0d, 0x62, 0x40, + 0xa8, 0xfe, 0x60, 0xfa, 0xf7, 0x51, 0xdc, 0xc3, 0x14, 0x91, 0xa7, 0x08, 0xf7, 0xf3, 0xf7, 0x59, + 0x72, 0xbe, 0x45, 0xba, 0x35, 0x19, 0xe5, 0x48, 0x17, 0xda, 0x92, 0x0d, 0x10, 0xcb, 0x17, 0x1f, + 0xbd, 0xf8, 0xe3, 0x71, 0xf7, 0x59, 0x72, 0x46, 0xf1, 0x45, 0x27, 0xb9, 0xb9, 0x5c, 0x6d, 0xf8, + 0x55, 0x13, 0x52, 0x36, 0xe0, 0xe3, 0xb0, 0xa9, 0x6c, 0xb0, 0xcf, 0xc1, 0x26, 0xf9, 0x12, 0xe1, + 0x01, 0x5e, 0x6f, 0xde, 0xb2, 0xc4, 0xb8, 0x24, 0x0f, 0x74, 0xc4, 0xb8, 0xa4, 0x0c, 0x69, 0xe4, + 0x13, 0x8c, 0xcb, 0x11, 0x32, 0x26, 0xc0, 0x85, 0xfc, 0x82, 0xf0, 0xfe, 0x18, 0x72, 0xb8, 0x57, + 0x93, 0x7c, 0x1b, 0x20, 0xa2, 0xc3, 0x01, 0x69, 0x6e, 0x3b, 0x2a, 0x80, 0xce, 0x79, 0x46, 0xe7, + 0x34, 0x99, 0x16, 0xa0, 0x03, 0xb2, 0x10, 0xa1, 0x4d, 0xf2, 0x33, 0xc2, 0x7f, 0xe7, 0x2e, 0xaf, + 0x1c, 0xb9, 0x4b, 0x82, 0xc8, 0x52, 0x07, 0x1f, 0x52, 0x7e, 0x1b, 0x1a, 0x80, 0xda, 0x2c, 0xa3, + 0x36, 0x43, 0x4e, 0xa7, 0x50, 0x33, 0xed, 0x14, 0x66, 0xaa, 0x59, 0xdc, 0x24, 0x5f, 0x20, 0xbc, + 0x37, 0x4a, 0x4e, 0x38, 0xe7, 0x12, 0x46, 0x10, 0xc2, 0x39, 0x97, 0x34, 0x56, 0x68, 0x99, 0x73, + 0x1c, 0x13, 0x97, 0xfc, 0x00, 0xc0, 0xb9, 0xab, 0xd9, 0xac, 0xe0, 0xcb, 0x9b, 0x78, 0x41, 0x95, + 0x2e, 0xb6, 0x29, 0x0d, 0xe0, 0xff, 0xc3, 0xc0, 0x4f, 0x93, 0x93, 0x4d, 0xc0, 0xd7, 0xc5, 0x94, + 0x8d, 0x70, 0xbd, 0x49, 0x7e, 0x44, 0x98, 0x34, 0x5e, 0xd9, 0x89, 0x10, 0x9e, 0xd4, 0x41, 0x81, + 0xf4, 0xdf, 0x76, 0xc5, 0x81, 0x4f, 0x9e, 0xf1, 0xb9, 0x40, 0xce, 0xa5, 0xf2, 0x89, 0xff, 0x97, + 0x81, 0x5a, 0xd4, 0x3c, 0x8d, 0x27, 0xf6, 0x35, 0xc2, 0x83, 0x51, 0x0b, 0x7e, 0x7a, 0xcd, 0x6e, + 0x21, 0x45, 0xda, 0x8c, 0x52, 0xea, 0x68, 0x40, 0x9e, 0x64, 0xac, 0xc6, 0xc9, 0x11, 0xa1, 0x28, + 0x91, 0x4f, 0x51, 0xfd, 0x4a, 0x4a, 0x66, 0x04, 0x13, 0x24, 0x76, 0x77, 0x96, 0xce, 0x6e, 0x59, + 0x0e, 0xc0, 0x2a, 0x0c, 0xec, 0x31, 0x32, 0x9e, 0x02, 0xd6, 0x00, 0x01, 0xdf, 0xe7, 0x45, 0x5a, + 0xdd, 0x24, 0x1f, 0x23, 0xdc, 0x17, 0x6a, 0xf1, 0x5d, 0x3d, 0x23, 0xe8, 0xac, 0xb6, 0x10, 0x27, + 0xdc, 0xe0, 0xe5, 0x71, 0x86, 0xf8, 0x30, 0x19, 0x6d, 0x81, 0x98, 0x3c, 0x41, 0xf8, 0x6f, 0xf1, + 0x96, 0x86, 0x08, 0x15, 0x8f, 0x94, 0xfe, 0x4a, 0x9a, 0x6d, 0x4f, 0x58, 0xd0, 0xd5, 0x7a, 0x1c, + 0xeb, 0x53, 0x84, 0xfb, 0xb8, 0xae, 0x85, 0x5c, 0x16, 0x31, 0xdf, 0xaa, 0x3b, 0x92, 0xae, 0x6c, + 0x53, 0x0b, 0xb0, 0x39, 0xce, 0xd8, 0xfc, 0x8b, 0xc8, 0x29, 0x6c, 0xb8, 0x4e, 0x8f, 0x3c, 0x43, + 0x0d, 0x97, 0x74, 0x22, 0x5a, 0x0a, 0x93, 0x47, 0x0c, 0x62, 0xa5, 0x27, 0x7d, 0x3c, 0x22, 0xcf, + 0x30, 0xf8, 0x27, 0x49, 0x2e, 0x05, 0xbe, 0x15, 0x95, 0xab, 0xa5, 0xff, 0xb7, 0x08, 0x93, 0x98, + 0x4e, 0xff, 0x2d, 0x10, 0x2d, 0x19, 0xdb, 0x61, 0x93, 0x3e, 0x04, 0x91, 0x73, 0x8c, 0xcd, 0x04, + 0x39, 0x2a, 0xc6, 0x86, 0x7c, 0x88, 0xf0, 0x0e, 0x56, 0x7c, 0xa6, 0x05, 0xdd, 0xc8, 0x97, 0xc7, + 0x53, 0x5b, 0x92, 0x11, 0xfc, 0xee, 0xea, 0xf0, 0xc1, 0x62, 0x4e, 0xfe, 0x1c, 0xe1, 0x3e, 0x6e, + 0xf8, 0x41, 0xce, 0x6d, 0xc1, 0x62, 0x74, 0x60, 0xd2, 0x1e, 0xd8, 0x33, 0x0c, 0xac, 0x42, 0x26, + 0x9b, 0x82, 0x6d, 0x68, 0xae, 0x3f, 0x40, 0x78, 0x57, 0xf8, 0x05, 0x9a, 0x16, 0x8c, 0xe8, 0x96, + 0x1d, 0x1b, 0x1b, 0x80, 0xc8, 0x63, 0x0c, 0xeb, 0x41, 0x72, 0xa0, 0x09, 0x56, 0xbf, 0x03, 0x1b, + 0xf0, 0xa5, 0xfc, 0x2b, 0x76, 0x78, 0xa3, 0x15, 0x6a, 0xc1, 0x92, 0x87, 0x17, 0x62, 0x2d, 0x58, + 0xca, 0x9c, 0xa2, 0x65, 0xe5, 0xd0, 0xeb, 0x32, 0xe4, 0x4f, 0x84, 0xb3, 0x31, 0xe0, 0x77, 0x4c, + 0xaf, 0x64, 0xda, 0xb5, 0xc9, 0x00, 0x59, 0x68, 0x03, 0x4b, 0xd2, 0x78, 0x42, 0xba, 0xb6, 0x7d, + 0x45, 0xc0, 0xf0, 0x2c, 0x63, 0x38, 0x45, 0x94, 0xd6, 0x0c, 0x23, 0x0a, 0x58, 0xa7, 0x1c, 0xfd, + 0x7b, 0x02, 0xb1, 0xdc, 0x4f, 0xfc, 0x0b, 0x05, 0xe9, 0x7c, 0x3b, 0xa2, 0x82, 0x4d, 0xcc, 0xc3, + 0x28, 0x4a, 0x1f, 0x78, 0x74, 0x98, 0x21, 0x06, 0x3c, 0x71, 0x3c, 0x22, 0x06, 0x3c, 0x79, 0x76, + 0xd2, 0x12, 0xb8, 0x15, 0x11, 0x9b, 0xfb, 0xdf, 0xb3, 0x97, 0x59, 0xf4, 0xfc, 0x65, 0x16, 0xfd, + 0xf6, 0x32, 0x8b, 0xde, 0x7f, 0x95, 0xed, 0x7a, 0xfe, 0x2a, 0xdb, 0xf5, 0xd3, 0xab, 0x6c, 0xd7, + 0xdd, 0x29, 0xc3, 0xf4, 0x4a, 0x95, 0x95, 0x9c, 0xee, 0xac, 0xf2, 0xaa, 0x42, 0x3c, 0x4a, 0x95, + 0xd7, 0xea, 0xad, 0xaf, 0x51, 0x77, 0x65, 0x27, 0xfb, 0xe8, 0x9d, 0xfa, 0x2b, 0x00, 0x00, 0xff, + 0xff, 0xc0, 0xad, 0x4e, 0xf8, 0x97, 0x23, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2177,6 +2179,8 @@ type QueryClient interface { CctxAll(ctx context.Context, in *QueryAllCctxRequest, opts ...grpc.CallOption) (*QueryAllCctxResponse, error) // Queries a list of pending cctxs. CctxListPending(ctx context.Context, in *QueryListCctxPendingRequest, opts ...grpc.CallOption) (*QueryListCctxPendingResponse, error) + // Queries a list of pending cctxs with rate limit. + CctxListPendingWithinRateLimit(ctx context.Context, in *QueryListCctxPendingWithRateLimitRequest, opts ...grpc.CallOption) (*QueryListCctxPendingWithRateLimitResponse, error) ZetaAccounting(ctx context.Context, in *QueryZetaAccountingRequest, opts ...grpc.CallOption) (*QueryZetaAccountingResponse, error) // Queries a list of lastMetaHeight items. LastZetaHeight(ctx context.Context, in *QueryLastZetaHeightRequest, opts ...grpc.CallOption) (*QueryLastZetaHeightResponse, error) @@ -2352,6 +2356,15 @@ func (c *queryClient) CctxListPending(ctx context.Context, in *QueryListCctxPend return out, nil } +func (c *queryClient) CctxListPendingWithinRateLimit(ctx context.Context, in *QueryListCctxPendingWithRateLimitRequest, opts ...grpc.CallOption) (*QueryListCctxPendingWithRateLimitResponse, error) { + out := new(QueryListCctxPendingWithRateLimitResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/CctxListPendingWithinRateLimit", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) ZetaAccounting(ctx context.Context, in *QueryZetaAccountingRequest, opts ...grpc.CallOption) (*QueryZetaAccountingResponse, error) { out := new(QueryZetaAccountingResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/ZetaAccounting", in, out, opts...) @@ -2403,6 +2416,8 @@ type QueryServer interface { CctxAll(context.Context, *QueryAllCctxRequest) (*QueryAllCctxResponse, error) // Queries a list of pending cctxs. CctxListPending(context.Context, *QueryListCctxPendingRequest) (*QueryListCctxPendingResponse, error) + // Queries a list of pending cctxs with rate limit. + CctxListPendingWithinRateLimit(context.Context, *QueryListCctxPendingWithRateLimitRequest) (*QueryListCctxPendingWithRateLimitResponse, error) ZetaAccounting(context.Context, *QueryZetaAccountingRequest) (*QueryZetaAccountingResponse, error) // Queries a list of lastMetaHeight items. LastZetaHeight(context.Context, *QueryLastZetaHeightRequest) (*QueryLastZetaHeightResponse, error) @@ -2466,6 +2481,9 @@ func (*UnimplementedQueryServer) CctxAll(ctx context.Context, req *QueryAllCctxR func (*UnimplementedQueryServer) CctxListPending(ctx context.Context, req *QueryListCctxPendingRequest) (*QueryListCctxPendingResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CctxListPending not implemented") } +func (*UnimplementedQueryServer) CctxListPendingWithinRateLimit(ctx context.Context, req *QueryListCctxPendingWithRateLimitRequest) (*QueryListCctxPendingWithRateLimitResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CctxListPendingWithinRateLimit not implemented") +} func (*UnimplementedQueryServer) ZetaAccounting(ctx context.Context, req *QueryZetaAccountingRequest) (*QueryZetaAccountingResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ZetaAccounting not implemented") } @@ -2801,6 +2819,24 @@ func _Query_CctxListPending_Handler(srv interface{}, ctx context.Context, dec fu return interceptor(ctx, in, info, handler) } +func _Query_CctxListPendingWithinRateLimit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryListCctxPendingWithRateLimitRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).CctxListPendingWithinRateLimit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.crosschain.Query/CctxListPendingWithinRateLimit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).CctxListPendingWithinRateLimit(ctx, req.(*QueryListCctxPendingWithRateLimitRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_ZetaAccounting_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryZetaAccountingRequest) if err := dec(in); err != nil { @@ -2913,6 +2949,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "CctxListPending", Handler: _Query_CctxListPending_Handler, }, + { + MethodName: "CctxListPendingWithinRateLimit", + Handler: _Query_CctxListPendingWithinRateLimit_Handler, + }, { MethodName: "ZetaAccounting", Handler: _Query_ZetaAccounting_Handler, diff --git a/x/crosschain/types/query.pb.gw.go b/x/crosschain/types/query.pb.gw.go index 6234a732ea..a32163b011 100644 --- a/x/crosschain/types/query.pb.gw.go +++ b/x/crosschain/types/query.pb.gw.go @@ -905,6 +905,42 @@ func local_request_Query_CctxListPending_0(ctx context.Context, marshaler runtim } +var ( + filter_Query_CctxListPendingWithinRateLimit_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_CctxListPendingWithinRateLimit_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListCctxPendingWithRateLimitRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CctxListPendingWithinRateLimit_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.CctxListPendingWithinRateLimit(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_CctxListPendingWithinRateLimit_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListCctxPendingWithRateLimitRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CctxListPendingWithinRateLimit_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.CctxListPendingWithinRateLimit(ctx, &protoReq) + return msg, metadata, err + +} + func request_Query_ZetaAccounting_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryZetaAccountingRequest var metadata runtime.ServerMetadata @@ -1361,6 +1397,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_CctxListPendingWithinRateLimit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_CctxListPendingWithinRateLimit_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_CctxListPendingWithinRateLimit_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_ZetaAccounting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1808,6 +1867,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_CctxListPendingWithinRateLimit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_CctxListPendingWithinRateLimit_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_CctxListPendingWithinRateLimit_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_ZetaAccounting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1888,6 +1967,8 @@ var ( pattern_Query_CctxListPending_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "cctxPending"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_CctxListPendingWithinRateLimit_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "cctxPendingWithRateLimit"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_ZetaAccounting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "zetaAccounting"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_LastZetaHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "lastZetaHeight"}, "", runtime.AssumeColonVerbOpt(false))) @@ -1930,6 +2011,8 @@ var ( forward_Query_CctxListPending_0 = runtime.ForwardResponseMessage + forward_Query_CctxListPendingWithinRateLimit_0 = runtime.ForwardResponseMessage + forward_Query_ZetaAccounting_0 = runtime.ForwardResponseMessage forward_Query_LastZetaHeight_0 = runtime.ForwardResponseMessage diff --git a/x/crosschain/types/rate_limiter_flags.pb.go b/x/crosschain/types/rate_limiter_flags.pb.go index ce24f8baf3..899a1d7099 100644 --- a/x/crosschain/types/rate_limiter_flags.pb.go +++ b/x/crosschain/types/rate_limiter_flags.pb.go @@ -4,7 +4,6 @@ package types import ( - encoding_binary "encoding/binary" fmt "fmt" io "io" math "math" @@ -13,7 +12,6 @@ import ( github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/gogo/protobuf/proto" - coin "github.com/zeta-chain/zetacore/pkg/coin" ) // Reference imports to suppress errors if they are not otherwise used. @@ -27,26 +25,28 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// ZRC20Rate defines the conversion rate of ZRC20 to ZETA -type ZRC20Rate struct { - ChainId int64 `protobuf:"varint,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - CoinType coin.CoinType `protobuf:"varint,2,opt,name=coin_type,json=coinType,proto3,enum=coin.CoinType" json:"coin_type,omitempty"` - Asset string `protobuf:"bytes,3,opt,name=asset,proto3" json:"asset,omitempty"` - ConversionRate float64 `protobuf:"fixed64,4,opt,name=conversion_rate,json=conversionRate,proto3" json:"conversion_rate,omitempty"` +type RateLimiterFlags struct { + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // window in blocks + Window int64 `protobuf:"varint,2,opt,name=window,proto3" json:"window,omitempty"` + // rate in azeta per block + Rate github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,3,opt,name=rate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"rate"` + // conversion in azeta per token + Conversions []Conversion `protobuf:"bytes,4,rep,name=conversions,proto3" json:"conversions"` } -func (m *ZRC20Rate) Reset() { *m = ZRC20Rate{} } -func (m *ZRC20Rate) String() string { return proto.CompactTextString(m) } -func (*ZRC20Rate) ProtoMessage() {} -func (*ZRC20Rate) Descriptor() ([]byte, []int) { +func (m *RateLimiterFlags) Reset() { *m = RateLimiterFlags{} } +func (m *RateLimiterFlags) String() string { return proto.CompactTextString(m) } +func (*RateLimiterFlags) ProtoMessage() {} +func (*RateLimiterFlags) Descriptor() ([]byte, []int) { return fileDescriptor_b17ae80d5af4e97e, []int{0} } -func (m *ZRC20Rate) XXX_Unmarshal(b []byte) error { +func (m *RateLimiterFlags) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *ZRC20Rate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *RateLimiterFlags) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_ZRC20Rate.Marshal(b, m, deterministic) + return xxx_messageInfo_RateLimiterFlags.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -56,66 +56,56 @@ func (m *ZRC20Rate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return b[:n], nil } } -func (m *ZRC20Rate) XXX_Merge(src proto.Message) { - xxx_messageInfo_ZRC20Rate.Merge(m, src) +func (m *RateLimiterFlags) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimiterFlags.Merge(m, src) } -func (m *ZRC20Rate) XXX_Size() int { +func (m *RateLimiterFlags) XXX_Size() int { return m.Size() } -func (m *ZRC20Rate) XXX_DiscardUnknown() { - xxx_messageInfo_ZRC20Rate.DiscardUnknown(m) +func (m *RateLimiterFlags) XXX_DiscardUnknown() { + xxx_messageInfo_RateLimiterFlags.DiscardUnknown(m) } -var xxx_messageInfo_ZRC20Rate proto.InternalMessageInfo - -func (m *ZRC20Rate) GetChainId() int64 { - if m != nil { - return m.ChainId - } - return 0 -} +var xxx_messageInfo_RateLimiterFlags proto.InternalMessageInfo -func (m *ZRC20Rate) GetCoinType() coin.CoinType { +func (m *RateLimiterFlags) GetEnabled() bool { if m != nil { - return m.CoinType + return m.Enabled } - return coin.CoinType_Zeta + return false } -func (m *ZRC20Rate) GetAsset() string { +func (m *RateLimiterFlags) GetWindow() int64 { if m != nil { - return m.Asset + return m.Window } - return "" + return 0 } -func (m *ZRC20Rate) GetConversionRate() float64 { +func (m *RateLimiterFlags) GetConversions() []Conversion { if m != nil { - return m.ConversionRate + return m.Conversions } - return 0 + return nil } -// RateLimiterFlags defines the outbound rate limiter flags -type RateLimiterFlags struct { - IsEnabled bool `protobuf:"varint,1,opt,name=is_enabled,json=isEnabled,proto3" json:"is_enabled,omitempty"` - RateLimitWindow int64 `protobuf:"varint,2,opt,name=rate_limit_window,json=rateLimitWindow,proto3" json:"rate_limit_window,omitempty"` - RateLimitInZeta github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,3,opt,name=rate_limit_in_zeta,json=rateLimitInZeta,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"rate_limit_in_zeta"` - Zrc20Rates []*ZRC20Rate `protobuf:"bytes,4,rep,name=zrc20_rates,json=zrc20Rates,proto3" json:"zrc20_rates,omitempty"` +type Conversion struct { + Zrc20 string `protobuf:"bytes,1,opt,name=zrc20,proto3" json:"zrc20,omitempty"` + Rate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=rate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"rate"` } -func (m *RateLimiterFlags) Reset() { *m = RateLimiterFlags{} } -func (m *RateLimiterFlags) String() string { return proto.CompactTextString(m) } -func (*RateLimiterFlags) ProtoMessage() {} -func (*RateLimiterFlags) Descriptor() ([]byte, []int) { +func (m *Conversion) Reset() { *m = Conversion{} } +func (m *Conversion) String() string { return proto.CompactTextString(m) } +func (*Conversion) ProtoMessage() {} +func (*Conversion) Descriptor() ([]byte, []int) { return fileDescriptor_b17ae80d5af4e97e, []int{1} } -func (m *RateLimiterFlags) XXX_Unmarshal(b []byte) error { +func (m *Conversion) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *RateLimiterFlags) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *Conversion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_RateLimiterFlags.Marshal(b, m, deterministic) + return xxx_messageInfo_Conversion.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -125,42 +115,28 @@ func (m *RateLimiterFlags) XXX_Marshal(b []byte, deterministic bool) ([]byte, er return b[:n], nil } } -func (m *RateLimiterFlags) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimiterFlags.Merge(m, src) +func (m *Conversion) XXX_Merge(src proto.Message) { + xxx_messageInfo_Conversion.Merge(m, src) } -func (m *RateLimiterFlags) XXX_Size() int { +func (m *Conversion) XXX_Size() int { return m.Size() } -func (m *RateLimiterFlags) XXX_DiscardUnknown() { - xxx_messageInfo_RateLimiterFlags.DiscardUnknown(m) +func (m *Conversion) XXX_DiscardUnknown() { + xxx_messageInfo_Conversion.DiscardUnknown(m) } -var xxx_messageInfo_RateLimiterFlags proto.InternalMessageInfo +var xxx_messageInfo_Conversion proto.InternalMessageInfo -func (m *RateLimiterFlags) GetIsEnabled() bool { +func (m *Conversion) GetZrc20() string { if m != nil { - return m.IsEnabled + return m.Zrc20 } - return false -} - -func (m *RateLimiterFlags) GetRateLimitWindow() int64 { - if m != nil { - return m.RateLimitWindow - } - return 0 -} - -func (m *RateLimiterFlags) GetZrc20Rates() []*ZRC20Rate { - if m != nil { - return m.Zrc20Rates - } - return nil + return "" } func init() { - proto.RegisterType((*ZRC20Rate)(nil), "zetachain.zetacore.crosschain.ZRC20Rate") proto.RegisterType((*RateLimiterFlags)(nil), "zetachain.zetacore.crosschain.RateLimiterFlags") + proto.RegisterType((*Conversion)(nil), "zetachain.zetacore.crosschain.Conversion") } func init() { @@ -168,36 +144,31 @@ func init() { } var fileDescriptor_b17ae80d5af4e97e = []byte{ - // 416 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x41, 0x6b, 0x14, 0x31, - 0x14, 0xde, 0x74, 0xab, 0xee, 0xa6, 0xb0, 0xd5, 0xd8, 0xc3, 0x58, 0xe8, 0x74, 0xa8, 0x87, 0x0e, - 0x4a, 0x93, 0x3a, 0xfe, 0x83, 0x16, 0x85, 0x45, 0x4f, 0x41, 0x11, 0x16, 0x21, 0x64, 0x33, 0x71, - 0x1a, 0xba, 0x9b, 0x0c, 0x49, 0xb4, 0xb6, 0x3f, 0xc0, 0xb3, 0xf8, 0xab, 0x7a, 0xec, 0x51, 0x3c, - 0x14, 0xd9, 0xfd, 0x23, 0x92, 0xa4, 0xeb, 0xce, 0xa9, 0x97, 0x99, 0x2f, 0x8f, 0xf7, 0x7d, 0x7c, - 0xdf, 0x7b, 0x0f, 0x3e, 0x17, 0xd6, 0x38, 0x27, 0xce, 0xb8, 0xd2, 0xc4, 0x72, 0x2f, 0xd9, 0x4c, - 0xcd, 0x95, 0x97, 0x96, 0x7d, 0x99, 0xf1, 0xc6, 0xe1, 0xd6, 0x1a, 0x6f, 0xd0, 0xde, 0x95, 0xf4, - 0x3c, 0xf6, 0xe0, 0x88, 0x8c, 0x95, 0x78, 0xcd, 0xdb, 0xdd, 0x69, 0x4c, 0x63, 0x62, 0x27, 0x09, - 0x28, 0x91, 0x76, 0x9f, 0xb6, 0xe7, 0x0d, 0x11, 0x46, 0xe9, 0xf8, 0x49, 0xc5, 0x83, 0x5f, 0x00, - 0x0e, 0x27, 0xf4, 0xb4, 0x3a, 0xa6, 0xdc, 0x4b, 0xf4, 0x0c, 0x0e, 0xa2, 0x02, 0x53, 0x75, 0x06, - 0x0a, 0x50, 0xf6, 0xe9, 0xa3, 0xf8, 0x1e, 0xd7, 0xe8, 0x25, 0x1c, 0x06, 0x1a, 0xf3, 0x97, 0xad, - 0xcc, 0x36, 0x0a, 0x50, 0x8e, 0xaa, 0x11, 0x8e, 0x42, 0xa7, 0x46, 0xe9, 0x0f, 0x97, 0xad, 0xa4, - 0x03, 0x71, 0x87, 0xd0, 0x0e, 0x7c, 0xc0, 0x9d, 0x93, 0x3e, 0xeb, 0x17, 0xa0, 0x1c, 0xd2, 0xf4, - 0x40, 0x87, 0x70, 0x5b, 0x18, 0xfd, 0x4d, 0x5a, 0xa7, 0x8c, 0x66, 0x21, 0x5c, 0xb6, 0x59, 0x80, - 0x12, 0xd0, 0xd1, 0xba, 0x1c, 0x6c, 0x1c, 0xfc, 0xd8, 0x80, 0x8f, 0x03, 0x78, 0x9f, 0xa2, 0xbf, - 0x0d, 0xc9, 0xd1, 0x1e, 0x84, 0xca, 0x31, 0xa9, 0xf9, 0x74, 0x26, 0x93, 0xbb, 0x01, 0x1d, 0x2a, - 0xf7, 0x26, 0x15, 0xd0, 0x0b, 0xf8, 0x64, 0x3d, 0x2e, 0x76, 0xa1, 0x74, 0x6d, 0x2e, 0xa2, 0xcf, - 0x3e, 0xdd, 0xb6, 0x2b, 0xad, 0x4f, 0xb1, 0x8c, 0x3e, 0x43, 0xd4, 0xe9, 0x55, 0x9a, 0x85, 0x21, - 0x26, 0xaf, 0x27, 0xe4, 0xfa, 0x76, 0xbf, 0xf7, 0xe7, 0x76, 0xff, 0xb0, 0x51, 0xfe, 0xec, 0xeb, - 0x14, 0x0b, 0x33, 0x27, 0xc2, 0xb8, 0xb9, 0x71, 0x77, 0xbf, 0x23, 0x57, 0x9f, 0x93, 0x30, 0x05, - 0x87, 0x3f, 0x2a, 0xed, 0x3b, 0xea, 0x63, 0x3d, 0x91, 0x9e, 0xa3, 0x31, 0xdc, 0xba, 0xb2, 0xa2, - 0x3a, 0x8e, 0x09, 0x5d, 0xb6, 0x59, 0xf4, 0xcb, 0xad, 0xaa, 0xc4, 0xf7, 0xae, 0x0c, 0xff, 0xdf, - 0x01, 0x85, 0x91, 0x1c, 0xa0, 0x3b, 0x79, 0x77, 0xbd, 0xc8, 0xc1, 0xcd, 0x22, 0x07, 0x7f, 0x17, - 0x39, 0xf8, 0xb9, 0xcc, 0x7b, 0x37, 0xcb, 0xbc, 0xf7, 0x7b, 0x99, 0xf7, 0x26, 0xaf, 0x3a, 0xf6, - 0x82, 0xde, 0x51, 0xba, 0x98, 0x95, 0x34, 0xf9, 0x4e, 0x3a, 0x77, 0x14, 0xdd, 0x4e, 0x1f, 0xc6, - 0x8d, 0xbf, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0xba, 0xde, 0xfb, 0xd3, 0x62, 0x02, 0x00, 0x00, + // 331 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xc1, 0x4e, 0x32, 0x31, + 0x14, 0x85, 0xa7, 0xc0, 0xcf, 0x2f, 0x65, 0x63, 0x1a, 0x62, 0x26, 0x26, 0x0e, 0x13, 0x4c, 0x74, + 0x5c, 0xd0, 0x2a, 0xbe, 0xc1, 0x60, 0xdc, 0xe8, 0xc6, 0x49, 0xdc, 0xb8, 0x21, 0x43, 0x29, 0x43, + 0x23, 0xb4, 0xa4, 0xad, 0xa2, 0x3c, 0x85, 0x8f, 0xc5, 0x92, 0xa5, 0x31, 0x86, 0x18, 0x78, 0x11, + 0x33, 0x9d, 0x41, 0x66, 0x65, 0x5c, 0xf5, 0xde, 0xe4, 0x9e, 0xd3, 0xef, 0xe4, 0xc0, 0x63, 0xaa, + 0xa4, 0xd6, 0x74, 0x14, 0x73, 0x41, 0x54, 0x6c, 0x58, 0x6f, 0xcc, 0x27, 0xdc, 0x30, 0xd5, 0x1b, + 0x8e, 0xe3, 0x44, 0xe3, 0xa9, 0x92, 0x46, 0xa2, 0xa3, 0x39, 0x33, 0xb1, 0xbd, 0xc1, 0x76, 0x92, + 0x8a, 0xe1, 0x9d, 0xee, 0xb0, 0x91, 0xc8, 0x44, 0xda, 0x4b, 0x92, 0x4e, 0x99, 0xa8, 0xf5, 0x09, + 0xe0, 0x7e, 0x14, 0x1b, 0x76, 0x9b, 0x19, 0x5e, 0xa7, 0x7e, 0xc8, 0x85, 0xff, 0x99, 0x88, 0xfb, + 0x63, 0x36, 0x70, 0x81, 0x0f, 0x82, 0xbd, 0x68, 0xbb, 0xa2, 0x03, 0x58, 0x9d, 0x71, 0x31, 0x90, + 0x33, 0xb7, 0xe4, 0x83, 0xa0, 0x1c, 0xe5, 0x1b, 0xea, 0xc2, 0x4a, 0xca, 0xe5, 0x96, 0x7d, 0x10, + 0xd4, 0x42, 0xb2, 0x58, 0x35, 0x9d, 0x8f, 0x55, 0xf3, 0x34, 0xe1, 0x66, 0xf4, 0xd4, 0xc7, 0x54, + 0x4e, 0x08, 0x95, 0x7a, 0x22, 0x75, 0xfe, 0xb4, 0xf5, 0xe0, 0x91, 0x98, 0xd7, 0x29, 0xd3, 0xf8, + 0x9e, 0x0b, 0x13, 0x59, 0x31, 0xba, 0x83, 0x75, 0x2a, 0xc5, 0x33, 0x53, 0x9a, 0x4b, 0xa1, 0xdd, + 0x8a, 0x5f, 0x0e, 0xea, 0x9d, 0x33, 0xfc, 0x6b, 0x2c, 0xdc, 0xfd, 0x51, 0x84, 0x95, 0xf4, 0xdb, + 0xa8, 0xe8, 0xd1, 0x1a, 0x42, 0xb8, 0x3b, 0x40, 0x0d, 0xf8, 0x6f, 0xae, 0x68, 0xe7, 0xdc, 0xa6, + 0xaa, 0x45, 0xd9, 0x82, 0xc2, 0x9c, 0xbd, 0x64, 0xd9, 0x71, 0xce, 0x7e, 0xf2, 0x07, 0xf6, 0x2b, + 0x46, 0x33, 0xf4, 0xf0, 0x66, 0xb1, 0xf6, 0xc0, 0x72, 0xed, 0x81, 0xaf, 0xb5, 0x07, 0xde, 0x36, + 0x9e, 0xb3, 0xdc, 0x78, 0xce, 0xfb, 0xc6, 0x73, 0x1e, 0x2e, 0x0a, 0x3e, 0x29, 0x7f, 0x3b, 0x6b, + 0x71, 0x1b, 0x85, 0xbc, 0x90, 0x42, 0xb7, 0xd6, 0xb6, 0x5f, 0xb5, 0xd5, 0x5c, 0x7e, 0x07, 0x00, + 0x00, 0xff, 0xff, 0xd2, 0x2c, 0x21, 0x90, 0xf6, 0x01, 0x00, 0x00, } -func (m *ZRC20Rate) Marshal() (dAtA []byte, err error) { +func (m *RateLimiterFlags) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -207,43 +178,59 @@ func (m *ZRC20Rate) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ZRC20Rate) MarshalTo(dAtA []byte) (int, error) { +func (m *RateLimiterFlags) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ZRC20Rate) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *RateLimiterFlags) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if m.ConversionRate != 0 { - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.ConversionRate)))) - i-- - dAtA[i] = 0x21 + if len(m.Conversions) > 0 { + for iNdEx := len(m.Conversions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Conversions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } } - if len(m.Asset) > 0 { - i -= len(m.Asset) - copy(dAtA[i:], m.Asset) - i = encodeVarintRateLimiterFlags(dAtA, i, uint64(len(m.Asset))) - i-- - dAtA[i] = 0x1a + { + size := m.Rate.Size() + i -= size + if _, err := m.Rate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(size)) } - if m.CoinType != 0 { - i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.CoinType)) + i-- + dAtA[i] = 0x1a + if m.Window != 0 { + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.Window)) i-- dAtA[i] = 0x10 } - if m.ChainId != 0 { - i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.ChainId)) + if m.Enabled { + i-- + if m.Enabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } -func (m *RateLimiterFlags) Marshal() (dAtA []byte, err error) { +func (m *Conversion) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -253,54 +240,32 @@ func (m *RateLimiterFlags) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *RateLimiterFlags) MarshalTo(dAtA []byte) (int, error) { +func (m *Conversion) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *RateLimiterFlags) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *Conversion) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if len(m.Zrc20Rates) > 0 { - for iNdEx := len(m.Zrc20Rates) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Zrc20Rates[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintRateLimiterFlags(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } - } { - size := m.RateLimitInZeta.Size() + size := m.Rate.Size() i -= size - if _, err := m.RateLimitInZeta.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.Rate.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintRateLimiterFlags(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x1a - if m.RateLimitWindow != 0 { - i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.RateLimitWindow)) - i-- - dAtA[i] = 0x10 - } - if m.IsEnabled { - i-- - if m.IsEnabled { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } + dAtA[i] = 0x12 + if len(m.Zrc20) > 0 { + i -= len(m.Zrc20) + copy(dAtA[i:], m.Zrc20) + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(len(m.Zrc20))) i-- - dAtA[i] = 0x8 + dAtA[i] = 0xa } return len(dAtA) - i, nil } @@ -316,48 +281,41 @@ func encodeVarintRateLimiterFlags(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *ZRC20Rate) Size() (n int) { +func (m *RateLimiterFlags) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.ChainId != 0 { - n += 1 + sovRateLimiterFlags(uint64(m.ChainId)) - } - if m.CoinType != 0 { - n += 1 + sovRateLimiterFlags(uint64(m.CoinType)) + if m.Enabled { + n += 2 } - l = len(m.Asset) - if l > 0 { - n += 1 + l + sovRateLimiterFlags(uint64(l)) + if m.Window != 0 { + n += 1 + sovRateLimiterFlags(uint64(m.Window)) } - if m.ConversionRate != 0 { - n += 9 + l = m.Rate.Size() + n += 1 + l + sovRateLimiterFlags(uint64(l)) + if len(m.Conversions) > 0 { + for _, e := range m.Conversions { + l = e.Size() + n += 1 + l + sovRateLimiterFlags(uint64(l)) + } } return n } -func (m *RateLimiterFlags) Size() (n int) { +func (m *Conversion) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.IsEnabled { - n += 2 - } - if m.RateLimitWindow != 0 { - n += 1 + sovRateLimiterFlags(uint64(m.RateLimitWindow)) + l = len(m.Zrc20) + if l > 0 { + n += 1 + l + sovRateLimiterFlags(uint64(l)) } - l = m.RateLimitInZeta.Size() + l = m.Rate.Size() n += 1 + l + sovRateLimiterFlags(uint64(l)) - if len(m.Zrc20Rates) > 0 { - for _, e := range m.Zrc20Rates { - l = e.Size() - n += 1 + l + sovRateLimiterFlags(uint64(l)) - } - } return n } @@ -367,7 +325,7 @@ func sovRateLimiterFlags(x uint64) (n int) { func sozRateLimiterFlags(x uint64) (n int) { return sovRateLimiterFlags(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func (m *ZRC20Rate) Unmarshal(dAtA []byte) error { +func (m *RateLimiterFlags) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -390,17 +348,17 @@ func (m *ZRC20Rate) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ZRC20Rate: wiretype end group for non-group") + return fmt.Errorf("proto: RateLimiterFlags: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ZRC20Rate: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RateLimiterFlags: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) } - m.ChainId = 0 + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowRateLimiterFlags @@ -410,16 +368,17 @@ func (m *ZRC20Rate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ChainId |= int64(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } + m.Enabled = bool(v != 0) case 2: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field CoinType", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Window", wireType) } - m.CoinType = 0 + m.Window = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowRateLimiterFlags @@ -429,14 +388,14 @@ func (m *ZRC20Rate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.CoinType |= coin.CoinType(b&0x7F) << shift + m.Window |= int64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Asset", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Rate", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -464,19 +423,44 @@ func (m *ZRC20Rate) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Asset = string(dAtA[iNdEx:postIndex]) + if err := m.Rate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex case 4: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field ConversionRate", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Conversions", wireType) } - var v uint64 - if (iNdEx + 8) > l { + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRateLimiterFlags + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRateLimiterFlags + } + if postIndex > l { return io.ErrUnexpectedEOF } - v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - m.ConversionRate = float64(math.Float64frombits(v)) + m.Conversions = append(m.Conversions, Conversion{}) + if err := m.Conversions[len(m.Conversions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRateLimiterFlags(dAtA[iNdEx:]) @@ -498,7 +482,7 @@ func (m *ZRC20Rate) Unmarshal(dAtA []byte) error { } return nil } -func (m *RateLimiterFlags) Unmarshal(dAtA []byte) error { +func (m *Conversion) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -521,54 +505,15 @@ func (m *RateLimiterFlags) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RateLimiterFlags: wiretype end group for non-group") + return fmt.Errorf("proto: Conversion: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RateLimiterFlags: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: Conversion: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field IsEnabled", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRateLimiterFlags - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.IsEnabled = bool(v != 0) - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field RateLimitWindow", wireType) - } - m.RateLimitWindow = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRateLimiterFlags - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.RateLimitWindow |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RateLimitInZeta", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Zrc20", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -596,15 +541,13 @@ func (m *RateLimiterFlags) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.RateLimitInZeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Zrc20 = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Zrc20Rates", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Rate", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowRateLimiterFlags @@ -614,23 +557,23 @@ func (m *RateLimiterFlags) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthRateLimiterFlags } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthRateLimiterFlags } if postIndex > l { return io.ErrUnexpectedEOF } - m.Zrc20Rates = append(m.Zrc20Rates, &ZRC20Rate{}) - if err := m.Zrc20Rates[len(m.Zrc20Rates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Rate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/fungible/keeper/foreign_coins.go b/x/fungible/keeper/foreign_coins.go index ee424e92c1..5f0a31da4a 100644 --- a/x/fungible/keeper/foreign_coins.go +++ b/x/fungible/keeper/foreign_coins.go @@ -1,6 +1,8 @@ package keeper import ( + "strings" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" @@ -79,6 +81,22 @@ func (k Keeper) GetAllForeignCoins(ctx sdk.Context) (list []types.ForeignCoins) return } +// GetAllForeignERC20CoinMap returns all foreign ERC20 coins in a map of chainID -> asset -> coin +func (k Keeper) GetAllForeignERC20CoinMap(ctx sdk.Context) map[int64]map[string]types.ForeignCoins { + allForeignCoins := k.GetAllForeignCoins(ctx) + + erc20CoinMap := make(map[int64]map[string]types.ForeignCoins) + for _, c := range allForeignCoins { + if c.CoinType == coin.CoinType_ERC20 { + if _, found := erc20CoinMap[c.ForeignChainId]; !found { + erc20CoinMap[c.ForeignChainId] = make(map[string]types.ForeignCoins) + } + erc20CoinMap[c.ForeignChainId][strings.ToLower(c.Asset)] = c + } + } + return erc20CoinMap +} + // GetGasCoinForForeignCoin returns the gas coin for a given chain func (k Keeper) GetGasCoinForForeignCoin(ctx sdk.Context, chainID int64) (types.ForeignCoins, bool) { foreignCoinList := k.GetAllForeignCoinsForChain(ctx, chainID) From 60cdbd0acbab06e0660ea052064dc4b43f796749 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 18 Apr 2024 15:11:44 -0500 Subject: [PATCH 03/40] split big loop into backwards loop and forwards loop to be more accurate --- x/crosschain/keeper/grpc_query_cctx.go | 46 ++++++++++++++++++-------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index ac3d2da63a..6a1b643863 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -11,6 +11,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -226,7 +227,6 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que if applyLimit { gasCoinRates, erc20CoinRates = k.GetRatelimiterRates(ctx) erc20Coins = k.fungibleKeeper.GetAllForeignERC20CoinMap(ctx) - //rateLimitInZeta = new(big.Float).SetInt(rateLimitFlags.RateLimitInZeta.BigInt()) rateLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()) } @@ -235,31 +235,39 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que totalPending := uint64(0) totalCctxValueInZeta := sdk.NewDec(0) cctxs := make([]*types.CrossChainTx, 0) + pendingNoncesMap := make(map[int64]*observertypes.PendingNonces) - // the criteria to stop adding cctxs to the result + // the criteria to stop adding cctxs to the rpc response maxCCTXsReached := func() bool { // #nosec G701 len always positive return uint32(len(cctxs)) >= limit } - // query pending cctxs for each supported chain + // query pending nonces for each supported chain + // Note: The pending nonces could change during the RPC call, so query them beforehand chains := k.zetaObserverKeeper.GetSupportedChains(ctx) -ChainLoop: for _, chain := range chains { - // skip zeta chain if chain.IsZetaChain() { continue } - - // get pending nonces for this chain pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) if !found { return nil, status.Error(codes.Internal, "pending nonces not found") } + pendingNoncesMap[chain.ChainId] = &pendingNonces + } + + // query backwards for potential missed pending cctxs for each supported chain +LoopBackwards: + for _, chain := range chains { + if chain.IsZetaChain() { + continue + } // 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 // therefore might mask some lower nonce cctx that is still pending. + pendingNonces := pendingNoncesMap[chain.ChainId] startNonce := pendingNonces.NonceLow - 1 endNonce := pendingNonces.NonceLow - 1000 if endNonce < 0 { @@ -293,7 +301,7 @@ ChainLoop: // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded if rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true - break ChainLoop + break LoopBackwards } } @@ -307,10 +315,21 @@ ChainLoop: } // 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 forwards for pending cctxs for each supported chain +LoopForwards: + for _, chain := range chains { + if chain.IsZetaChain() { + continue + } - // now query the pending nonces that we know are pending + // query the pending cctxs in range [NonceLow, NonceHigh) + pendingNonces := pendingNoncesMap[chain.ChainId] for i := pendingNonces.NonceLow; i < pendingNonces.NonceHigh; i++ { nonceToCctx, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tss.TssPubkey, chain.ChainId, i) if !found { @@ -323,12 +342,12 @@ ChainLoop: // only take a `limit` number of pending cctxs as result if maxCCTXsReached() { - break ChainLoop + break LoopForwards } // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded if applyLimit && rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true - break ChainLoop + break LoopForwards } cctxs = append(cctxs, &cctx) } @@ -389,10 +408,9 @@ func convertCctxValue( return sdk.NewDec(0) } - // calculate the reciprocal of `rate` as the amount of zrc20 needed to buy 1 ZETA + // the reciprocal of `rate` is the amount of zrc20 needed to buy 1 ZETA // for example, given rate = 0.8, the reciprocal is 1.25, which means 1.25 ZRC20 can buy 1 ZETA - // given decimals = 6, below calculation is equivalent to 1,000,000 / 1.25 = 800,000 - // which means 800,000 ZRC20 can buy 1 ZETA + // given decimals = 6, the `oneZeta` amount will be 1.25 * 10^6 = 1250000 oneZrc20 := sdk.NewDec(1).Power(decimals) oneZeta := oneZrc20.Quo(rate) From 3f63f227783b1c804eb7a07463f8f14c8c608ed2 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 18 Apr 2024 17:10:00 -0500 Subject: [PATCH 04/40] adjust zetaclient code to query pending cctx with rate limit --- zetaclient/interfaces/interfaces.go | 1 + zetaclient/testutils/stub/core_bridge.go | 7 ++++ zetaclient/zetabridge/query.go | 26 +++++++++++-- zetaclient/zetacore_observer.go | 48 +++++++++++++++++++----- 4 files changed, 69 insertions(+), 13 deletions(-) diff --git a/zetaclient/interfaces/interfaces.go b/zetaclient/interfaces/interfaces.go index 387e764688..0c803c3497 100644 --- a/zetaclient/interfaces/interfaces.go +++ b/zetaclient/interfaces/interfaces.go @@ -98,6 +98,7 @@ type ZetaCoreBridger interface { GetZetaBlockHeight() (int64, error) GetLastBlockHeightByChain(chain chains.Chain) (*crosschaintypes.LastBlockHeight, error) ListPendingCctx(chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) + ListPendingCctxWithRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, 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 2f0ddaa732..51c2afb7ef 100644 --- a/zetaclient/testutils/stub/core_bridge.go +++ b/zetaclient/testutils/stub/core_bridge.go @@ -121,6 +121,13 @@ func (z *MockZetaCoreBridge) ListPendingCctx(_ int64) ([]*cctxtypes.CrossChainTx return []*cctxtypes.CrossChainTx{}, 0, nil } +func (z *MockZetaCoreBridge) ListPendingCctxWithRatelimit() ([]*cctxtypes.CrossChainTx, uint64, bool, error) { + if z.paused { + return nil, 0, false, errors.New(ErrMsgPaused) + } + return []*cctxtypes.CrossChainTx{}, 0, false, nil +} + func (z *MockZetaCoreBridge) GetPendingNoncesByChain(_ int64) (observerTypes.PendingNonces, error) { if z.paused { return observerTypes.PendingNonces{}, errors.New(ErrMsgPaused) diff --git a/zetaclient/zetabridge/query.go b/zetaclient/zetabridge/query.go index b9e08bef56..fab225c571 100644 --- a/zetaclient/zetabridge/query.go +++ b/zetaclient/zetabridge/query.go @@ -121,18 +121,38 @@ func (b *ZetaCoreBridge) GetObserverList() ([]string, error) { } // ListPendingCctx returns a list of pending cctxs for a given chainID -// the returned list has a limited size of crosschainkeeper.MaxPendingCctxs -// the total number of pending cctxs is returned +// - The max size of the list is crosschainkeeper.MaxPendingCctxs func (b *ZetaCoreBridge) ListPendingCctx(chainID int64) ([]*types.CrossChainTx, uint64, error) { client := types.NewQueryClient(b.grpcConn) maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) - resp, err := client.CctxListPending(context.Background(), &types.QueryListCctxPendingRequest{ChainId: chainID}, maxSizeOption) + resp, err := client.CctxListPending( + context.Background(), + &types.QueryListCctxPendingRequest{ChainId: chainID}, + maxSizeOption, + ) if err != nil { return nil, 0, err } return resp.CrossChainTx, resp.TotalPending, nil } +// ListPendingCctxWithRatelimit 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) ListPendingCctxWithRatelimit() ([]*types.CrossChainTx, uint64, bool, error) { + client := types.NewQueryClient(b.grpcConn) + maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) + resp, err := client.CctxListPendingWithinRateLimit( + context.Background(), + &types.QueryListCctxPendingWithRateLimitRequest{}, + maxSizeOption, + ) + if err != nil { + return nil, 0, false, err + } + return resp.CrossChainTx, resp.TotalPending, resp.RateLimitExceeded, nil +} + func (b *ZetaCoreBridge) GetAbortedZetaAmount() (string, error) { client := types.NewQueryClient(b.grpcConn) resp, err := client.ZetaAccounting(context.Background(), &types.QueryZetaAccountingRequest{}) diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 48771bc9bc..a40b7f22b8 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -130,16 +130,29 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { } } - // Set Current Hot key burn rate + // set current hot key burn rate metrics.HotKeyBurnRate.Set(float64(co.ts.HotKeyBurnRate.GetBurnRate().Int64())) + // query pending cctxs across all foreign chains with rate limit + cctxMap, err := co.getAllPendingCctxWithRatelimit() + if err != nil { + co.logger.ZetaChainWatcher.Error().Err(err).Msgf("startCctxScheduler: queryPendingCctxWithRatelimit failed") + } + // schedule keysign for pending cctxs on each chain coreContext := appContext.ZetaCoreContext() supportedChains := coreContext.GetEnabledChains() for _, c := range supportedChains { - if c.ChainId == co.bridge.ZetaChain().ChainId { + if c.IsZetaChain() { + continue + } + // get cctxs from map and set pending transactions prometheus gauge + cctxList := cctxMap[c.ChainId] + metrics.PendingTxsPerChain.WithLabelValues(c.ChainName.String()).Set(float64(len(cctxList))) + if len(cctxList) == 0 { continue } + // update chain parameters for signer and chain client signer, err := co.GetUpdatedSigner(coreContext, c.ChainId) if err != nil { @@ -155,14 +168,6 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { continue } - cctxList, totalPending, err := co.bridge.ListPendingCctx(c.ChainId) - if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("startCctxScheduler: ListPendingCctx failed for chain %d", c.ChainId) - continue - } - // Set Pending transactions prometheus gauge - metrics.PendingTxsPerChain.WithLabelValues(c.ChainName.String()).Set(float64(totalPending)) - // #nosec G701 range is verified zetaHeight := uint64(bn) if chains.IsEVMChain(c.ChainId) { @@ -184,6 +189,29 @@ 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, error) { + cctxList, totalPending, rateLimitExceeded, err := co.bridge.ListPendingCctxWithRatelimit() + if err != nil { + return nil, err + } + if rateLimitExceeded { + co.logger.ZetaChainWatcher.Warn().Msgf("rate limit exceeded, fetched %d cctxs out of %d", len(cctxList), totalPending) + } + + // classify pending cctxs by chain id + cctxMap := make(map[int64][]*types.CrossChainTx) + for _, cctx := range cctxList { + chainID := cctx.GetCurrentOutTxParam().ReceiverChainId + if _, found := cctxMap[chainID]; !found { + cctxMap[chainID] = make([]*types.CrossChainTx, 0) + } + cctxMap[chainID] = append(cctxMap[chainID], cctx) + } + + return cctxMap, nil +} + // scheduleCctxEVM schedules evm outtx keysign on each ZetaChain block (the ticker) func (co *CoreObserver) scheduleCctxEVM( outTxMan *outtxprocessor.Processor, From f0b0677643268b221197d84acb5a69d20cf2708a Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 18 Apr 2024 22:33:53 -0500 Subject: [PATCH 05/40] update change log and add one more rate limiter flag test --- changelog.md | 1 + .../keeper/rate_limiter_flags_test.go | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/changelog.md b/changelog.md index fd1a9c8b99..c5c6c413d1 100644 --- a/changelog.md +++ b/changelog.md @@ -53,6 +53,7 @@ * [1935](https://github.com/zeta-chain/node/pull/1935) - add an operational authority group * [1954](https://github.com/zeta-chain/node/pull/1954) - add metric for concurrent keysigns * [2006](https://github.com/zeta-chain/node/pull/2006) - add Amoy testnet static chain information +* [2045](https://github.com/zeta-chain/node/pull/2046) - add grpc query with outbound rate limit for zetaclient to use * [2046](https://github.com/zeta-chain/node/pull/2046) - add state variable in crosschain for rate limiter flags ### Tests diff --git a/x/crosschain/keeper/rate_limiter_flags_test.go b/x/crosschain/keeper/rate_limiter_flags_test.go index 044d02bc67..0b8010031d 100644 --- a/x/crosschain/keeper/rate_limiter_flags_test.go +++ b/x/crosschain/keeper/rate_limiter_flags_test.go @@ -1,11 +1,16 @@ package keeper_test import ( + "strings" "testing" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" ) func TestKeeper_GetRateLimiterFlags(t *testing.T) { @@ -22,3 +27,65 @@ func TestKeeper_GetRateLimiterFlags(t *testing.T) { require.True(t, found) require.Equal(t, flags, r) } + +func TestKeeper_GetRateLimiterRates(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // create test flags + zrc20GasAddr := sample.EthAddress().Hex() + zrc20ERC20Addr1 := sample.EthAddress().Hex() + zrc20ERC20Addr2 := sample.EthAddress().Hex() + flags := types.RateLimiterFlags{ + Rate: sdk.NewUint(100), + Conversions: []types.Conversion{ + { + Zrc20: zrc20GasAddr, + Rate: sdk.NewDec(1), + }, + { + Zrc20: zrc20ERC20Addr1, + Rate: sdk.NewDec(2), + }, + { + Zrc20: zrc20ERC20Addr2, + Rate: sdk.NewDec(3), + }, + }, + } + + chainID := chains.GoerliLocalnetChain().ChainId + + // add gas coin + fcGas := sample.ForeignCoins(t, zrc20GasAddr) + fcGas.CoinType = coin.CoinType_Gas + fcGas.ForeignChainId = chainID + zk.FungibleKeeper.SetForeignCoins(ctx, fcGas) + + // add two erc20 coins + asset1 := sample.EthAddress().Hex() + fcERC20 := sample.ForeignCoins(t, zrc20ERC20Addr1) + fcERC20.Asset = asset1 + fcERC20.ForeignChainId = chainID + zk.FungibleKeeper.SetForeignCoins(ctx, fcERC20) + + asset2 := sample.EthAddress().Hex() + fcERC20 = sample.ForeignCoins(t, zrc20ERC20Addr2) + fcERC20.Asset = asset2 + fcERC20.ForeignChainId = chainID + zk.FungibleKeeper.SetForeignCoins(ctx, fcERC20) + + // set flags + k.SetRateLimiterFlags(ctx, flags) + r, found := k.GetRateLimiterFlags(ctx) + require.True(t, found) + require.Equal(t, flags, r) + + // get rates + gasRates, erc20Rates := k.GetRateLimiterRates(ctx) + require.Equal(t, 1, len(gasRates)) + require.Equal(t, 1, len(erc20Rates)) + require.Equal(t, sdk.NewDec(1), gasRates[chainID]) + require.Equal(t, 2, len(erc20Rates[chainID])) + require.Equal(t, sdk.NewDec(2), erc20Rates[chainID][strings.ToLower(asset1)]) + require.Equal(t, sdk.NewDec(3), erc20Rates[chainID][strings.ToLower(asset2)]) +} From aab441e9a7a6e8a5bfb20e91a1b502ca9475beee Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 18 Apr 2024 23:25:34 -0500 Subject: [PATCH 06/40] use outboun amount for calculation --- x/crosschain/keeper/grpc_query_cctx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index 9c6290aa26..1ab7078b1a 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -415,7 +415,7 @@ func convertCctxValue( oneZeta := oneZrc20.Quo(rate) // convert asset amount into ZETA - amountCctx := sdk.NewDecFromBigInt(cctx.InboundTxParams.Amount.BigInt()) + amountCctx := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) amountZeta := amountCctx.Quo(oneZeta) return amountZeta } From 00ab5acde7a9f40fdef26c38abd573b2c49d60cd Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 19 Apr 2024 10:55:11 -0500 Subject: [PATCH 07/40] some minimum code refactor --- testutil/keeper/mocks/crosschain/observer.go | 20 ++++ x/crosschain/keeper/cctx_utils.go | 2 +- x/crosschain/keeper/cctx_utils_test.go | 2 +- x/crosschain/keeper/grpc_query_cctx.go | 98 ++++++++----------- .../keeper/msg_server_add_to_outtx_tracker.go | 2 +- x/crosschain/types/expected_keepers.go | 1 + x/observer/keeper/chain_params.go | 13 +++ zetaclient/evm/evm_signer.go | 2 +- 8 files changed, 78 insertions(+), 62 deletions(-) diff --git a/testutil/keeper/mocks/crosschain/observer.go b/testutil/keeper/mocks/crosschain/observer.go index aa05e13226..37b8ea2720 100644 --- a/testutil/keeper/mocks/crosschain/observer.go +++ b/testutil/keeper/mocks/crosschain/observer.go @@ -596,6 +596,26 @@ func (_m *CrosschainObserverKeeper) GetSupportedChains(ctx types.Context) []*cha return r0 } +// GetSupportedForeignChains provides a mock function with given fields: ctx +func (_m *CrosschainObserverKeeper) GetSupportedForeignChains(ctx types.Context) []*chains.Chain { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetSupportedForeignChains") + } + + var r0 []*chains.Chain + if rf, ok := ret.Get(0).(func(types.Context) []*chains.Chain); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*chains.Chain) + } + } + + return r0 +} + // GetTSS provides a mock function with given fields: ctx func (_m *CrosschainObserverKeeper) GetTSS(ctx types.Context) (observertypes.TSS, bool) { ret := _m.Called(ctx) diff --git a/x/crosschain/keeper/cctx_utils.go b/x/crosschain/keeper/cctx_utils.go index 6ae826f9ed..6f9651967a 100644 --- a/x/crosschain/keeper/cctx_utils.go +++ b/x/crosschain/keeper/cctx_utils.go @@ -86,7 +86,7 @@ func (k Keeper) GetRevertGasLimit(ctx sdk.Context, cctx types.CrossChainTx) (uin return 0, nil } -func IsPending(cctx types.CrossChainTx) bool { +func IsPending(cctx *types.CrossChainTx) bool { // pending inbound is not considered a "pending" state because it has not reached consensus yet return cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound || cctx.CctxStatus.Status == types.CctxStatus_PendingRevert } diff --git a/x/crosschain/keeper/cctx_utils_test.go b/x/crosschain/keeper/cctx_utils_test.go index 1c807a97c1..e0dedbb41d 100644 --- a/x/crosschain/keeper/cctx_utils_test.go +++ b/x/crosschain/keeper/cctx_utils_test.go @@ -215,7 +215,7 @@ func Test_IsPending(t *testing.T) { } for _, tc := range tt { t.Run(fmt.Sprintf("status %s", tc.status), func(t *testing.T) { - require.Equal(t, tc.expected, crosschainkeeper.IsPending(types.CrossChainTx{CctxStatus: &types.Status{Status: tc.status}})) + require.Equal(t, tc.expected, crosschainkeeper.IsPending(&types.CrossChainTx{CctxStatus: &types.Status{Status: tc.status}})) }) } } diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index 1ab7078b1a..95a2336f31 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -82,16 +82,12 @@ func (k Keeper) CctxByNonce(c context.Context, req *types.QueryGetCctxByNonceReq return nil, status.Error(codes.Internal, "tss not found") } // #nosec G701 always in range - res, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tss.TssPubkey, req.ChainID, int64(req.Nonce)) - if !found { - return nil, status.Error(codes.Internal, fmt.Sprintf("nonceToCctx not found: nonce %d, chainid %d", req.Nonce, req.ChainID)) - } - val, found := k.GetCrossChainTx(ctx, res.CctxIndex) - if !found { - return nil, status.Error(codes.Internal, fmt.Sprintf("cctx not found: index %s", res.CctxIndex)) + cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, req.ChainID, int64(req.Nonce)) + if err != nil { + return nil, err } - return &types.QueryGetCctxResponse{CrossChainTx: &val}, nil + return &types.QueryGetCctxResponse{CrossChainTx: cctx}, nil } // CctxListPending returns a list of pending cctxs and the total number of pending cctxs @@ -138,20 +134,16 @@ func (k Keeper) CctxListPending(c context.Context, req *types.QueryListCctxPendi startNonce = 0 } for i := startNonce; i < pendingNonces.NonceLow; i++ { - nonceToCctx, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tss.TssPubkey, req.ChainId, i) - if !found { - return nil, status.Error(codes.Internal, fmt.Sprintf("nonceToCctx not found: nonce %d, chainid %d", i, req.ChainId)) - } - cctx, found := k.GetCrossChainTx(ctx, nonceToCctx.CctxIndex) - if !found { - return nil, status.Error(codes.Internal, fmt.Sprintf("cctx not found: index %s", nonceToCctx.CctxIndex)) + cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, req.ChainId, i) + if err != nil { + return nil, err } // only take a `limit` number of pending cctxs as result but still count the total pending cctxs if IsPending(cctx) { totalPending++ if !maxCCTXsReached() { - cctxs = append(cctxs, &cctx) + cctxs = append(cctxs, cctx) } } } @@ -162,15 +154,11 @@ func (k Keeper) CctxListPending(c context.Context, req *types.QueryListCctxPendi // now query the pending nonces that we know are pending for i := pendingNonces.NonceLow; i < pendingNonces.NonceHigh && !maxCCTXsReached(); i++ { - nonceToCctx, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tss.TssPubkey, req.ChainId, i) - if !found { - return nil, status.Error(codes.Internal, "nonceToCctx not found") + cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, req.ChainId, i) + if err != nil { + return nil, err } - cctx, found := k.GetCrossChainTx(ctx, nonceToCctx.CctxIndex) - if !found { - return nil, status.Error(codes.Internal, "cctxIndex not found") - } - cctxs = append(cctxs, &cctx) + cctxs = append(cctxs, cctx) } return &types.QueryListCctxPendingResponse{ @@ -243,13 +231,10 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que return uint32(len(cctxs)) >= limit } - // query pending nonces for each supported chain + // query pending nonces for each foreign chain // Note: The pending nonces could change during the RPC call, so query them beforehand - chains := k.zetaObserverKeeper.GetSupportedChains(ctx) + chains := k.zetaObserverKeeper.GetSupportedForeignChains(ctx) for _, chain := range chains { - if chain.IsZetaChain() { - continue - } pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) if !found { return nil, status.Error(codes.Internal, "pending nonces not found") @@ -257,13 +242,9 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que pendingNoncesMap[chain.ChainId] = &pendingNonces } - // query backwards for potential missed pending cctxs for each supported chain + // query backwards for potential missed pending cctxs for each foreign chain LoopBackwards: for _, chain := range chains { - if chain.IsZetaChain() { - continue - } - // 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 // therefore might mask some lower nonce cctx that is still pending. @@ -276,13 +257,9 @@ LoopBackwards: // query cctx by nonce backwards to the left boundary of the rate limit sliding window for nonce := startNonce; nonce >= 0; nonce-- { - nonceToCctx, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tss.TssPubkey, chain.ChainId, nonce) - if !found { - return nil, status.Error(codes.Internal, fmt.Sprintf("nonceToCctx not found: chainid %d, nonce %d", chain.ChainId, nonce)) - } - cctx, found := k.GetCrossChainTx(ctx, nonceToCctx.CctxIndex) - if !found { - return nil, status.Error(codes.Internal, fmt.Sprintf("cctx not found: index %s", nonceToCctx.CctxIndex)) + cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, chain.ChainId, nonce) + if err != nil { + return nil, err } // We should at least go backwards by 1000 nonces to pick up missed pending cctxs @@ -299,7 +276,7 @@ LoopBackwards: break } // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded - if rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { + if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true break LoopBackwards } @@ -309,7 +286,7 @@ LoopBackwards: if IsPending(cctx) { totalPending++ if !maxCCTXsReached() { - cctxs = append(cctxs, &cctx) + cctxs = append(cctxs, cctx) } } } @@ -321,23 +298,15 @@ LoopBackwards: totalPending += uint64(pendingNonces.NonceHigh - pendingNonces.NonceLow) } - // query forwards for pending cctxs for each supported chain + // query forwards for pending cctxs for each foreign chain LoopForwards: for _, chain := range chains { - if chain.IsZetaChain() { - continue - } - // query the pending cctxs in range [NonceLow, NonceHigh) pendingNonces := pendingNoncesMap[chain.ChainId] - for i := pendingNonces.NonceLow; i < pendingNonces.NonceHigh; i++ { - nonceToCctx, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tss.TssPubkey, chain.ChainId, i) - if !found { - return nil, status.Error(codes.Internal, "nonceToCctx not found") - } - cctx, found := k.GetCrossChainTx(ctx, nonceToCctx.CctxIndex) - if !found { - return nil, status.Error(codes.Internal, "cctxIndex not found") + 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 @@ -345,11 +314,11 @@ LoopForwards: break LoopForwards } // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded - if applyLimit && rateLimitExceeded(chain.ChainId, &cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { + if applyLimit && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true break LoopForwards } - cctxs = append(cctxs, &cctx) + cctxs = append(cctxs, cctx) } } @@ -360,6 +329,19 @@ LoopForwards: }, nil } +// getCctxByChainIDAndNonce returns the cctx by chainID and nonce +func getCctxByChainIDAndNonce(k Keeper, ctx sdk.Context, tssPubkey string, chainID int64, nonce int64) (*types.CrossChainTx, error) { + nonceToCctx, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tssPubkey, chainID, nonce) + if !found { + return nil, status.Error(codes.Internal, fmt.Sprintf("nonceToCctx not found: chainid %d, nonce %d", chainID, nonce)) + } + cctx, found := k.GetCrossChainTx(ctx, nonceToCctx.CctxIndex) + if !found { + return nil, status.Error(codes.Internal, fmt.Sprintf("cctx not found: index %s", nonceToCctx.CctxIndex)) + } + return &cctx, nil +} + // convertCctxValue converts the value of the cctx in ZETA using given conversion rates func convertCctxValue( chainID int64, diff --git a/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go b/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go index 9031d0bfe5..4f149a901f 100644 --- a/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go +++ b/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go @@ -41,7 +41,7 @@ func (k msgServer) AddToOutTxTracker(goCtx context.Context, msg *types.MsgAddToO } // tracker submission is only allowed when the cctx is pending - if !IsPending(*cctx.CrossChainTx) { + if !IsPending(cctx.CrossChainTx) { // garbage tracker (for any reason) is harmful to outTx observation and should be removed if it exists // it if does not exist, RemoveOutTxTracker is a no-op k.RemoveOutTxTracker(ctx, msg.ChainId, msg.Nonce) diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index cbcaede349..3d524a91cb 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -90,6 +90,7 @@ type ObserverKeeper interface { ) (bool, bool, observertypes.Ballot, string, error) GetSupportedChainFromChainID(ctx sdk.Context, chainID int64) *chains.Chain GetSupportedChains(ctx sdk.Context) []*chains.Chain + GetSupportedForeignChains(ctx sdk.Context) []*chains.Chain } type FungibleKeeper interface { diff --git a/x/observer/keeper/chain_params.go b/x/observer/keeper/chain_params.go index 7b5e0a246e..8c4d7301fb 100644 --- a/x/observer/keeper/chain_params.go +++ b/x/observer/keeper/chain_params.go @@ -71,3 +71,16 @@ func (k Keeper) GetSupportedChains(ctx sdk.Context) []*chains.Chain { } return c } + +// GetSupportedForeignChains returns the list of supported foreign chains +func (k Keeper) GetSupportedForeignChains(ctx sdk.Context) []*chains.Chain { + allChains := k.GetSupportedChains(ctx) + + foreignChains := make([]*chains.Chain, 0) + for _, chain := range allChains { + if !chain.IsZetaChain() { + foreignChains = append(foreignChains, chain) + } + } + return foreignChains +} diff --git a/zetaclient/evm/evm_signer.go b/zetaclient/evm/evm_signer.go index e077d19fe2..cd038f2912 100644 --- a/zetaclient/evm/evm_signer.go +++ b/zetaclient/evm/evm_signer.go @@ -560,7 +560,7 @@ func (signer *Signer) reportToOutTxTracker(zetaBridge interfaces.ZetaCoreBridger cctx, err := zetaBridge.GetCctxByNonce(chainID, nonce) if err != nil { logger.Err(err).Msgf("reportToOutTxTracker: error getting cctx for chain %d nonce %d outTxHash %s", chainID, nonce, outTxHash) - } else if !crosschainkeeper.IsPending(*cctx) { + } else if !crosschainkeeper.IsPending(cctx) { logger.Info().Msgf("reportToOutTxTracker: cctx already finalized for chain %d nonce %d outTxHash %s", chainID, nonce, outTxHash) break } From f31c988378329b4e88a85ebeee43b57990b09b92 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 19 Apr 2024 11:20:15 -0500 Subject: [PATCH 08/40] created separate file for cctx query with rate limit --- x/crosschain/keeper/grpc_query_cctx.go | 242 ----------------- .../keeper/grpc_query_cctx_rate_limit.go | 253 ++++++++++++++++++ .../keeper/grpc_query_cctx_rate_limit_test.go | 38 +++ x/crosschain/keeper/grpc_query_cctx_test.go | 30 --- 4 files changed, 291 insertions(+), 272 deletions(-) create mode 100644 x/crosschain/keeper/grpc_query_cctx_rate_limit.go create mode 100644 x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index 95a2336f31..5e916beeac 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -3,15 +3,11 @@ package keeper import ( "context" "fmt" - "strings" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" - "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" - fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -167,168 +163,6 @@ func (k Keeper) CctxListPending(c context.Context, req *types.QueryListCctxPendi }, nil } -// CctxListPendingWithinRateLimit returns a list of pending cctxs that do not exceed the outbound rate limit -// a limit for the number of cctxs to return can be specified or the default is MaxPendingCctxs -func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.QueryListCctxPendingWithRateLimitRequest) (*types.QueryListCctxPendingWithRateLimitResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - - // check limit and use default MaxPendingCctxs if not specified - if req.Limit > MaxPendingCctxs { - return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("limit exceeds max limit of %d", MaxPendingCctxs)) - } - limit := req.Limit - if limit == 0 { - limit = MaxPendingCctxs - } - - // get current height and tss - ctx := sdk.UnwrapSDKContext(c) - height := ctx.BlockHeight() - if height <= 0 { - return nil, status.Error(codes.OutOfRange, "height out of range") - } - tss, found := k.zetaObserverKeeper.GetTSS(ctx) - if !found { - return nil, status.Error(codes.Internal, "tss not found") - } - - // check rate limit flags to decide if we should apply rate limit - applyLimit := true - rateLimitFlags, found := k.GetRateLimiterFlags(ctx) - if !found || !rateLimitFlags.Enabled { - applyLimit = false - } - - // calculate the rate limiter sliding window left boundary (inclusive) - leftWindowBoundary := height - rateLimitFlags.Window - if leftWindowBoundary < 0 { - leftWindowBoundary = 0 - } - - // get the conversion rates for all foreign coins - var gasCoinRates map[int64]sdk.Dec - var erc20CoinRates map[int64]map[string]sdk.Dec - var erc20Coins map[int64]map[string]fungibletypes.ForeignCoins - var rateLimitInZeta sdk.Dec - if applyLimit { - gasCoinRates, erc20CoinRates = k.GetRateLimiterRates(ctx) - erc20Coins = k.fungibleKeeper.GetAllForeignERC20CoinMap(ctx) - rateLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()) - } - - // define a few variables to be used in the below loops - limitExceeded := false - totalPending := uint64(0) - totalCctxValueInZeta := sdk.NewDec(0) - cctxs := make([]*types.CrossChainTx, 0) - pendingNoncesMap := make(map[int64]*observertypes.PendingNonces) - - // the criteria to stop adding cctxs to the rpc response - maxCCTXsReached := func() bool { - // #nosec G701 len always positive - return uint32(len(cctxs)) >= limit - } - - // query pending nonces for each foreign chain - // Note: The pending nonces could change during the RPC call, so query them beforehand - chains := k.zetaObserverKeeper.GetSupportedForeignChains(ctx) - for _, chain := range chains { - pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) - if !found { - return nil, status.Error(codes.Internal, "pending nonces not found") - } - pendingNoncesMap[chain.ChainId] = &pendingNonces - } - - // 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 - // therefore might mask some lower nonce cctx that is still pending. - pendingNonces := pendingNoncesMap[chain.ChainId] - startNonce := pendingNonces.NonceLow - 1 - endNonce := pendingNonces.NonceLow - 1000 - if endNonce < 0 { - endNonce = 0 - } - - // 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) - if err != nil { - return nil, err - } - - // 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 three criteria to stop scanning backwards: - // criteria #1: if rate limiter is disabled, we should stop exactly on the `endNonce` - if !applyLimit && nonce < endNonce { - break - } - if applyLimit { - // criteria #2: if rate limiter is enabled, we'll stop at the left window boundary if the `endNonce` hasn't hit it yet - // #nosec G701 always positive - if nonce < endNonce && cctx.InboundTxParams.InboundTxObservedExternalHeight < uint64(leftWindowBoundary) { - break - } - // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded - if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { - limitExceeded = true - break LoopBackwards - } - } - - // only take a `limit` number of pending cctxs as result but still count the total pending cctxs - if IsPending(cctx) { - totalPending++ - if !maxCCTXsReached() { - cctxs = append(cctxs, cctx) - } - } - } - - // 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 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] - 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 #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded - if applyLimit && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { - limitExceeded = true - break LoopForwards - } - cctxs = append(cctxs, cctx) - } - } - - return &types.QueryListCctxPendingWithRateLimitResponse{ - CrossChainTx: cctxs, - TotalPending: totalPending, - RateLimitExceeded: limitExceeded, - }, nil -} - // getCctxByChainIDAndNonce returns the cctx by chainID and nonce func getCctxByChainIDAndNonce(k Keeper, ctx sdk.Context, tssPubkey string, chainID int64, nonce int64) (*types.CrossChainTx, error) { nonceToCctx, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tssPubkey, chainID, nonce) @@ -341,79 +175,3 @@ func getCctxByChainIDAndNonce(k Keeper, ctx sdk.Context, tssPubkey string, chain } return &cctx, nil } - -// convertCctxValue converts the value of the cctx in ZETA using given conversion rates -func convertCctxValue( - chainID int64, - cctx *types.CrossChainTx, - gasCoinRates map[int64]sdk.Dec, - erc20CoinRates map[int64]map[string]sdk.Dec, - erc20Coins map[int64]map[string]fungibletypes.ForeignCoins, -) sdk.Dec { - var rate sdk.Dec - var decimals uint64 - switch cctx.InboundTxParams.CoinType { - case coin.CoinType_Zeta: - // no conversion needed for ZETA - rate = sdk.NewDec(1) - case coin.CoinType_Gas: - rate = gasCoinRates[chainID] - case coin.CoinType_ERC20: - // get the ERC20 coin decimals - _, found := erc20Coins[chainID] - if !found { - // skip if no coin found for this chainID - return sdk.NewDec(0) - } - fCoin, found := erc20Coins[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] - if !found { - // skip if no coin found for this Asset - return sdk.NewDec(0) - } - // #nosec G701 always in range - decimals = uint64(fCoin.Decimals) - - // get the ERC20 coin rate - _, found = erc20CoinRates[chainID] - if !found { - // skip if no rate found for this chainID - return sdk.NewDec(0) - } - rate = erc20CoinRates[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] - default: - // skip CoinType_Cmd - return sdk.NewDec(0) - } - - // should not happen, return 0 to skip if it happens - if rate.LTE(sdk.NewDec(0)) { - return sdk.NewDec(0) - } - - // the reciprocal of `rate` is the amount of zrc20 needed to buy 1 ZETA - // for example, given rate = 0.8, the reciprocal is 1.25, which means 1.25 ZRC20 can buy 1 ZETA - // given decimals = 6, the `oneZeta` amount will be 1.25 * 10^6 = 1250000 - oneZrc20 := sdk.NewDec(1).Power(decimals) - oneZeta := oneZrc20.Quo(rate) - - // convert asset amount into ZETA - amountCctx := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) - amountZeta := amountCctx.Quo(oneZeta) - return amountZeta -} - -// rateLimitExceeded accumulates the cctx value and then checks if the rate limit is exceeded -// returns true if the rate limit is exceeded -func rateLimitExceeded( - chainID int64, - cctx *types.CrossChainTx, - gasCoinRates map[int64]sdk.Dec, - erc20CoinRates map[int64]map[string]sdk.Dec, - erc20Coins map[int64]map[string]fungibletypes.ForeignCoins, - currentCctxValue *sdk.Dec, - rateLimitValue sdk.Dec, -) bool { - amountZeta := convertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates, erc20Coins) - *currentCctxValue = currentCctxValue.Add(amountZeta) - return currentCctxValue.GT(rateLimitValue) -} diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go new file mode 100644 index 0000000000..9f277cc556 --- /dev/null +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -0,0 +1,253 @@ +package keeper + +import ( + "context" + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/x/crosschain/types" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// CctxListPendingWithinRateLimit returns a list of pending cctxs that do not exceed the outbound rate limit +// a limit for the number of cctxs to return can be specified or the default is MaxPendingCctxs +func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.QueryListCctxPendingWithRateLimitRequest) (*types.QueryListCctxPendingWithRateLimitResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + // check limit and use default MaxPendingCctxs if not specified + if req.Limit > MaxPendingCctxs { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("limit exceeds max limit of %d", MaxPendingCctxs)) + } + limit := req.Limit + if limit == 0 { + limit = MaxPendingCctxs + } + + // get current height and tss + ctx := sdk.UnwrapSDKContext(c) + height := ctx.BlockHeight() + if height <= 0 { + return nil, status.Error(codes.OutOfRange, "height out of range") + } + tss, found := k.zetaObserverKeeper.GetTSS(ctx) + if !found { + return nil, status.Error(codes.Internal, "tss not found") + } + + // check rate limit flags to decide if we should apply rate limit + applyLimit := true + rateLimitFlags, found := k.GetRateLimiterFlags(ctx) + if !found || !rateLimitFlags.Enabled { + applyLimit = false + } + + // calculate the rate limiter sliding window left boundary (inclusive) + leftWindowBoundary := height - rateLimitFlags.Window + if leftWindowBoundary < 0 { + leftWindowBoundary = 0 + } + + // get the conversion rates for all foreign coins + var gasCoinRates map[int64]sdk.Dec + var erc20CoinRates map[int64]map[string]sdk.Dec + var erc20Coins map[int64]map[string]fungibletypes.ForeignCoins + var rateLimitInZeta sdk.Dec + if applyLimit { + gasCoinRates, erc20CoinRates = k.GetRateLimiterRates(ctx) + erc20Coins = k.fungibleKeeper.GetAllForeignERC20CoinMap(ctx) + rateLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()) + } + + // define a few variables to be used in the below loops + limitExceeded := false + totalPending := uint64(0) + totalCctxValueInZeta := sdk.NewDec(0) + cctxs := make([]*types.CrossChainTx, 0) + pendingNoncesMap := make(map[int64]*observertypes.PendingNonces) + + // the criteria to stop adding cctxs to the rpc response + maxCCTXsReached := func() bool { + // #nosec G701 len always positive + return uint32(len(cctxs)) >= limit + } + + // query pending nonces for each foreign chain + // Note: The pending nonces could change during the RPC call, so query them beforehand + chains := k.zetaObserverKeeper.GetSupportedForeignChains(ctx) + for _, chain := range chains { + pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) + if !found { + return nil, status.Error(codes.Internal, "pending nonces not found") + } + pendingNoncesMap[chain.ChainId] = &pendingNonces + } + + // 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 + // therefore might mask some lower nonce cctx that is still pending. + pendingNonces := pendingNoncesMap[chain.ChainId] + startNonce := pendingNonces.NonceLow - 1 + endNonce := pendingNonces.NonceLow - 1000 + if endNonce < 0 { + endNonce = 0 + } + + // 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) + if err != nil { + return nil, err + } + + // 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 three criteria to stop scanning backwards: + // criteria #1: if rate limiter is disabled, we should stop exactly on the `endNonce` + if !applyLimit && nonce < endNonce { + break + } + if applyLimit { + // criteria #2: if rate limiter is enabled, we'll stop at the left window boundary if the `endNonce` hasn't hit it yet + // #nosec G701 always positive + if nonce < endNonce && cctx.InboundTxParams.InboundTxObservedExternalHeight < uint64(leftWindowBoundary) { + break + } + // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded + if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { + limitExceeded = true + break LoopBackwards + } + } + + // only take a `limit` number of pending cctxs as result but still count the total pending cctxs + if IsPending(cctx) { + totalPending++ + if !maxCCTXsReached() { + cctxs = append(cctxs, cctx) + } + } + } + + // 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 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] + 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 #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded + if applyLimit && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { + limitExceeded = true + break LoopForwards + } + cctxs = append(cctxs, cctx) + } + } + + return &types.QueryListCctxPendingWithRateLimitResponse{ + CrossChainTx: cctxs, + TotalPending: totalPending, + RateLimitExceeded: limitExceeded, + }, nil +} + +// convertCctxValue converts the value of the cctx in ZETA using given conversion rates +func convertCctxValue( + chainID int64, + cctx *types.CrossChainTx, + gasCoinRates map[int64]sdk.Dec, + erc20CoinRates map[int64]map[string]sdk.Dec, + erc20Coins map[int64]map[string]fungibletypes.ForeignCoins, +) sdk.Dec { + var rate sdk.Dec + var decimals uint64 + switch cctx.InboundTxParams.CoinType { + case coin.CoinType_Zeta: + // no conversion needed for ZETA + rate = sdk.NewDec(1) + case coin.CoinType_Gas: + rate = gasCoinRates[chainID] + case coin.CoinType_ERC20: + // get the ERC20 coin decimals + _, found := erc20Coins[chainID] + if !found { + // skip if no coin found for this chainID + return sdk.NewDec(0) + } + fCoin, found := erc20Coins[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] + if !found { + // skip if no coin found for this Asset + return sdk.NewDec(0) + } + // #nosec G701 always in range + decimals = uint64(fCoin.Decimals) + + // get the ERC20 coin rate + _, found = erc20CoinRates[chainID] + if !found { + // skip if no rate found for this chainID + return sdk.NewDec(0) + } + rate = erc20CoinRates[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] + default: + // skip CoinType_Cmd + return sdk.NewDec(0) + } + + // should not happen, return 0 to skip if it happens + if rate.LTE(sdk.NewDec(0)) { + return sdk.NewDec(0) + } + + // the reciprocal of `rate` is the amount of zrc20 needed to buy 1 ZETA + // for example, given rate = 0.8, the reciprocal is 1.25, which means 1.25 ZRC20 can buy 1 ZETA + // given decimals = 6, the `oneZeta` amount will be 1.25 * 10^6 = 1250000 + oneZrc20 := sdk.NewDec(1).Power(decimals) + oneZeta := oneZrc20.Quo(rate) + + // convert asset amount into ZETA + amountCctx := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) + amountZeta := amountCctx.Quo(oneZeta) + return amountZeta +} + +// rateLimitExceeded accumulates the cctx value and then checks if the rate limit is exceeded +// returns true if the rate limit is exceeded +func rateLimitExceeded( + chainID int64, + cctx *types.CrossChainTx, + gasCoinRates map[int64]sdk.Dec, + erc20CoinRates map[int64]map[string]sdk.Dec, + erc20Coins map[int64]map[string]fungibletypes.ForeignCoins, + currentCctxValue *sdk.Dec, + rateLimitValue sdk.Dec, +) bool { + amountZeta := convertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates, erc20Coins) + *currentCctxValue = currentCctxValue.Add(amountZeta) + return currentCctxValue.GT(rateLimitValue) +} diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go new file mode 100644 index 0000000000..97f6664b9c --- /dev/null +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -0,0 +1,38 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/keeper" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestKeeper_CctxListPendingWithRateLimit(t *testing.T) { + t.Run("should fail for empty req", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + _, err := k.CctxListPendingWithinRateLimit(ctx, nil) + require.ErrorContains(t, err, "invalid request") + }) + t.Run("should fail if limit is too high", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: keeper.MaxPendingCctxs + 1}) + require.ErrorContains(t, err, "limit exceeds max limit of") + }) + t.Run("should fail if no TSS", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: 1}) + require.ErrorContains(t, err, "tss not found") + }) + t.Run("should return empty list if no nonces", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // set TSS + zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) + + _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: 1}) + require.ErrorContains(t, err, "pending nonces not found") + }) +} diff --git a/x/crosschain/keeper/grpc_query_cctx_test.go b/x/crosschain/keeper/grpc_query_cctx_test.go index 883315c0a0..59b0b68863 100644 --- a/x/crosschain/keeper/grpc_query_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_test.go @@ -185,36 +185,6 @@ func TestKeeper_CctxListPending(t *testing.T) { }) } -func TestKeeper_CctxListPendingWithRateLimit(t *testing.T) { - t.Run("should fail for empty req", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeper(t) - _, err := k.CctxListPendingWithinRateLimit(ctx, nil) - require.ErrorContains(t, err, "invalid request") - }) - - t.Run("should fail if limit is too high", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeper(t) - _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: keeper.MaxPendingCctxs + 1}) - require.ErrorContains(t, err, "limit exceeds max limit of") - }) - - t.Run("should fail if no TSS", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeper(t) - _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: 1}) - require.ErrorContains(t, err, "tss not found") - }) - - t.Run("should return empty list if no nonces", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) - - // set TSS - zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) - - _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: 1}) - require.ErrorContains(t, err, "pending nonces not found") - }) -} - func TestKeeper_ZetaAccounting(t *testing.T) { t.Run("should error if zeta accounting not found", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) From 6ad94af0ccf6399dbf75ed82b1a52178fc30ec1b Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 19 Apr 2024 12:16:54 -0500 Subject: [PATCH 09/40] improved a few error handlling --- x/crosschain/keeper/grpc_query_cctx.go | 11 ++++------- x/crosschain/keeper/grpc_query_cctx_rate_limit.go | 12 ++++-------- .../keeper/grpc_query_cctx_rate_limit_test.go | 4 ++-- x/crosschain/keeper/grpc_query_cctx_test.go | 4 ++-- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index 5e916beeac..0cc659869c 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -93,21 +94,17 @@ func (k Keeper) CctxListPending(c context.Context, req *types.QueryListCctxPendi return nil, status.Error(codes.InvalidArgument, "invalid request") } - // check limit and use default MaxPendingCctxs if not specified - if req.Limit > MaxPendingCctxs { - return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("limit exceeds max limit of %d", MaxPendingCctxs)) - } + // use default MaxPendingCctxs if not specified or too high limit := req.Limit - if limit == 0 { + if limit == 0 || limit > MaxPendingCctxs { limit = MaxPendingCctxs } - ctx := sdk.UnwrapSDKContext(c) // query the nonces that are pending tss, found := k.zetaObserverKeeper.GetTSS(ctx) if !found { - return nil, status.Error(codes.Internal, "tss not found") + return nil, observertypes.ErrTssNotFound } pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, req.ChainId) if !found { diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 9f277cc556..78f73b9d78 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -2,7 +2,6 @@ package keeper import ( "context" - "fmt" "strings" sdk "github.com/cosmos/cosmos-sdk/types" @@ -21,24 +20,21 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que return nil, status.Error(codes.InvalidArgument, "invalid request") } - // check limit and use default MaxPendingCctxs if not specified - if req.Limit > MaxPendingCctxs { - return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("limit exceeds max limit of %d", MaxPendingCctxs)) - } + // use default MaxPendingCctxs if not specified or too high limit := req.Limit - if limit == 0 { + if limit == 0 || limit > MaxPendingCctxs { limit = MaxPendingCctxs } + ctx := sdk.UnwrapSDKContext(c) // get current height and tss - ctx := sdk.UnwrapSDKContext(c) height := ctx.BlockHeight() if height <= 0 { return nil, status.Error(codes.OutOfRange, "height out of range") } tss, found := k.zetaObserverKeeper.GetTSS(ctx) if !found { - return nil, status.Error(codes.Internal, "tss not found") + return nil, observertypes.ErrTssNotFound } // check rate limit flags to decide if we should apply rate limit 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 97f6664b9c..1f994872d8 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -16,10 +16,10 @@ func TestKeeper_CctxListPendingWithRateLimit(t *testing.T) { _, err := k.CctxListPendingWithinRateLimit(ctx, nil) require.ErrorContains(t, err, "invalid request") }) - t.Run("should fail if limit is too high", func(t *testing.T) { + t.Run("should use max limit if limit is too high", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: keeper.MaxPendingCctxs + 1}) - require.ErrorContains(t, err, "limit exceeds max limit of") + require.ErrorContains(t, err, "tss not found") }) t.Run("should fail if no TSS", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) diff --git a/x/crosschain/keeper/grpc_query_cctx_test.go b/x/crosschain/keeper/grpc_query_cctx_test.go index 59b0b68863..2387f9dc3a 100644 --- a/x/crosschain/keeper/grpc_query_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_test.go @@ -70,10 +70,10 @@ func TestKeeper_CctxListPending(t *testing.T) { require.ErrorContains(t, err, "invalid request") }) - t.Run("should fail if limit is too high", func(t *testing.T) { + t.Run("should use max limit if limit is too high", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) _, err := k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{Limit: keeper.MaxPendingCctxs + 1}) - require.ErrorContains(t, err, "limit exceeds max limit of") + require.ErrorContains(t, err, "tss not found") }) t.Run("should fail if no TSS", func(t *testing.T) { From 27e94a74a22d6f31bfb64ad3f5119e9c68135bf1 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 19 Apr 2024 15:16:40 -0500 Subject: [PATCH 10/40] use old cctx query as fallback when rate limiter is disabled; some renaming --- cmd/zetae2e/stress.go | 2 +- docs/openapi/openapi.swagger.yaml | 98 ++-- proto/crosschain/query.proto | 20 +- typescript/crosschain/query_pb.d.ts | 66 +-- x/crosschain/client/cli/cli_cctx.go | 4 +- x/crosschain/keeper/abci.go | 2 +- x/crosschain/keeper/grpc_query_cctx.go | 6 +- .../keeper/grpc_query_cctx_rate_limit.go | 78 +-- .../keeper/grpc_query_cctx_rate_limit_test.go | 24 +- x/crosschain/keeper/grpc_query_cctx_test.go | 18 +- x/crosschain/types/query.pb.go | 505 +++++++++--------- x/crosschain/types/query.pb.gw.go | 68 +-- zetaclient/interfaces/interfaces.go | 2 +- zetaclient/testutils/stub/core_bridge.go | 2 +- zetaclient/zetabridge/query.go | 12 +- zetaclient/zetacore_observer.go | 2 +- 16 files changed, 449 insertions(+), 460 deletions(-) diff --git a/cmd/zetae2e/stress.go b/cmd/zetae2e/stress.go index 5228905074..f8ca5b9f28 100644 --- a/cmd/zetae2e/stress.go +++ b/cmd/zetae2e/stress.go @@ -252,7 +252,7 @@ func EchoNetworkMetrics(runner *runner.E2ERunner) { case <-ticker.C: numTicks++ // Get all pending outbound transactions - cctxResp, err := runner.CctxClient.CctxListPending(context.Background(), &crosschaintypes.QueryListCctxPendingRequest{ + cctxResp, err := runner.CctxClient.ListPendingCctx(context.Background(), &crosschaintypes.QueryListPendingCctxRequest{ ChainId: chainID.Int64(), }) if err != nil { diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 1d9296f6a1..1eb8fe4593 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -26582,53 +26582,6 @@ paths: type: string tags: - Query - /zeta-chain/crosschain/cctxPending: - get: - summary: Queries a list of pending cctxs. - operationId: Query_CctxListPending - responses: - "200": - description: A successful response. - schema: - $ref: '#/definitions/crosschainQueryListCctxPendingResponse' - default: - description: An unexpected error response. - schema: - $ref: '#/definitions/googlerpcStatus' - parameters: - - name: chain_id - in: query - required: false - type: string - format: int64 - - name: limit - in: query - required: false - type: integer - format: int64 - tags: - - Query - /zeta-chain/crosschain/cctxPendingWithRateLimit: - get: - summary: Queries a list of pending cctxs with rate limit. - operationId: Query_CctxListPendingWithinRateLimit - responses: - "200": - description: A successful response. - schema: - $ref: '#/definitions/crosschainQueryListCctxPendingWithRateLimitResponse' - default: - description: An unexpected error response. - schema: - $ref: '#/definitions/googlerpcStatus' - parameters: - - name: limit - in: query - required: false - type: integer - format: int64 - tags: - - Query /zeta-chain/crosschain/convertGasToZeta: get: operationId: Query_ConvertGasToZeta @@ -27194,6 +27147,53 @@ paths: type: boolean tags: - Query + /zeta-chain/crosschain/pendingCctx: + get: + summary: Queries a list of pending cctxs. + operationId: Query_ListPendingCctx + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/crosschainQueryListPendingCctxResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: chain_id + in: query + required: false + type: string + format: int64 + - name: limit + in: query + required: false + type: integer + format: int64 + tags: + - Query + /zeta-chain/crosschain/pendingCctxWithinRateLimit: + get: + summary: Queries a list of pending cctxs within rate limit. + operationId: Query_ListPendingCctxWithinRateLimit + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/crosschainQueryListPendingCctxWithinRateLimitResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: limit + in: query + required: false + type: integer + format: int64 + tags: + - Query /zeta-chain/crosschain/protocolFee: get: operationId: Query_ProtocolFee @@ -53991,7 +53991,7 @@ definitions: Height: type: string format: int64 - crosschainQueryListCctxPendingResponse: + crosschainQueryListPendingCctxResponse: type: object properties: CrossChainTx: @@ -54002,7 +54002,7 @@ definitions: totalPending: type: string format: uint64 - crosschainQueryListCctxPendingWithRateLimitResponse: + crosschainQueryListPendingCctxWithinRateLimitResponse: type: object properties: cross_chain_tx: diff --git a/proto/crosschain/query.proto b/proto/crosschain/query.proto index b4c6a9a753..b017e2c523 100644 --- a/proto/crosschain/query.proto +++ b/proto/crosschain/query.proto @@ -95,13 +95,13 @@ service Query { } // Queries a list of pending cctxs. - rpc CctxListPending(QueryListCctxPendingRequest) returns (QueryListCctxPendingResponse) { - option (google.api.http).get = "/zeta-chain/crosschain/cctxPending"; + rpc ListPendingCctx(QueryListPendingCctxRequest) returns (QueryListPendingCctxResponse) { + option (google.api.http).get = "/zeta-chain/crosschain/pendingCctx"; } - // Queries a list of pending cctxs with rate limit. - rpc CctxListPendingWithinRateLimit(QueryListCctxPendingWithRateLimitRequest) returns (QueryListCctxPendingWithRateLimitResponse) { - option (google.api.http).get = "/zeta-chain/crosschain/cctxPendingWithRateLimit"; + // Queries a list of pending cctxs within rate limit. + rpc ListPendingCctxWithinRateLimit(QueryListPendingCctxWithinRateLimitRequest) returns (QueryListPendingCctxWithinRateLimitResponse) { + option (google.api.http).get = "/zeta-chain/crosschain/pendingCctxWithinRateLimit"; } rpc ZetaAccounting(QueryZetaAccountingRequest) returns (QueryZetaAccountingResponse) { @@ -253,21 +253,21 @@ message QueryAllCctxResponse { cosmos.base.query.v1beta1.PageResponse pagination = 2; } -message QueryListCctxPendingRequest { +message QueryListPendingCctxRequest { int64 chain_id = 1; uint32 limit = 2; } -message QueryListCctxPendingResponse { +message QueryListPendingCctxResponse { repeated CrossChainTx CrossChainTx = 1; uint64 totalPending = 2; } -message QueryListCctxPendingWithRateLimitRequest { - uint32 limit = 2; +message QueryListPendingCctxWithinRateLimitRequest { + uint32 limit = 1; } -message QueryListCctxPendingWithRateLimitResponse { +message QueryListPendingCctxWithinRateLimitResponse { repeated CrossChainTx cross_chain_tx = 1; uint64 total_pending = 2; bool rate_limit_exceeded = 3; diff --git a/typescript/crosschain/query_pb.d.ts b/typescript/crosschain/query_pb.d.ts index cc093b0b13..34b4a44f38 100644 --- a/typescript/crosschain/query_pb.d.ts +++ b/typescript/crosschain/query_pb.d.ts @@ -814,9 +814,9 @@ export declare class QueryAllCctxResponse extends Message } /** - * @generated from message zetachain.zetacore.crosschain.QueryListCctxPendingRequest + * @generated from message zetachain.zetacore.crosschain.QueryListPendingCctxRequest */ -export declare class QueryListCctxPendingRequest extends Message { +export declare class QueryListPendingCctxRequest extends Message { /** * @generated from field: int64 chain_id = 1; */ @@ -827,25 +827,25 @@ export declare class QueryListCctxPendingRequest extends Message); + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.QueryListCctxPendingRequest"; + static readonly typeName = "zetachain.zetacore.crosschain.QueryListPendingCctxRequest"; static readonly fields: FieldList; - static fromBinary(bytes: Uint8Array, options?: Partial): QueryListCctxPendingRequest; + static fromBinary(bytes: Uint8Array, options?: Partial): QueryListPendingCctxRequest; - static fromJson(jsonValue: JsonValue, options?: Partial): QueryListCctxPendingRequest; + static fromJson(jsonValue: JsonValue, options?: Partial): QueryListPendingCctxRequest; - static fromJsonString(jsonString: string, options?: Partial): QueryListCctxPendingRequest; + static fromJsonString(jsonString: string, options?: Partial): QueryListPendingCctxRequest; - static equals(a: QueryListCctxPendingRequest | PlainMessage | undefined, b: QueryListCctxPendingRequest | PlainMessage | undefined): boolean; + static equals(a: QueryListPendingCctxRequest | PlainMessage | undefined, b: QueryListPendingCctxRequest | PlainMessage | undefined): boolean; } /** - * @generated from message zetachain.zetacore.crosschain.QueryListCctxPendingResponse + * @generated from message zetachain.zetacore.crosschain.QueryListPendingCctxResponse */ -export declare class QueryListCctxPendingResponse extends Message { +export declare class QueryListPendingCctxResponse extends Message { /** * @generated from field: repeated zetachain.zetacore.crosschain.CrossChainTx CrossChainTx = 1; */ @@ -856,49 +856,49 @@ export declare class QueryListCctxPendingResponse extends Message); + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.QueryListCctxPendingResponse"; + static readonly typeName = "zetachain.zetacore.crosschain.QueryListPendingCctxResponse"; static readonly fields: FieldList; - static fromBinary(bytes: Uint8Array, options?: Partial): QueryListCctxPendingResponse; + static fromBinary(bytes: Uint8Array, options?: Partial): QueryListPendingCctxResponse; - static fromJson(jsonValue: JsonValue, options?: Partial): QueryListCctxPendingResponse; + static fromJson(jsonValue: JsonValue, options?: Partial): QueryListPendingCctxResponse; - static fromJsonString(jsonString: string, options?: Partial): QueryListCctxPendingResponse; + static fromJsonString(jsonString: string, options?: Partial): QueryListPendingCctxResponse; - static equals(a: QueryListCctxPendingResponse | PlainMessage | undefined, b: QueryListCctxPendingResponse | PlainMessage | undefined): boolean; + static equals(a: QueryListPendingCctxResponse | PlainMessage | undefined, b: QueryListPendingCctxResponse | PlainMessage | undefined): boolean; } /** - * @generated from message zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitRequest + * @generated from message zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitRequest */ -export declare class QueryListCctxPendingWithRateLimitRequest extends Message { +export declare class QueryListPendingCctxWithinRateLimitRequest extends Message { /** - * @generated from field: uint32 limit = 2; + * @generated from field: uint32 limit = 1; */ limit: number; - constructor(data?: PartialMessage); + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitRequest"; + static readonly typeName = "zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitRequest"; static readonly fields: FieldList; - static fromBinary(bytes: Uint8Array, options?: Partial): QueryListCctxPendingWithRateLimitRequest; + static fromBinary(bytes: Uint8Array, options?: Partial): QueryListPendingCctxWithinRateLimitRequest; - static fromJson(jsonValue: JsonValue, options?: Partial): QueryListCctxPendingWithRateLimitRequest; + static fromJson(jsonValue: JsonValue, options?: Partial): QueryListPendingCctxWithinRateLimitRequest; - static fromJsonString(jsonString: string, options?: Partial): QueryListCctxPendingWithRateLimitRequest; + static fromJsonString(jsonString: string, options?: Partial): QueryListPendingCctxWithinRateLimitRequest; - static equals(a: QueryListCctxPendingWithRateLimitRequest | PlainMessage | undefined, b: QueryListCctxPendingWithRateLimitRequest | PlainMessage | undefined): boolean; + static equals(a: QueryListPendingCctxWithinRateLimitRequest | PlainMessage | undefined, b: QueryListPendingCctxWithinRateLimitRequest | PlainMessage | undefined): boolean; } /** - * @generated from message zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitResponse + * @generated from message zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitResponse */ -export declare class QueryListCctxPendingWithRateLimitResponse extends Message { +export declare class QueryListPendingCctxWithinRateLimitResponse extends Message { /** * @generated from field: repeated zetachain.zetacore.crosschain.CrossChainTx cross_chain_tx = 1; */ @@ -914,19 +914,19 @@ export declare class QueryListCctxPendingWithRateLimitResponse extends Message); + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitResponse"; + static readonly typeName = "zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitResponse"; static readonly fields: FieldList; - static fromBinary(bytes: Uint8Array, options?: Partial): QueryListCctxPendingWithRateLimitResponse; + static fromBinary(bytes: Uint8Array, options?: Partial): QueryListPendingCctxWithinRateLimitResponse; - static fromJson(jsonValue: JsonValue, options?: Partial): QueryListCctxPendingWithRateLimitResponse; + static fromJson(jsonValue: JsonValue, options?: Partial): QueryListPendingCctxWithinRateLimitResponse; - static fromJsonString(jsonString: string, options?: Partial): QueryListCctxPendingWithRateLimitResponse; + static fromJsonString(jsonString: string, options?: Partial): QueryListPendingCctxWithinRateLimitResponse; - static equals(a: QueryListCctxPendingWithRateLimitResponse | PlainMessage | undefined, b: QueryListCctxPendingWithRateLimitResponse | PlainMessage | undefined): boolean; + static equals(a: QueryListPendingCctxWithinRateLimitResponse | PlainMessage | undefined, b: QueryListPendingCctxWithinRateLimitResponse | PlainMessage | undefined): boolean; } /** diff --git a/x/crosschain/client/cli/cli_cctx.go b/x/crosschain/client/cli/cli_cctx.go index 87991442d1..b5add8610b 100644 --- a/x/crosschain/client/cli/cli_cctx.go +++ b/x/crosschain/client/cli/cli_cctx.go @@ -69,13 +69,13 @@ func CmdPendingCctx() *cobra.Command { return err } - params := &types.QueryListCctxPendingRequest{ + params := &types.QueryListPendingCctxRequest{ ChainId: chainID, // #nosec G701 bit size verified Limit: uint32(limit), } - res, err := queryClient.CctxListPending(context.Background(), params) + res, err := queryClient.ListPendingCctx(context.Background(), params) if err != nil { return err } diff --git a/x/crosschain/keeper/abci.go b/x/crosschain/keeper/abci.go index 5a02c53676..f893e795bb 100644 --- a/x/crosschain/keeper/abci.go +++ b/x/crosschain/keeper/abci.go @@ -51,7 +51,7 @@ IterateChains: for _, chain := range chains { // support only external evm chains if zetachains.IsEVMChain(chain.ChainId) && !zetachains.IsZetaChain(chain.ChainId) { - res, err := k.CctxListPending(sdk.UnwrapSDKContext(ctx), &types.QueryListCctxPendingRequest{ + res, err := k.ListPendingCctx(sdk.UnwrapSDKContext(ctx), &types.QueryListPendingCctxRequest{ ChainId: chain.ChainId, Limit: gasPriceIncreaseFlags.MaxPendingCctxs, }) diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index 0cc659869c..8e96757b44 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -87,9 +87,9 @@ func (k Keeper) CctxByNonce(c context.Context, req *types.QueryGetCctxByNonceReq return &types.QueryGetCctxResponse{CrossChainTx: cctx}, nil } -// CctxListPending returns a list of pending cctxs and the total number of pending cctxs +// ListPendingCctx returns a list of pending cctxs and the total number of pending cctxs // a limit for the number of cctxs to return can be specified or the default is MaxPendingCctxs -func (k Keeper) CctxListPending(c context.Context, req *types.QueryListCctxPendingRequest) (*types.QueryListCctxPendingResponse, error) { +func (k Keeper) ListPendingCctx(c context.Context, req *types.QueryListPendingCctxRequest) (*types.QueryListPendingCctxResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } @@ -154,7 +154,7 @@ func (k Keeper) CctxListPending(c context.Context, req *types.QueryListCctxPendi cctxs = append(cctxs, cctx) } - return &types.QueryListCctxPendingResponse{ + return &types.QueryListPendingCctxResponse{ CrossChainTx: cctxs, TotalPending: totalPending, }, nil diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 78f73b9d78..b3b30ca939 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -13,9 +13,9 @@ import ( "google.golang.org/grpc/status" ) -// CctxListPendingWithinRateLimit returns a list of pending cctxs that do not exceed the outbound rate limit +// ListPendingCctxWithinRateLimit returns a list of pending cctxs that do not exceed the outbound rate limit // a limit for the number of cctxs to return can be specified or the default is MaxPendingCctxs -func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.QueryListCctxPendingWithRateLimitRequest) (*types.QueryListCctxPendingWithRateLimitResponse, error) { +func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.QueryListPendingCctxWithinRateLimitRequest) (*types.QueryListPendingCctxWithinRateLimitResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } @@ -27,6 +27,36 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que } ctx := sdk.UnwrapSDKContext(c) + // define a few variables to be used in the query loops + limitExceeded := false + totalPending := uint64(0) + totalCctxValueInZeta := sdk.NewDec(0) + cctxs := make([]*types.CrossChainTx, 0) + chains := k.zetaObserverKeeper.GetSupportedForeignChains(ctx) + + // check rate limit flags to decide if we should apply rate limit + applyLimit := true + rateLimitFlags, found := k.GetRateLimiterFlags(ctx) + if !found || !rateLimitFlags.Enabled { + applyLimit = false + } + + // fallback to non-rate-limited query if rate limiter is disabled + if !applyLimit { + for _, chain := range chains { + resp, err := k.ListPendingCctx(ctx, &types.QueryListPendingCctxRequest{ChainId: chain.ChainId, Limit: limit}) + if err == nil { + cctxs = append(cctxs, resp.CrossChainTx...) + totalPending += resp.TotalPending + } + } + return &types.QueryListPendingCctxWithinRateLimitResponse{ + CrossChainTx: cctxs, + TotalPending: totalPending, + RateLimitExceeded: false, + }, nil + } + // get current height and tss height := ctx.BlockHeight() if height <= 0 { @@ -37,13 +67,6 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que return nil, observertypes.ErrTssNotFound } - // check rate limit flags to decide if we should apply rate limit - applyLimit := true - rateLimitFlags, found := k.GetRateLimiterFlags(ctx) - if !found || !rateLimitFlags.Enabled { - applyLimit = false - } - // calculate the rate limiter sliding window left boundary (inclusive) leftWindowBoundary := height - rateLimitFlags.Window if leftWindowBoundary < 0 { @@ -61,13 +84,6 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que rateLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()) } - // define a few variables to be used in the below loops - limitExceeded := false - totalPending := uint64(0) - totalCctxValueInZeta := sdk.NewDec(0) - cctxs := make([]*types.CrossChainTx, 0) - pendingNoncesMap := make(map[int64]*observertypes.PendingNonces) - // the criteria to stop adding cctxs to the rpc response maxCCTXsReached := func() bool { // #nosec G701 len always positive @@ -76,7 +92,7 @@ func (k Keeper) CctxListPendingWithinRateLimit(c context.Context, req *types.Que // query pending nonces for each foreign chain // Note: The pending nonces could change during the RPC call, so query them beforehand - chains := k.zetaObserverKeeper.GetSupportedForeignChains(ctx) + pendingNoncesMap := make(map[int64]*observertypes.PendingNonces) for _, chain := range chains { pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) if !found { @@ -107,22 +123,16 @@ LoopBackwards: // 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 three criteria to stop scanning backwards: - // criteria #1: if rate limiter is disabled, we should stop exactly on the `endNonce` - if !applyLimit && nonce < endNonce { + // 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 + // #nosec G701 always positive + if nonce < endNonce && cctx.InboundTxParams.InboundTxObservedExternalHeight < uint64(leftWindowBoundary) { break } - if applyLimit { - // criteria #2: if rate limiter is enabled, we'll stop at the left window boundary if the `endNonce` hasn't hit it yet - // #nosec G701 always positive - if nonce < endNonce && cctx.InboundTxParams.InboundTxObservedExternalHeight < uint64(leftWindowBoundary) { - break - } - // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded - if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { - limitExceeded = true - break LoopBackwards - } + // criteria #2: we should finish the RPC call if the rate limit is exceeded + if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { + limitExceeded = true + break LoopBackwards } // only take a `limit` number of pending cctxs as result but still count the total pending cctxs @@ -156,8 +166,8 @@ LoopForwards: if maxCCTXsReached() { break LoopForwards } - // criteria #3: if rate limiter is enabled, we should finish the RPC call if the rate limit is exceeded - if applyLimit && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { + // criteria #2: we should finish the RPC call if the rate limit is exceeded + if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true break LoopForwards } @@ -165,7 +175,7 @@ LoopForwards: } } - return &types.QueryListCctxPendingWithRateLimitResponse{ + return &types.QueryListPendingCctxWithinRateLimitResponse{ CrossChainTx: cctxs, TotalPending: totalPending, RateLimitExceeded: limitExceeded, 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 1f994872d8..5c2eee4066 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -5,34 +5,12 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" - "github.com/zeta-chain/zetacore/testutil/sample" - "github.com/zeta-chain/zetacore/x/crosschain/keeper" - "github.com/zeta-chain/zetacore/x/crosschain/types" ) func TestKeeper_CctxListPendingWithRateLimit(t *testing.T) { t.Run("should fail for empty req", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) - _, err := k.CctxListPendingWithinRateLimit(ctx, nil) + _, err := k.ListPendingCctxWithinRateLimit(ctx, nil) require.ErrorContains(t, err, "invalid request") }) - t.Run("should use max limit if limit is too high", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeper(t) - _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: keeper.MaxPendingCctxs + 1}) - require.ErrorContains(t, err, "tss not found") - }) - t.Run("should fail if no TSS", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeper(t) - _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: 1}) - require.ErrorContains(t, err, "tss not found") - }) - t.Run("should return empty list if no nonces", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) - - // set TSS - zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) - - _, err := k.CctxListPendingWithinRateLimit(ctx, &types.QueryListCctxPendingWithRateLimitRequest{Limit: 1}) - require.ErrorContains(t, err, "pending nonces not found") - }) } diff --git a/x/crosschain/keeper/grpc_query_cctx_test.go b/x/crosschain/keeper/grpc_query_cctx_test.go index 2387f9dc3a..240d0bfe92 100644 --- a/x/crosschain/keeper/grpc_query_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_test.go @@ -66,19 +66,19 @@ func createCctxWithNonceRange( func TestKeeper_CctxListPending(t *testing.T) { t.Run("should fail for empty req", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) - _, err := k.CctxListPending(ctx, nil) + _, err := k.ListPendingCctx(ctx, nil) require.ErrorContains(t, err, "invalid request") }) t.Run("should use max limit if limit is too high", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) - _, err := k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{Limit: keeper.MaxPendingCctxs + 1}) + _, err := k.ListPendingCctx(ctx, &types.QueryListPendingCctxRequest{Limit: keeper.MaxPendingCctxs + 1}) require.ErrorContains(t, err, "tss not found") }) t.Run("should fail if no TSS", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) - _, err := k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{Limit: 1}) + _, err := k.ListPendingCctx(ctx, &types.QueryListPendingCctxRequest{Limit: 1}) require.ErrorContains(t, err, "tss not found") }) @@ -88,7 +88,7 @@ func TestKeeper_CctxListPending(t *testing.T) { // set TSS zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) - _, err := k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{Limit: 1}) + _, err := k.ListPendingCctx(ctx, &types.QueryListPendingCctxRequest{Limit: 1}) require.ErrorContains(t, err, "pending nonces not found") }) @@ -99,13 +99,13 @@ func TestKeeper_CctxListPending(t *testing.T) { zk.ObserverKeeper.SetTSS(ctx, tss) cctxs := createCctxWithNonceRange(t, ctx, *k, 1000, 2000, chainID, tss, zk) - res, err := k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{ChainId: chainID, Limit: 100}) + res, err := k.ListPendingCctx(ctx, &types.QueryListPendingCctxRequest{ChainId: chainID, Limit: 100}) require.NoError(t, err) require.Equal(t, 100, len(res.CrossChainTx)) require.EqualValues(t, cctxs[0:100], res.CrossChainTx) require.EqualValues(t, uint64(1000), res.TotalPending) - res, err = k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{ChainId: chainID}) + res, err = k.ListPendingCctx(ctx, &types.QueryListPendingCctxRequest{ChainId: chainID}) require.NoError(t, err) require.Equal(t, keeper.MaxPendingCctxs, len(res.CrossChainTx)) require.EqualValues(t, cctxs[0:keeper.MaxPendingCctxs], res.CrossChainTx) @@ -119,7 +119,7 @@ func TestKeeper_CctxListPending(t *testing.T) { zk.ObserverKeeper.SetTSS(ctx, tss) cctxs := createCctxWithNonceRange(t, ctx, *k, 1000, 1100, chainID, tss, zk) - res, err := k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{ChainId: chainID}) + res, err := k.ListPendingCctx(ctx, &types.QueryListPendingCctxRequest{ChainId: chainID}) require.NoError(t, err) require.Equal(t, 100, len(res.CrossChainTx)) require.EqualValues(t, cctxs, res.CrossChainTx) @@ -144,7 +144,7 @@ func TestKeeper_CctxListPending(t *testing.T) { cctx2.CctxStatus.Status = types.CctxStatus_PendingOutbound k.SetCrossChainTx(ctx, cctx2) - res, err := k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{ChainId: chainID, Limit: 100}) + res, err := k.ListPendingCctx(ctx, &types.QueryListPendingCctxRequest{ChainId: chainID, Limit: 100}) require.NoError(t, err) require.Equal(t, 100, len(res.CrossChainTx)) @@ -173,7 +173,7 @@ func TestKeeper_CctxListPending(t *testing.T) { cctx2.CctxStatus.Status = types.CctxStatus_PendingOutbound k.SetCrossChainTx(ctx, cctx2) - res, err := k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{ChainId: chainID, Limit: 100}) + res, err := k.ListPendingCctx(ctx, &types.QueryListPendingCctxRequest{ChainId: chainID, Limit: 100}) require.NoError(t, err) require.Equal(t, 100, len(res.CrossChainTx)) diff --git a/x/crosschain/types/query.pb.go b/x/crosschain/types/query.pb.go index 7d88a5f6df..879dcce901 100644 --- a/x/crosschain/types/query.pb.go +++ b/x/crosschain/types/query.pb.go @@ -1483,23 +1483,23 @@ func (m *QueryAllCctxResponse) GetPagination() *query.PageResponse { return nil } -type QueryListCctxPendingRequest struct { +type QueryListPendingCctxRequest struct { ChainId int64 `protobuf:"varint,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` Limit uint32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` } -func (m *QueryListCctxPendingRequest) Reset() { *m = QueryListCctxPendingRequest{} } -func (m *QueryListCctxPendingRequest) String() string { return proto.CompactTextString(m) } -func (*QueryListCctxPendingRequest) ProtoMessage() {} -func (*QueryListCctxPendingRequest) Descriptor() ([]byte, []int) { +func (m *QueryListPendingCctxRequest) Reset() { *m = QueryListPendingCctxRequest{} } +func (m *QueryListPendingCctxRequest) String() string { return proto.CompactTextString(m) } +func (*QueryListPendingCctxRequest) ProtoMessage() {} +func (*QueryListPendingCctxRequest) Descriptor() ([]byte, []int) { return fileDescriptor_65a992045e92a606, []int{31} } -func (m *QueryListCctxPendingRequest) XXX_Unmarshal(b []byte) error { +func (m *QueryListPendingCctxRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryListCctxPendingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryListPendingCctxRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryListCctxPendingRequest.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryListPendingCctxRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1509,49 +1509,49 @@ func (m *QueryListCctxPendingRequest) XXX_Marshal(b []byte, deterministic bool) return b[:n], nil } } -func (m *QueryListCctxPendingRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryListCctxPendingRequest.Merge(m, src) +func (m *QueryListPendingCctxRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListPendingCctxRequest.Merge(m, src) } -func (m *QueryListCctxPendingRequest) XXX_Size() int { +func (m *QueryListPendingCctxRequest) XXX_Size() int { return m.Size() } -func (m *QueryListCctxPendingRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryListCctxPendingRequest.DiscardUnknown(m) +func (m *QueryListPendingCctxRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListPendingCctxRequest.DiscardUnknown(m) } -var xxx_messageInfo_QueryListCctxPendingRequest proto.InternalMessageInfo +var xxx_messageInfo_QueryListPendingCctxRequest proto.InternalMessageInfo -func (m *QueryListCctxPendingRequest) GetChainId() int64 { +func (m *QueryListPendingCctxRequest) GetChainId() int64 { if m != nil { return m.ChainId } return 0 } -func (m *QueryListCctxPendingRequest) GetLimit() uint32 { +func (m *QueryListPendingCctxRequest) GetLimit() uint32 { if m != nil { return m.Limit } return 0 } -type QueryListCctxPendingResponse struct { +type QueryListPendingCctxResponse struct { CrossChainTx []*CrossChainTx `protobuf:"bytes,1,rep,name=CrossChainTx,proto3" json:"CrossChainTx,omitempty"` TotalPending uint64 `protobuf:"varint,2,opt,name=totalPending,proto3" json:"totalPending,omitempty"` } -func (m *QueryListCctxPendingResponse) Reset() { *m = QueryListCctxPendingResponse{} } -func (m *QueryListCctxPendingResponse) String() string { return proto.CompactTextString(m) } -func (*QueryListCctxPendingResponse) ProtoMessage() {} -func (*QueryListCctxPendingResponse) Descriptor() ([]byte, []int) { +func (m *QueryListPendingCctxResponse) Reset() { *m = QueryListPendingCctxResponse{} } +func (m *QueryListPendingCctxResponse) String() string { return proto.CompactTextString(m) } +func (*QueryListPendingCctxResponse) ProtoMessage() {} +func (*QueryListPendingCctxResponse) Descriptor() ([]byte, []int) { return fileDescriptor_65a992045e92a606, []int{32} } -func (m *QueryListCctxPendingResponse) XXX_Unmarshal(b []byte) error { +func (m *QueryListPendingCctxResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryListCctxPendingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryListPendingCctxResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryListCctxPendingResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryListPendingCctxResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1561,50 +1561,52 @@ func (m *QueryListCctxPendingResponse) XXX_Marshal(b []byte, deterministic bool) return b[:n], nil } } -func (m *QueryListCctxPendingResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryListCctxPendingResponse.Merge(m, src) +func (m *QueryListPendingCctxResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListPendingCctxResponse.Merge(m, src) } -func (m *QueryListCctxPendingResponse) XXX_Size() int { +func (m *QueryListPendingCctxResponse) XXX_Size() int { return m.Size() } -func (m *QueryListCctxPendingResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryListCctxPendingResponse.DiscardUnknown(m) +func (m *QueryListPendingCctxResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListPendingCctxResponse.DiscardUnknown(m) } -var xxx_messageInfo_QueryListCctxPendingResponse proto.InternalMessageInfo +var xxx_messageInfo_QueryListPendingCctxResponse proto.InternalMessageInfo -func (m *QueryListCctxPendingResponse) GetCrossChainTx() []*CrossChainTx { +func (m *QueryListPendingCctxResponse) GetCrossChainTx() []*CrossChainTx { if m != nil { return m.CrossChainTx } return nil } -func (m *QueryListCctxPendingResponse) GetTotalPending() uint64 { +func (m *QueryListPendingCctxResponse) GetTotalPending() uint64 { if m != nil { return m.TotalPending } return 0 } -type QueryListCctxPendingWithRateLimitRequest struct { - Limit uint32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` +type QueryListPendingCctxWithinRateLimitRequest struct { + Limit uint32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` } -func (m *QueryListCctxPendingWithRateLimitRequest) Reset() { - *m = QueryListCctxPendingWithRateLimitRequest{} +func (m *QueryListPendingCctxWithinRateLimitRequest) Reset() { + *m = QueryListPendingCctxWithinRateLimitRequest{} } -func (m *QueryListCctxPendingWithRateLimitRequest) String() string { return proto.CompactTextString(m) } -func (*QueryListCctxPendingWithRateLimitRequest) ProtoMessage() {} -func (*QueryListCctxPendingWithRateLimitRequest) Descriptor() ([]byte, []int) { +func (m *QueryListPendingCctxWithinRateLimitRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryListPendingCctxWithinRateLimitRequest) ProtoMessage() {} +func (*QueryListPendingCctxWithinRateLimitRequest) Descriptor() ([]byte, []int) { return fileDescriptor_65a992045e92a606, []int{33} } -func (m *QueryListCctxPendingWithRateLimitRequest) XXX_Unmarshal(b []byte) error { +func (m *QueryListPendingCctxWithinRateLimitRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryListCctxPendingWithRateLimitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryListPendingCctxWithinRateLimitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryListCctxPendingWithRateLimitRequest.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryListPendingCctxWithinRateLimitRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1614,47 +1616,47 @@ func (m *QueryListCctxPendingWithRateLimitRequest) XXX_Marshal(b []byte, determi return b[:n], nil } } -func (m *QueryListCctxPendingWithRateLimitRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryListCctxPendingWithRateLimitRequest.Merge(m, src) +func (m *QueryListPendingCctxWithinRateLimitRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListPendingCctxWithinRateLimitRequest.Merge(m, src) } -func (m *QueryListCctxPendingWithRateLimitRequest) XXX_Size() int { +func (m *QueryListPendingCctxWithinRateLimitRequest) XXX_Size() int { return m.Size() } -func (m *QueryListCctxPendingWithRateLimitRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryListCctxPendingWithRateLimitRequest.DiscardUnknown(m) +func (m *QueryListPendingCctxWithinRateLimitRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListPendingCctxWithinRateLimitRequest.DiscardUnknown(m) } -var xxx_messageInfo_QueryListCctxPendingWithRateLimitRequest proto.InternalMessageInfo +var xxx_messageInfo_QueryListPendingCctxWithinRateLimitRequest proto.InternalMessageInfo -func (m *QueryListCctxPendingWithRateLimitRequest) GetLimit() uint32 { +func (m *QueryListPendingCctxWithinRateLimitRequest) GetLimit() uint32 { if m != nil { return m.Limit } return 0 } -type QueryListCctxPendingWithRateLimitResponse struct { +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"` RateLimitExceeded bool `protobuf:"varint,3,opt,name=rate_limit_exceeded,json=rateLimitExceeded,proto3" json:"rate_limit_exceeded,omitempty"` } -func (m *QueryListCctxPendingWithRateLimitResponse) Reset() { - *m = QueryListCctxPendingWithRateLimitResponse{} +func (m *QueryListPendingCctxWithinRateLimitResponse) Reset() { + *m = QueryListPendingCctxWithinRateLimitResponse{} } -func (m *QueryListCctxPendingWithRateLimitResponse) String() string { +func (m *QueryListPendingCctxWithinRateLimitResponse) String() string { return proto.CompactTextString(m) } -func (*QueryListCctxPendingWithRateLimitResponse) ProtoMessage() {} -func (*QueryListCctxPendingWithRateLimitResponse) Descriptor() ([]byte, []int) { +func (*QueryListPendingCctxWithinRateLimitResponse) ProtoMessage() {} +func (*QueryListPendingCctxWithinRateLimitResponse) Descriptor() ([]byte, []int) { return fileDescriptor_65a992045e92a606, []int{34} } -func (m *QueryListCctxPendingWithRateLimitResponse) XXX_Unmarshal(b []byte) error { +func (m *QueryListPendingCctxWithinRateLimitResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryListCctxPendingWithRateLimitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryListPendingCctxWithinRateLimitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryListCctxPendingWithRateLimitResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryListPendingCctxWithinRateLimitResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1664,33 +1666,33 @@ func (m *QueryListCctxPendingWithRateLimitResponse) XXX_Marshal(b []byte, determ return b[:n], nil } } -func (m *QueryListCctxPendingWithRateLimitResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryListCctxPendingWithRateLimitResponse.Merge(m, src) +func (m *QueryListPendingCctxWithinRateLimitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryListPendingCctxWithinRateLimitResponse.Merge(m, src) } -func (m *QueryListCctxPendingWithRateLimitResponse) XXX_Size() int { +func (m *QueryListPendingCctxWithinRateLimitResponse) XXX_Size() int { return m.Size() } -func (m *QueryListCctxPendingWithRateLimitResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryListCctxPendingWithRateLimitResponse.DiscardUnknown(m) +func (m *QueryListPendingCctxWithinRateLimitResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryListPendingCctxWithinRateLimitResponse.DiscardUnknown(m) } -var xxx_messageInfo_QueryListCctxPendingWithRateLimitResponse proto.InternalMessageInfo +var xxx_messageInfo_QueryListPendingCctxWithinRateLimitResponse proto.InternalMessageInfo -func (m *QueryListCctxPendingWithRateLimitResponse) GetCrossChainTx() []*CrossChainTx { +func (m *QueryListPendingCctxWithinRateLimitResponse) GetCrossChainTx() []*CrossChainTx { if m != nil { return m.CrossChainTx } return nil } -func (m *QueryListCctxPendingWithRateLimitResponse) GetTotalPending() uint64 { +func (m *QueryListPendingCctxWithinRateLimitResponse) GetTotalPending() uint64 { if m != nil { return m.TotalPending } return 0 } -func (m *QueryListCctxPendingWithRateLimitResponse) GetRateLimitExceeded() bool { +func (m *QueryListPendingCctxWithinRateLimitResponse) GetRateLimitExceeded() bool { if m != nil { return m.RateLimitExceeded } @@ -2083,10 +2085,10 @@ func init() { proto.RegisterType((*QueryGetCctxResponse)(nil), "zetachain.zetacore.crosschain.QueryGetCctxResponse") proto.RegisterType((*QueryAllCctxRequest)(nil), "zetachain.zetacore.crosschain.QueryAllCctxRequest") proto.RegisterType((*QueryAllCctxResponse)(nil), "zetachain.zetacore.crosschain.QueryAllCctxResponse") - proto.RegisterType((*QueryListCctxPendingRequest)(nil), "zetachain.zetacore.crosschain.QueryListCctxPendingRequest") - proto.RegisterType((*QueryListCctxPendingResponse)(nil), "zetachain.zetacore.crosschain.QueryListCctxPendingResponse") - proto.RegisterType((*QueryListCctxPendingWithRateLimitRequest)(nil), "zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitRequest") - proto.RegisterType((*QueryListCctxPendingWithRateLimitResponse)(nil), "zetachain.zetacore.crosschain.QueryListCctxPendingWithRateLimitResponse") + proto.RegisterType((*QueryListPendingCctxRequest)(nil), "zetachain.zetacore.crosschain.QueryListPendingCctxRequest") + proto.RegisterType((*QueryListPendingCctxResponse)(nil), "zetachain.zetacore.crosschain.QueryListPendingCctxResponse") + proto.RegisterType((*QueryListPendingCctxWithinRateLimitRequest)(nil), "zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitRequest") + proto.RegisterType((*QueryListPendingCctxWithinRateLimitResponse)(nil), "zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitResponse") proto.RegisterType((*QueryLastZetaHeightRequest)(nil), "zetachain.zetacore.crosschain.QueryLastZetaHeightRequest") proto.RegisterType((*QueryLastZetaHeightResponse)(nil), "zetachain.zetacore.crosschain.QueryLastZetaHeightResponse") proto.RegisterType((*QueryConvertGasToZetaRequest)(nil), "zetachain.zetacore.crosschain.QueryConvertGasToZetaRequest") @@ -2100,127 +2102,126 @@ func init() { func init() { proto.RegisterFile("crosschain/query.proto", fileDescriptor_65a992045e92a606) } var fileDescriptor_65a992045e92a606 = []byte{ - // 1911 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0x41, 0x6f, 0x1c, 0x49, - 0x15, 0x76, 0x79, 0xe2, 0x5d, 0xa7, 0xec, 0xc4, 0xeb, 0x5a, 0x13, 0x4c, 0xaf, 0x3d, 0x4e, 0xda, - 0x24, 0xf6, 0x26, 0x78, 0x7a, 0xed, 0xdd, 0x38, 0x6c, 0xe2, 0x45, 0x3b, 0x76, 0xd6, 0x4e, 0x84, - 0x77, 0xe3, 0x8c, 0x8c, 0x82, 0x82, 0x50, 0xab, 0xdc, 0x53, 0xe9, 0x69, 0xa5, 0xdd, 0xed, 0x4c, - 0xd7, 0x44, 0xe3, 0x58, 0xbe, 0xe4, 0xc0, 0x19, 0x29, 0x07, 0x2e, 0x9c, 0x90, 0x10, 0x1c, 0x38, - 0x70, 0x40, 0x70, 0x40, 0x0a, 0x42, 0x40, 0xc8, 0x31, 0x52, 0x24, 0x84, 0x40, 0x42, 0x28, 0xe1, - 0xc8, 0x8f, 0x58, 0x75, 0xcd, 0xeb, 0x99, 0xea, 0x9e, 0xee, 0x99, 0xf2, 0x78, 0x72, 0xc8, 0x29, - 0x53, 0x5d, 0xf5, 0xde, 0xfb, 0xbe, 0xf7, 0x5e, 0xbf, 0x7e, 0xf5, 0x62, 0x7c, 0xc6, 0xaa, 0xfa, - 0x41, 0x60, 0x55, 0xa8, 0xe3, 0x19, 0x0f, 0x6a, 0xac, 0xba, 0x5f, 0xd8, 0xab, 0xfa, 0xdc, 0x27, - 0xd3, 0x8f, 0x18, 0xa7, 0xe2, 0x71, 0x41, 0xfc, 0xf2, 0xab, 0xac, 0xd0, 0x3a, 0xaa, 0x5d, 0xb4, - 0xfc, 0x60, 0xd7, 0x0f, 0x8c, 0x1d, 0x1a, 0xb0, 0x86, 0x9c, 0xf1, 0x70, 0x71, 0x87, 0x71, 0xba, - 0x68, 0xec, 0x51, 0xdb, 0xf1, 0x28, 0x77, 0x7c, 0xaf, 0xa1, 0x4a, 0x9b, 0x91, 0x4c, 0x88, 0x9f, - 0xa6, 0xf8, 0x6d, 0xf2, 0x3a, 0x1c, 0xd0, 0xa4, 0x03, 0x36, 0x0d, 0xcc, 0xbd, 0xaa, 0x63, 0x31, - 0xd8, 0x9b, 0x95, 0xf6, 0x84, 0x8c, 0x59, 0xa1, 0x41, 0xc5, 0xe4, 0xbe, 0x69, 0x59, 0x4d, 0x05, - 0xf9, 0xb6, 0x43, 0xbc, 0x4a, 0xad, 0xfb, 0xac, 0x0a, 0xfb, 0xba, 0xb4, 0xef, 0xd2, 0x80, 0x9b, - 0x3b, 0xae, 0x6f, 0xdd, 0x37, 0x2b, 0xcc, 0xb1, 0x2b, 0x3c, 0x05, 0xa5, 0x5f, 0xe3, 0xed, 0x4a, - 0x64, 0x24, 0x55, 0xca, 0x99, 0xe9, 0x3a, 0xbb, 0x0e, 0x67, 0x55, 0xf3, 0x9e, 0x4b, 0xed, 0x00, - 0x0e, 0x4d, 0xd8, 0xbe, 0xed, 0x8b, 0x9f, 0x46, 0xf8, 0x0b, 0x9e, 0x4e, 0xd9, 0xbe, 0x6f, 0xbb, - 0xcc, 0xa0, 0x7b, 0x8e, 0x41, 0x3d, 0xcf, 0xe7, 0xc2, 0x3d, 0x20, 0xa3, 0x4f, 0x61, 0xed, 0x76, - 0xe8, 0xc1, 0xbb, 0x8c, 0xd3, 0xa2, 0x65, 0xf9, 0x35, 0x8f, 0x3b, 0x9e, 0x5d, 0x62, 0x0f, 0x6a, - 0x2c, 0xe0, 0xfa, 0x97, 0xf8, 0x83, 0xd4, 0xdd, 0x60, 0xcf, 0xf7, 0x02, 0x46, 0x0a, 0xf8, 0x7d, - 0xba, 0xe3, 0x57, 0x39, 0x2b, 0x9b, 0x61, 0x9c, 0x4c, 0xba, 0x1b, 0x9e, 0x98, 0x44, 0x67, 0xd1, - 0xfc, 0xc9, 0xd2, 0x38, 0x6c, 0x09, 0x59, 0xb1, 0xd1, 0x54, 0xb7, 0xc1, 0xf8, 0xad, 0x1a, 0xdf, - 0xae, 0x6f, 0x37, 0x38, 0x82, 0x35, 0x32, 0x89, 0xdf, 0x15, 0x0c, 0x6f, 0x5e, 0x17, 0x2a, 0x72, - 0xa5, 0x68, 0x49, 0x26, 0xf0, 0x90, 0xe7, 0x7b, 0x16, 0x9b, 0x1c, 0x3c, 0x8b, 0xe6, 0x4f, 0x94, - 0x1a, 0x0b, 0xbd, 0x86, 0xa7, 0xd2, 0xd5, 0x01, 0xbc, 0x1f, 0xe0, 0x51, 0x5f, 0x7a, 0x2e, 0x94, - 0x8e, 0x2c, 0x5d, 0x2a, 0x74, 0xcc, 0xae, 0x82, 0xac, 0x6a, 0xf5, 0xc4, 0xf3, 0xff, 0xcc, 0x0c, - 0x94, 0x62, 0x6a, 0x74, 0x06, 0x2c, 0x8a, 0xae, 0x9b, 0xc6, 0x62, 0x1d, 0xe3, 0x56, 0x16, 0x82, - 0xcd, 0x0b, 0x85, 0x46, 0xca, 0x16, 0xc2, 0x94, 0x2d, 0x34, 0x52, 0x1d, 0x52, 0xb6, 0xb0, 0x45, - 0x6d, 0x06, 0xb2, 0x25, 0x49, 0x52, 0x7f, 0x8a, 0x80, 0x5e, 0x9b, 0x9d, 0x4c, 0x7a, 0xb9, 0x3e, - 0xd0, 0x23, 0x1b, 0x31, 0xfc, 0x83, 0x02, 0xff, 0x5c, 0x57, 0xfc, 0x0d, 0x4c, 0x31, 0x02, 0x8f, - 0x11, 0xd6, 0xd3, 0x08, 0xac, 0xee, 0xaf, 0x85, 0x48, 0x22, 0x7f, 0x4d, 0xe0, 0x21, 0x81, 0x0c, - 0x62, 0xde, 0x58, 0x24, 0xbc, 0x38, 0xd8, 0xb3, 0x17, 0xff, 0x86, 0xf0, 0x6c, 0x47, 0x10, 0x6f, - 0x89, 0x33, 0x7f, 0x82, 0xf0, 0xb9, 0x88, 0xc7, 0x4d, 0x2f, 0xcb, 0x97, 0xdf, 0xc2, 0xc3, 0x8d, - 0xf2, 0xe6, 0x94, 0xe3, 0xaf, 0x50, 0xb9, 0x6f, 0x0e, 0xfd, 0xb3, 0x14, 0xd5, 0x34, 0x20, 0xe0, - 0xcf, 0x12, 0x1e, 0x71, 0xbc, 0xa4, 0x3b, 0x2f, 0x76, 0x71, 0xa7, 0xac, 0xaf, 0xe1, 0x4d, 0x59, - 0x49, 0xff, 0x9c, 0x29, 0xbd, 0xc1, 0x92, 0xc9, 0xa0, 0xdf, 0x6f, 0xf0, 0x1f, 0xa5, 0x37, 0x38, - 0x6e, 0xe7, 0x6d, 0x70, 0xd2, 0x35, 0x3c, 0x1d, 0x55, 0xd7, 0xd0, 0xe4, 0x0d, 0x1a, 0x54, 0xb6, - 0xfd, 0x35, 0x8b, 0xd7, 0x23, 0x37, 0x69, 0x78, 0xd8, 0x81, 0x0d, 0x28, 0xf9, 0xcd, 0xb5, 0x7e, - 0x88, 0xf3, 0x59, 0xc2, 0xc0, 0xfd, 0x47, 0xf8, 0xb4, 0x13, 0xdb, 0x01, 0x47, 0x2f, 0x28, 0xd0, - 0x6f, 0x09, 0x81, 0x07, 0x12, 0xaa, 0xf4, 0x15, 0x30, 0x1f, 0x3f, 0x7c, 0x9d, 0x72, 0xaa, 0x02, - 0xfe, 0x11, 0x9e, 0xc9, 0x94, 0x06, 0xf4, 0x77, 0xf0, 0xa9, 0xb5, 0x10, 0x93, 0x48, 0xfa, 0xed, - 0x7a, 0xa0, 0x58, 0x2f, 0x64, 0x19, 0x80, 0x1e, 0xd7, 0xa3, 0xdb, 0xe0, 0x75, 0x48, 0x99, 0x76, - 0xaf, 0xf7, 0x2b, 0x39, 0x9f, 0x21, 0xf0, 0x51, 0x8a, 0xa5, 0x0e, 0x21, 0xca, 0xf5, 0x29, 0x44, - 0xfd, 0xcb, 0x53, 0x03, 0x7f, 0x33, 0x4a, 0xb5, 0x0d, 0x1a, 0x6c, 0x85, 0xed, 0x9b, 0xf4, 0x69, - 0x71, 0xbc, 0x32, 0xab, 0x43, 0x84, 0x1b, 0x0b, 0xdd, 0xc4, 0x93, 0xed, 0x02, 0x40, 0x79, 0x0d, - 0x0f, 0x47, 0xcf, 0xc0, 0xb7, 0x73, 0x5d, 0xc8, 0x36, 0x55, 0x34, 0x05, 0x75, 0x0a, 0x88, 0x8a, - 0xae, 0x9b, 0x44, 0xd4, 0xaf, 0xe8, 0xfd, 0x1a, 0x01, 0x89, 0x98, 0x8d, 0x54, 0x12, 0xb9, 0x9e, - 0x48, 0xf4, 0x2f, 0x3e, 0xcb, 0xad, 0x52, 0xb0, 0x49, 0x03, 0xbe, 0x1a, 0x76, 0xbf, 0x37, 0x44, - 0xf3, 0xdb, 0x39, 0x4c, 0x07, 0xf0, 0x16, 0xa6, 0xc9, 0x01, 0xd1, 0x1f, 0xe2, 0xb1, 0xc4, 0x16, - 0xb8, 0xb4, 0xd0, 0x85, 0x6f, 0x52, 0x61, 0x52, 0x8d, 0x5e, 0x69, 0xbd, 0x1c, 0x19, 0xa0, 0xfb, - 0x15, 0xc9, 0xbf, 0x22, 0xe0, 0x99, 0x66, 0xaa, 0x13, 0xcf, 0x5c, 0x1f, 0x78, 0xf6, 0x2f, 0xca, - 0x97, 0xf0, 0xfb, 0x51, 0xb4, 0xe4, 0x6a, 0x95, 0x1e, 0xda, 0x4d, 0xb8, 0x74, 0xc0, 0xe1, 0xd5, - 0xfd, 0xaf, 0xc2, 0x7e, 0xbe, 0xd7, 0x6b, 0x80, 0x8d, 0x27, 0xe2, 0xa6, 0xc1, 0x6b, 0xb7, 0xf0, - 0xa8, 0x5c, 0x5b, 0x15, 0xdb, 0x7f, 0x59, 0xa4, 0x14, 0x53, 0xa0, 0xff, 0x18, 0x38, 0x16, 0x5d, - 0xf7, 0x4d, 0x54, 0xe4, 0xdf, 0x22, 0x20, 0xd2, 0xd4, 0x9f, 0x49, 0x24, 0x77, 0x2c, 0x22, 0xfd, - 0x8b, 0xfa, 0x57, 0xd0, 0x48, 0x6d, 0x3a, 0x81, 0xf0, 0xfd, 0x16, 0xf3, 0xca, 0xad, 0xeb, 0x63, - 0xa7, 0x76, 0x74, 0x02, 0x0f, 0x89, 0x2b, 0xac, 0xb0, 0x7e, 0xaa, 0xd4, 0x58, 0xe8, 0x4f, 0xa2, - 0x8e, 0xa9, 0x4d, 0xe1, 0x9b, 0x72, 0x85, 0x8e, 0x47, 0xb9, 0xcf, 0xa9, 0x0b, 0x86, 0x20, 0xb3, - 0x62, 0xcf, 0xf4, 0xcf, 0xf1, 0x7c, 0x1a, 0xa8, 0x3b, 0x0e, 0xaf, 0x94, 0x28, 0x67, 0x9b, 0x21, - 0x74, 0x29, 0xe1, 0x53, 0x78, 0xbd, 0x44, 0xf8, 0x43, 0x05, 0x15, 0x40, 0xf2, 0x36, 0x3e, 0x1d, - 0x1f, 0x55, 0xf4, 0x44, 0xd3, 0x92, 0x69, 0xce, 0xe2, 0x53, 0x82, 0x92, 0xb9, 0x97, 0xcd, 0x33, - 0xbc, 0xce, 0xb7, 0x66, 0x0b, 0x26, 0xab, 0x5b, 0x8c, 0x95, 0x59, 0x79, 0x32, 0x77, 0x16, 0xcd, - 0x0f, 0x97, 0xc6, 0xab, 0x11, 0xce, 0x2f, 0x60, 0xa3, 0x39, 0x3b, 0x08, 0x8b, 0x4a, 0x78, 0xcb, - 0x8f, 0x15, 0x48, 0xfd, 0x72, 0x94, 0x1b, 0x89, 0x5d, 0x20, 0x79, 0x06, 0xbf, 0x23, 0x95, 0xec, - 0x5c, 0x09, 0x56, 0xfa, 0x36, 0x64, 0xc0, 0x9a, 0xef, 0x3d, 0x64, 0xd5, 0xf0, 0x0b, 0xbd, 0xed, - 0x87, 0xe2, 0x6d, 0xd5, 0xa1, 0x2d, 0xa5, 0x34, 0x3c, 0x6c, 0xd3, 0x60, 0xb3, 0xe9, 0xfd, 0x93, - 0xa5, 0xe6, 0x5a, 0xff, 0x25, 0x82, 0xbe, 0xaa, 0x5d, 0x2d, 0xe0, 0xf9, 0x0e, 0x1e, 0xf7, 0x6b, - 0x7c, 0xc7, 0xaf, 0x79, 0xe5, 0x0d, 0x1a, 0xdc, 0xf4, 0xc2, 0xcd, 0x68, 0x92, 0xd1, 0xb6, 0x11, - 0x9e, 0x16, 0xf3, 0x13, 0xcb, 0x77, 0xd7, 0x19, 0x83, 0xd3, 0x0d, 0xa3, 0xed, 0x1b, 0x64, 0x1e, - 0x8f, 0x85, 0xff, 0xca, 0xf5, 0x3b, 0x27, 0xfc, 0x9f, 0x7c, 0xac, 0xcf, 0xe1, 0xf3, 0x02, 0xe6, - 0x97, 0x2c, 0x08, 0xa8, 0xcd, 0xb6, 0x68, 0x10, 0x38, 0x9e, 0xbd, 0xd5, 0xd2, 0x18, 0x79, 0x77, - 0x1d, 0x5f, 0xe8, 0x76, 0x10, 0x88, 0x4d, 0xe1, 0x93, 0xf7, 0x9a, 0x10, 0x1b, 0x84, 0x5a, 0x0f, - 0xf4, 0x3c, 0xb8, 0xbb, 0x99, 0x85, 0xac, 0xba, 0xee, 0x52, 0x3b, 0xba, 0x0b, 0x85, 0x97, 0xf8, - 0xe9, 0x8c, 0x03, 0xa0, 0x9f, 0xe2, 0xf7, 0xaa, 0x89, 0x3d, 0x28, 0x82, 0x46, 0x97, 0x7c, 0x4d, - 0xaa, 0x84, 0x4e, 0xb1, 0x4d, 0xdd, 0xd2, 0x2f, 0xce, 0xe1, 0x21, 0x01, 0x82, 0x3c, 0x43, 0x78, - 0x54, 0xbe, 0x74, 0x93, 0xab, 0x5d, 0x6c, 0x74, 0x98, 0x37, 0x69, 0xd7, 0x7a, 0x92, 0x6d, 0xd0, - 0xd6, 0x3f, 0x7b, 0xfc, 0xf2, 0x7f, 0x4f, 0x06, 0xaf, 0x90, 0xcb, 0x46, 0x28, 0xba, 0x20, 0x4d, - 0x18, 0x9b, 0x63, 0xbc, 0xa6, 0x90, 0x71, 0x00, 0x5f, 0xb0, 0x43, 0xe3, 0x40, 0x7c, 0xb3, 0x0e, - 0xc9, 0x1f, 0x10, 0x1e, 0x93, 0xf5, 0x16, 0x5d, 0x57, 0x8d, 0x4b, 0xfa, 0xd4, 0x49, 0x8d, 0x4b, - 0xc6, 0x24, 0x49, 0xbf, 0x24, 0xb8, 0x9c, 0x27, 0xb3, 0x0a, 0x5c, 0xc8, 0xbf, 0x11, 0x3e, 0x93, - 0x40, 0x0e, 0x97, 0x7f, 0x52, 0xec, 0x01, 0x44, 0x7c, 0x82, 0xa1, 0xad, 0x1e, 0x47, 0x05, 0xd0, - 0xb9, 0x2a, 0xe8, 0x7c, 0x42, 0x96, 0x14, 0xe8, 0x80, 0x2c, 0x44, 0xe8, 0x90, 0xfc, 0x0b, 0xe1, - 0x6f, 0x48, 0x37, 0x6c, 0x89, 0xdc, 0xe7, 0x8a, 0xc8, 0x32, 0xa7, 0x33, 0x5a, 0xf1, 0x18, 0x1a, - 0x80, 0xda, 0x8a, 0xa0, 0xb6, 0x4c, 0x3e, 0xc9, 0xa0, 0xe6, 0x78, 0x19, 0xcc, 0x4c, 0xa7, 0x7c, - 0x48, 0x7e, 0x8f, 0xf0, 0xe9, 0x38, 0x39, 0xe5, 0x9c, 0x4b, 0x99, 0x93, 0x28, 0xe7, 0x5c, 0xda, - 0xec, 0xa3, 0x6b, 0xce, 0x49, 0x4c, 0x02, 0xf2, 0x77, 0x00, 0x2e, 0xdd, 0x1f, 0x57, 0x14, 0x5f, - 0xde, 0xd4, 0x5b, 0xb4, 0xf6, 0x59, 0x8f, 0xd2, 0x00, 0xfe, 0xbb, 0x02, 0xfc, 0x12, 0xf9, 0xa8, - 0x03, 0xf8, 0x96, 0x98, 0x71, 0x10, 0xad, 0x0f, 0xc9, 0x3f, 0x10, 0x26, 0xed, 0x73, 0x05, 0xa2, - 0x84, 0x27, 0x73, 0x9a, 0xa1, 0x7d, 0xaf, 0x57, 0x71, 0xe0, 0x53, 0x14, 0x7c, 0xae, 0x91, 0x4f, - 0x33, 0xf9, 0x24, 0xff, 0xf3, 0xc3, 0x2c, 0x53, 0x4e, 0x65, 0x62, 0x7f, 0x42, 0x78, 0x3c, 0x6e, - 0x21, 0x4c, 0xaf, 0x95, 0x23, 0xa4, 0x48, 0x8f, 0x51, 0xca, 0x9c, 0x5f, 0xe8, 0x0b, 0x82, 0xd5, - 0x1c, 0x39, 0xaf, 0x14, 0x25, 0xf2, 0x1b, 0xd4, 0xba, 0x37, 0x93, 0x65, 0xc5, 0x04, 0x49, 0x5c, - 0xf0, 0xb5, 0x2b, 0x47, 0x96, 0x03, 0xb0, 0x86, 0x00, 0xfb, 0x21, 0x99, 0xcb, 0x00, 0x6b, 0x83, - 0x40, 0xe8, 0xf3, 0x32, 0xab, 0x1f, 0x92, 0x5f, 0x21, 0x3c, 0x12, 0x69, 0x09, 0x5d, 0xbd, 0xac, - 0xe8, 0xac, 0x9e, 0x10, 0xa7, 0x8c, 0x19, 0xf4, 0x39, 0x81, 0xf8, 0x1c, 0x99, 0xe9, 0x82, 0x98, - 0x3c, 0x45, 0xf8, 0xbd, 0x64, 0xdf, 0x45, 0x94, 0x8a, 0x47, 0x46, 0x13, 0xa8, 0xad, 0xf4, 0x26, - 0xac, 0xe8, 0x6a, 0x2b, 0x89, 0xf5, 0x19, 0xc2, 0x23, 0x52, 0x6b, 0x45, 0xae, 0xab, 0x98, 0xef, - 0xd6, 0xc2, 0x69, 0x5f, 0x1c, 0x53, 0x0b, 0xb0, 0xb9, 0x28, 0xd8, 0x7c, 0x9b, 0xe8, 0x19, 0x6c, - 0xa4, 0x76, 0x94, 0x3c, 0x47, 0x6d, 0x93, 0x04, 0xa2, 0x5a, 0x0a, 0xd3, 0xe7, 0x20, 0x6a, 0xa5, - 0x27, 0x7b, 0x86, 0xa3, 0x2f, 0x0b, 0xf8, 0x1f, 0x91, 0x42, 0x06, 0x7c, 0x37, 0x2e, 0xd7, 0x4c, - 0xff, 0xbf, 0x20, 0x4c, 0x12, 0x3a, 0xc3, 0xb7, 0x40, 0xb5, 0x64, 0x1c, 0x87, 0x4d, 0xf6, 0xa4, - 0x46, 0x2f, 0x08, 0x36, 0xf3, 0xe4, 0x82, 0x1a, 0x1b, 0xf2, 0x73, 0x84, 0x4f, 0x88, 0xe2, 0xb3, - 0xa4, 0xe8, 0x46, 0xb9, 0x3c, 0x7e, 0x7c, 0x24, 0x19, 0xc5, 0xef, 0xae, 0x05, 0x1f, 0x2c, 0xe1, - 0xe4, 0xdf, 0x21, 0x3c, 0x22, 0x4d, 0x68, 0xc8, 0xa7, 0x47, 0xb0, 0x18, 0x9f, 0xea, 0xf4, 0x06, - 0xf6, 0xb2, 0x00, 0x6b, 0x90, 0x85, 0x8e, 0x60, 0xdb, 0x9a, 0xeb, 0x9f, 0x21, 0xfc, 0x6e, 0xf4, - 0x05, 0x5a, 0x52, 0x8c, 0xe8, 0x91, 0x1d, 0x9b, 0x98, 0xd2, 0xe8, 0xb3, 0x02, 0xeb, 0x34, 0xf9, - 0xa0, 0x03, 0xd6, 0xb0, 0x03, 0x1b, 0x0b, 0xa5, 0x36, 0x9d, 0x80, 0x47, 0xd7, 0x6e, 0xa5, 0x16, - 0x2c, 0x7d, 0xc2, 0xa2, 0xd6, 0x82, 0x65, 0x0c, 0x53, 0xba, 0x56, 0x0e, 0xab, 0x25, 0x43, 0xfe, - 0x8f, 0x70, 0x3e, 0x01, 0xfc, 0x8e, 0xc3, 0x2b, 0x8e, 0xd7, 0xbc, 0xc4, 0x91, 0x8d, 0x1e, 0xb0, - 0xa4, 0xcd, 0x50, 0xb4, 0x1b, 0xc7, 0x57, 0x04, 0x0c, 0xaf, 0x08, 0x86, 0x8b, 0xc4, 0xe8, 0xce, - 0x30, 0xa6, 0x40, 0x74, 0xca, 0xf1, 0x3f, 0x7a, 0x50, 0xcb, 0xfd, 0xd4, 0x3f, 0xa3, 0xd0, 0xae, - 0xf6, 0x22, 0xaa, 0xd8, 0xc4, 0x3c, 0x8a, 0xa3, 0x0c, 0x81, 0xc7, 0x27, 0x2e, 0x6a, 0xc0, 0x53, - 0x67, 0x38, 0x6a, 0xc0, 0xd3, 0x07, 0x3c, 0x5d, 0x81, 0xbb, 0x71, 0x94, 0x61, 0x93, 0x90, 0x1c, - 0x08, 0xa8, 0x35, 0x09, 0x19, 0xa3, 0x0b, 0xb5, 0x26, 0x21, 0x6b, 0xac, 0xd1, 0xb5, 0x49, 0x48, - 0x0e, 0x29, 0x56, 0xbf, 0xff, 0xfc, 0x55, 0x1e, 0xbd, 0x78, 0x95, 0x47, 0xff, 0x7d, 0x95, 0x47, - 0x3f, 0x7d, 0x9d, 0x1f, 0x78, 0xf1, 0x3a, 0x3f, 0xf0, 0xcf, 0xd7, 0xf9, 0x81, 0xbb, 0x8b, 0xb6, - 0xc3, 0x2b, 0xb5, 0x9d, 0x82, 0xe5, 0xef, 0xca, 0xca, 0x22, 0x4c, 0x46, 0x5d, 0xd6, 0xcb, 0xf7, - 0xf7, 0x58, 0xb0, 0xf3, 0x8e, 0xf8, 0x6a, 0x7f, 0xfc, 0x75, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdc, - 0x55, 0x35, 0x06, 0x22, 0x25, 0x00, 0x00, + // 1901 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xc1, 0x6f, 0xdc, 0x4a, + 0x19, 0xcf, 0x64, 0x9b, 0xf7, 0xd2, 0x49, 0xda, 0xbc, 0xcc, 0x0b, 0x25, 0xf8, 0x25, 0x9b, 0xd6, + 0xa1, 0x4d, 0x48, 0xc8, 0xfa, 0x25, 0x7d, 0xcd, 0xa3, 0x6d, 0x1e, 0x62, 0x93, 0xbe, 0xa4, 0x81, + 0xb4, 0x4d, 0x57, 0x41, 0x45, 0x45, 0xc8, 0x9a, 0x78, 0xa7, 0x5e, 0xab, 0x8e, 0x9d, 0xae, 0xbd, + 0xd5, 0xa6, 0x51, 0x2e, 0x3d, 0x70, 0x46, 0xea, 0x81, 0x0b, 0x57, 0x44, 0x0f, 0x1c, 0x38, 0x20, + 0x38, 0x20, 0x15, 0x21, 0xa0, 0xf4, 0x58, 0x09, 0x81, 0x10, 0x48, 0x08, 0xb5, 0xfc, 0x05, 0xfc, + 0x05, 0xc8, 0xe3, 0xcf, 0xbb, 0x63, 0xaf, 0xbd, 0x3b, 0xd9, 0x6c, 0x0f, 0x3d, 0x75, 0xed, 0x99, + 0xef, 0x9b, 0xdf, 0xef, 0x9b, 0x6f, 0x3e, 0x7f, 0xf3, 0x6b, 0xf0, 0x39, 0xa3, 0xea, 0x7a, 0x9e, + 0x51, 0xa1, 0x96, 0xa3, 0x3d, 0xaa, 0xb1, 0xea, 0x41, 0x61, 0xbf, 0xea, 0xfa, 0x2e, 0x99, 0x7c, + 0xc2, 0x7c, 0xca, 0x5f, 0x17, 0xf8, 0x2f, 0xb7, 0xca, 0x0a, 0xcd, 0xa9, 0xca, 0x9c, 0xe1, 0x7a, + 0x7b, 0xae, 0xa7, 0xed, 0x52, 0x8f, 0x85, 0x76, 0xda, 0xe3, 0xc5, 0x5d, 0xe6, 0xd3, 0x45, 0x6d, + 0x9f, 0x9a, 0x96, 0x43, 0x7d, 0xcb, 0x75, 0x42, 0x57, 0xca, 0x94, 0xb0, 0x04, 0xff, 0xa9, 0xf3, + 0xdf, 0xba, 0x5f, 0x87, 0x09, 0x8a, 0x30, 0xc1, 0xa4, 0x9e, 0xbe, 0x5f, 0xb5, 0x0c, 0x06, 0x63, + 0xd3, 0xc2, 0x18, 0xb7, 0xd1, 0x2b, 0xd4, 0xab, 0xe8, 0xbe, 0xab, 0x1b, 0x46, 0xc3, 0x41, 0xbe, + 0x65, 0x92, 0x5f, 0xa5, 0xc6, 0x43, 0x56, 0x85, 0x71, 0x55, 0x18, 0xb7, 0xa9, 0xe7, 0xeb, 0xbb, + 0xb6, 0x6b, 0x3c, 0xd4, 0x2b, 0xcc, 0x32, 0x2b, 0x7e, 0x0a, 0x4a, 0xb7, 0xe6, 0xb7, 0x3a, 0x11, + 0x91, 0x54, 0xa9, 0xcf, 0x74, 0xdb, 0xda, 0xb3, 0x7c, 0x56, 0xd5, 0x1f, 0xd8, 0xd4, 0xf4, 0x60, + 0xd2, 0x98, 0xe9, 0x9a, 0x2e, 0xff, 0xa9, 0x05, 0xbf, 0xe0, 0xed, 0x84, 0xe9, 0xba, 0xa6, 0xcd, + 0x34, 0xba, 0x6f, 0x69, 0xd4, 0x71, 0x5c, 0x9f, 0x87, 0x07, 0x6c, 0xd4, 0x09, 0xac, 0xdc, 0x0d, + 0x22, 0x78, 0x9f, 0xf9, 0xb4, 0x68, 0x18, 0x6e, 0xcd, 0xf1, 0x2d, 0xc7, 0x2c, 0xb1, 0x47, 0x35, + 0xe6, 0xf9, 0xea, 0x2d, 0xfc, 0x49, 0xea, 0xa8, 0xb7, 0xef, 0x3a, 0x1e, 0x23, 0x05, 0xfc, 0x31, + 0xdd, 0x75, 0xab, 0x3e, 0x2b, 0xeb, 0xc1, 0x3e, 0xe9, 0x74, 0x2f, 0x98, 0x31, 0x8e, 0xce, 0xa3, + 0xd9, 0xd3, 0xa5, 0x51, 0x18, 0xe2, 0xb6, 0x7c, 0xa0, 0xe1, 0x6e, 0x83, 0xf9, 0x77, 0x6a, 0xfe, + 0x4e, 0x7d, 0x27, 0xe4, 0x08, 0xab, 0x91, 0x71, 0xfc, 0x21, 0x67, 0xb8, 0x79, 0x83, 0xbb, 0xc8, + 0x95, 0xa2, 0x47, 0x32, 0x86, 0x07, 0x1c, 0xd7, 0x31, 0xd8, 0x78, 0xff, 0x79, 0x34, 0x7b, 0xaa, + 0x14, 0x3e, 0xa8, 0x35, 0x3c, 0x91, 0xee, 0x0e, 0xe0, 0x7d, 0x1f, 0x0f, 0xbb, 0xc2, 0x7b, 0xee, + 0x74, 0x68, 0x69, 0xbe, 0xd0, 0x36, 0xbb, 0x0a, 0xa2, 0xab, 0xd5, 0x53, 0xaf, 0xfe, 0x3d, 0xd5, + 0x57, 0x8a, 0xb9, 0x51, 0x19, 0xb0, 0x28, 0xda, 0x76, 0x1a, 0x8b, 0x75, 0x8c, 0x9b, 0x59, 0x08, + 0x6b, 0x5e, 0x2a, 0x84, 0x29, 0x5b, 0x08, 0x52, 0xb6, 0x10, 0xa6, 0x3a, 0xa4, 0x6c, 0x61, 0x9b, + 0x9a, 0x0c, 0x6c, 0x4b, 0x82, 0xa5, 0xfa, 0x02, 0x01, 0xbd, 0x96, 0x75, 0x32, 0xe9, 0xe5, 0x7a, + 0x40, 0x8f, 0x6c, 0xc4, 0xf0, 0xf7, 0x73, 0xfc, 0x33, 0x1d, 0xf1, 0x87, 0x98, 0x62, 0x04, 0x9e, + 0x22, 0xac, 0xa6, 0x11, 0x58, 0x3d, 0x58, 0x0b, 0x90, 0x44, 0xf1, 0x1a, 0xc3, 0x03, 0x1c, 0x19, + 0xec, 0x79, 0xf8, 0x90, 0x88, 0x62, 0x7f, 0xd7, 0x51, 0xfc, 0x33, 0xc2, 0xd3, 0x6d, 0x41, 0xbc, + 0x27, 0xc1, 0xfc, 0x31, 0xc2, 0x17, 0x22, 0x1e, 0x9b, 0x4e, 0x56, 0x2c, 0xbf, 0x86, 0x07, 0xc3, + 0xf2, 0x66, 0x95, 0xe3, 0x47, 0xa8, 0xdc, 0xb3, 0x80, 0xfe, 0x41, 0xd8, 0xd5, 0x34, 0x20, 0x10, + 0xcf, 0x12, 0x1e, 0xb2, 0x9c, 0x64, 0x38, 0xe7, 0x3a, 0x84, 0x53, 0xf4, 0x17, 0x46, 0x53, 0x74, + 0xd2, 0xbb, 0x60, 0x0a, 0x27, 0x58, 0x58, 0xd2, 0xeb, 0xf5, 0x09, 0xfe, 0x9d, 0x70, 0x82, 0xe3, + 0xeb, 0xbc, 0x0f, 0x41, 0xba, 0x8e, 0x27, 0xa3, 0xea, 0x1a, 0x2c, 0x79, 0x93, 0x7a, 0x95, 0x1d, + 0x77, 0xcd, 0xf0, 0xeb, 0x51, 0x98, 0x14, 0x3c, 0x68, 0xc1, 0x00, 0x94, 0xfc, 0xc6, 0xb3, 0x7a, + 0x84, 0xf3, 0x59, 0xc6, 0xc0, 0xfd, 0x87, 0xf8, 0xac, 0x15, 0x1b, 0x81, 0x40, 0x2f, 0x48, 0xd0, + 0x6f, 0x1a, 0x41, 0x04, 0x12, 0xae, 0xd4, 0x15, 0x58, 0x3e, 0x3e, 0xf9, 0x06, 0xf5, 0xa9, 0x0c, + 0xf8, 0x27, 0x78, 0x2a, 0xd3, 0x1a, 0xd0, 0xdf, 0xc3, 0x67, 0xd6, 0x02, 0x4c, 0x3c, 0xe9, 0x77, + 0xea, 0x9e, 0x64, 0xbd, 0x10, 0x6d, 0x00, 0x7a, 0xdc, 0x8f, 0x6a, 0x42, 0xd4, 0x21, 0x65, 0x5a, + 0xa3, 0xde, 0xab, 0xe4, 0x7c, 0x89, 0x20, 0x46, 0x29, 0x2b, 0xb5, 0xd9, 0xa2, 0x5c, 0x8f, 0xb6, + 0xa8, 0x77, 0x79, 0xaa, 0xe1, 0xaf, 0x46, 0xa9, 0xb6, 0x41, 0xbd, 0xed, 0xa0, 0x7d, 0x13, 0x3e, + 0x2d, 0x96, 0x53, 0x66, 0x75, 0xd8, 0xe1, 0xf0, 0x41, 0xd5, 0xf1, 0x78, 0xab, 0x01, 0x50, 0x5e, + 0xc3, 0x83, 0xd1, 0x3b, 0x88, 0xed, 0x4c, 0x07, 0xb2, 0x0d, 0x17, 0x0d, 0x43, 0x95, 0x02, 0xa2, + 0xa2, 0x6d, 0x27, 0x11, 0xf5, 0x6a, 0xf7, 0x9e, 0x23, 0x20, 0x11, 0x5b, 0x23, 0x95, 0x44, 0xae, + 0x2b, 0x12, 0xbd, 0xdb, 0x9f, 0xe5, 0x66, 0x29, 0xd8, 0xa2, 0x9e, 0xbf, 0x1a, 0x74, 0xbf, 0x37, + 0x79, 0xf3, 0xdb, 0x7e, 0x9b, 0x0e, 0xe1, 0x14, 0xa6, 0xd9, 0x01, 0xd1, 0x1f, 0xe0, 0x91, 0xc4, + 0x10, 0x84, 0xb4, 0xd0, 0x81, 0x6f, 0xd2, 0x61, 0xd2, 0x8d, 0x5a, 0x69, 0x1e, 0x8e, 0x0c, 0xd0, + 0xbd, 0xda, 0xc9, 0x3f, 0x21, 0xe0, 0x99, 0xb6, 0x54, 0x3b, 0x9e, 0xb9, 0x1e, 0xf0, 0xec, 0xdd, + 0x2e, 0xcf, 0xe3, 0x8f, 0xa3, 0xdd, 0x12, 0xab, 0x55, 0xfa, 0xd6, 0x6e, 0xc1, 0xa5, 0x03, 0x26, + 0xaf, 0x1e, 0xdc, 0x0e, 0xfa, 0xf9, 0x6e, 0xaf, 0x01, 0x26, 0x1e, 0x8b, 0x2f, 0x0d, 0x51, 0xbb, + 0x83, 0x87, 0xc5, 0xda, 0x2a, 0xd9, 0xfe, 0x8b, 0x26, 0xa5, 0x98, 0x03, 0xf5, 0x47, 0xc0, 0xb1, + 0x68, 0xdb, 0xef, 0xa2, 0x22, 0xff, 0x0a, 0x01, 0x91, 0x86, 0xff, 0x4c, 0x22, 0xb9, 0x13, 0x11, + 0xe9, 0xdd, 0xae, 0xdf, 0x86, 0x46, 0x6a, 0xcb, 0xf2, 0xfc, 0x6d, 0xe6, 0x94, 0x2d, 0xc7, 0x14, + 0x23, 0xd3, 0xa6, 0x1d, 0x1d, 0xc3, 0x03, 0xfc, 0x0a, 0xcb, 0x57, 0x3f, 0x53, 0x0a, 0x1f, 0xd4, + 0x67, 0x51, 0xc7, 0xd4, 0xe2, 0xf0, 0x5d, 0x85, 0x42, 0xc5, 0xc3, 0xbe, 0xeb, 0x53, 0x1b, 0x16, + 0x83, 0xcc, 0x8a, 0xbd, 0x53, 0x57, 0xf1, 0x5c, 0x1a, 0xa8, 0x7b, 0x96, 0x5f, 0xb1, 0x9c, 0x12, + 0xf5, 0xd9, 0x56, 0x00, 0x5e, 0x48, 0xf9, 0x90, 0x19, 0x12, 0x99, 0xfd, 0x0d, 0xe1, 0x79, 0x29, + 0x27, 0x40, 0xf4, 0x2e, 0x3e, 0x1b, 0x97, 0x2b, 0xba, 0xa2, 0x6a, 0x88, 0x54, 0xa7, 0xf1, 0x19, + 0x4e, 0x4b, 0xdf, 0xcf, 0xe6, 0x1a, 0x5c, 0xe9, 0x9b, 0xfa, 0x82, 0xce, 0xea, 0x06, 0x63, 0x65, + 0x56, 0x1e, 0xcf, 0x9d, 0x47, 0xb3, 0x83, 0xa5, 0xd1, 0x6a, 0x84, 0xf3, 0x4b, 0x18, 0x68, 0xe8, + 0x07, 0x41, 0x61, 0x09, 0x6e, 0xfa, 0xb1, 0x22, 0xa9, 0x5e, 0x89, 0xf2, 0x23, 0x31, 0x0a, 0x24, + 0xcf, 0xe1, 0x0f, 0x84, 0xb2, 0x9d, 0x2b, 0xc1, 0x93, 0xba, 0x03, 0x59, 0xb0, 0xe6, 0x3a, 0x8f, + 0x59, 0x35, 0xf8, 0x4a, 0xef, 0xb8, 0x81, 0x79, 0x4b, 0x85, 0x68, 0x49, 0x2b, 0x05, 0x0f, 0x9a, + 0xd4, 0xdb, 0x6a, 0x64, 0xd6, 0xe9, 0x52, 0xe3, 0x59, 0xfd, 0x39, 0x82, 0xde, 0xaa, 0xd5, 0x2d, + 0xe0, 0xf9, 0x26, 0x1e, 0x75, 0x6b, 0xfe, 0xae, 0x5b, 0x73, 0xca, 0x1b, 0xd4, 0xdb, 0x74, 0x82, + 0xc1, 0x48, 0xcd, 0x68, 0x19, 0x08, 0x66, 0x73, 0x0d, 0xc5, 0x70, 0xed, 0x75, 0xc6, 0x60, 0x76, + 0xb8, 0x68, 0xeb, 0x00, 0x99, 0xc5, 0x23, 0xc1, 0xbf, 0x62, 0x0d, 0xcf, 0xf1, 0xf8, 0x27, 0x5f, + 0xab, 0x33, 0xf8, 0x22, 0x87, 0x79, 0x8b, 0x79, 0x1e, 0x35, 0xd9, 0x36, 0xf5, 0x3c, 0xcb, 0x31, + 0xb7, 0x9b, 0x1e, 0xa3, 0xe8, 0xae, 0xe3, 0x4b, 0x9d, 0x26, 0x02, 0xb1, 0x09, 0x7c, 0xfa, 0x41, + 0x03, 0x62, 0x48, 0xa8, 0xf9, 0x42, 0xcd, 0x43, 0xb8, 0x1b, 0x59, 0xc8, 0xaa, 0xeb, 0x36, 0x35, + 0xa3, 0xfb, 0x50, 0x70, 0x91, 0x9f, 0xcc, 0x98, 0x00, 0xfe, 0x29, 0xfe, 0xa8, 0x9a, 0x18, 0x83, + 0x42, 0xa8, 0x75, 0xc8, 0xd7, 0xa4, 0x4b, 0xe8, 0x16, 0x5b, 0xdc, 0x2d, 0x3d, 0xbf, 0x80, 0x07, + 0x38, 0x08, 0xf2, 0x12, 0xe1, 0x61, 0xf1, 0xe2, 0x4d, 0xae, 0x75, 0x58, 0xa3, 0x8d, 0xe6, 0xa4, + 0x5c, 0xef, 0xca, 0x36, 0xa4, 0xad, 0x7e, 0xf1, 0xf4, 0xaf, 0xff, 0x7d, 0xd6, 0xff, 0x39, 0xb9, + 0xa2, 0x05, 0xa6, 0x0b, 0x82, 0xca, 0xd8, 0x90, 0xf2, 0x1a, 0x46, 0xda, 0x21, 0x7c, 0xc5, 0x8e, + 0xb4, 0x43, 0xfe, 0xdd, 0x3a, 0x22, 0xbf, 0x45, 0x78, 0x44, 0xf4, 0x5b, 0xb4, 0x6d, 0x39, 0x2e, + 0xe9, 0xca, 0x93, 0x1c, 0x97, 0x0c, 0x35, 0x49, 0x9d, 0xe7, 0x5c, 0x2e, 0x92, 0x69, 0x09, 0x2e, + 0xe4, 0x5f, 0x08, 0x9f, 0x4b, 0x20, 0x07, 0x01, 0x80, 0x14, 0xbb, 0x00, 0x11, 0x57, 0x31, 0x94, + 0xd5, 0x93, 0xb8, 0x00, 0x3a, 0xd7, 0x38, 0x9d, 0xcf, 0xc8, 0x92, 0x04, 0x1d, 0xb0, 0x85, 0x1d, + 0x3a, 0x22, 0xff, 0x44, 0xf8, 0x2b, 0xc2, 0x2d, 0x5b, 0x20, 0xf7, 0x1d, 0x49, 0x64, 0x99, 0x0a, + 0x8d, 0x52, 0x3c, 0x81, 0x07, 0xa0, 0xb6, 0xc2, 0xa9, 0x2d, 0x93, 0xcf, 0x32, 0xa8, 0x59, 0x4e, + 0x06, 0x33, 0xdd, 0x2a, 0x1f, 0x91, 0xdf, 0x20, 0x7c, 0x36, 0x4e, 0x4e, 0x3a, 0xe7, 0x52, 0xb4, + 0x12, 0xe9, 0x9c, 0x4b, 0xd3, 0x3f, 0x3a, 0xe6, 0x9c, 0xc0, 0xc4, 0x23, 0x7f, 0x01, 0xe0, 0xc2, + 0x1d, 0x72, 0x45, 0xf2, 0xf0, 0xa6, 0xde, 0xa4, 0x95, 0x2f, 0xba, 0xb4, 0x06, 0xf0, 0xdf, 0xe2, + 0xe0, 0x97, 0xc8, 0xa7, 0x6d, 0xc0, 0x37, 0xcd, 0xb4, 0xc3, 0xe8, 0xf9, 0x88, 0xfc, 0x1d, 0x61, + 0xd2, 0xaa, 0x2d, 0x10, 0x29, 0x3c, 0x99, 0x8a, 0x86, 0xf2, 0xed, 0x6e, 0xcd, 0x81, 0x4f, 0x91, + 0xf3, 0xb9, 0x4e, 0xae, 0x66, 0xf2, 0x49, 0xfe, 0x07, 0x88, 0x5e, 0xa6, 0x3e, 0x15, 0x89, 0xfd, + 0x1e, 0xe1, 0xd1, 0xf8, 0x0a, 0x41, 0x7a, 0xad, 0x1c, 0x23, 0x45, 0xba, 0xdc, 0xa5, 0x4c, 0x0d, + 0x43, 0x5d, 0xe0, 0xac, 0x66, 0xc8, 0x45, 0xa9, 0x5d, 0x22, 0xbf, 0x44, 0xcd, 0xbb, 0x33, 0x59, + 0x96, 0x4c, 0x90, 0xc4, 0x25, 0x5f, 0xf9, 0xfc, 0xd8, 0x76, 0x00, 0x56, 0xe3, 0x60, 0xbf, 0x41, + 0x66, 0x32, 0xc0, 0x9a, 0x60, 0x10, 0xc4, 0xbc, 0xcc, 0xea, 0x47, 0xe4, 0x17, 0x08, 0x0f, 0x45, + 0x5e, 0x82, 0x50, 0x2f, 0x4b, 0x06, 0xab, 0x2b, 0xc4, 0x29, 0x52, 0x83, 0x3a, 0xc3, 0x11, 0x5f, + 0x20, 0x53, 0x1d, 0x10, 0x93, 0x17, 0x08, 0x7f, 0x94, 0xec, 0xbb, 0x88, 0x54, 0xf1, 0xc8, 0x68, + 0x02, 0x95, 0x95, 0xee, 0x8c, 0x25, 0x43, 0x6d, 0x24, 0xb1, 0xbe, 0x44, 0x78, 0x48, 0x68, 0xad, + 0xc8, 0x0d, 0x99, 0xe5, 0x3b, 0xb5, 0x70, 0xca, 0x97, 0x27, 0xf4, 0x02, 0x6c, 0xe6, 0x38, 0x9b, + 0xaf, 0x13, 0x35, 0x83, 0x8d, 0xd0, 0x8e, 0x92, 0x57, 0xa8, 0x45, 0x4d, 0x20, 0xb2, 0xa5, 0x30, + 0x5d, 0x0b, 0x91, 0x2b, 0x3d, 0xd9, 0x3a, 0x8e, 0xba, 0xcc, 0xe1, 0x7f, 0x4a, 0x0a, 0x19, 0xf0, + 0xed, 0xb8, 0x5d, 0x23, 0xfd, 0xff, 0x88, 0x30, 0x49, 0xf8, 0x0c, 0x4e, 0x81, 0x6c, 0xc9, 0x38, + 0x09, 0x9b, 0x6c, 0xb5, 0x46, 0x2d, 0x70, 0x36, 0xb3, 0xe4, 0x92, 0x1c, 0x1b, 0xf2, 0x33, 0x84, + 0x4f, 0xf1, 0xe2, 0xb3, 0x24, 0x19, 0x46, 0xb1, 0x3c, 0x5e, 0x3e, 0x96, 0x8d, 0xe4, 0x77, 0xd7, + 0x80, 0x0f, 0x16, 0x0f, 0xf2, 0xaf, 0x11, 0x1e, 0x12, 0x54, 0x1a, 0x72, 0xf5, 0x18, 0x2b, 0xc6, + 0x95, 0x9d, 0xee, 0xc0, 0x5e, 0xe1, 0x60, 0x35, 0xb2, 0xd0, 0x16, 0x6c, 0x4b, 0x73, 0xfd, 0x53, + 0x84, 0x3f, 0x8c, 0xbe, 0x40, 0x4b, 0x92, 0x3b, 0x7a, 0xec, 0xc0, 0x26, 0x94, 0x1a, 0x75, 0x9a, + 0x63, 0x9d, 0x24, 0x9f, 0xb4, 0xc1, 0x1a, 0x74, 0x60, 0x23, 0x09, 0x15, 0x40, 0xae, 0x05, 0x4b, + 0x57, 0x59, 0xe4, 0x5a, 0xb0, 0x0c, 0x41, 0xa5, 0x73, 0xe5, 0x10, 0x40, 0xfe, 0x0f, 0xe1, 0x7c, + 0x7b, 0xf9, 0x82, 0x6c, 0x76, 0x81, 0x25, 0x5d, 0x47, 0x51, 0xbe, 0xdb, 0x0b, 0x57, 0xc0, 0xf2, + 0x2a, 0x67, 0x79, 0x99, 0x2c, 0x76, 0x66, 0x99, 0x64, 0x14, 0xf4, 0xcb, 0xf1, 0x3f, 0x7f, 0x90, + 0x3b, 0x01, 0xa9, 0x7f, 0x50, 0xa1, 0x5c, 0xeb, 0xc6, 0x54, 0xb2, 0x95, 0x79, 0x12, 0x47, 0x19, + 0x00, 0x8f, 0xeb, 0x2e, 0x72, 0xc0, 0x53, 0x95, 0x1c, 0x39, 0xe0, 0xe9, 0x32, 0x4f, 0x47, 0xe0, + 0x76, 0x1c, 0x65, 0xd0, 0x2a, 0x24, 0x65, 0x01, 0xb9, 0x56, 0x21, 0x43, 0xc0, 0x90, 0x6b, 0x15, + 0xb2, 0xc4, 0x8d, 0x8e, 0xad, 0x42, 0x52, 0xaa, 0x58, 0xfd, 0xde, 0xab, 0x37, 0x79, 0xf4, 0xfa, + 0x4d, 0x1e, 0xfd, 0xe7, 0x4d, 0x1e, 0xfd, 0xe4, 0x6d, 0xbe, 0xef, 0xf5, 0xdb, 0x7c, 0xdf, 0x3f, + 0xde, 0xe6, 0xfb, 0xee, 0x2f, 0x9a, 0x96, 0x5f, 0xa9, 0xed, 0x16, 0x0c, 0x77, 0x4f, 0x74, 0x16, + 0x61, 0xd2, 0xea, 0xa2, 0x5f, 0xff, 0x60, 0x9f, 0x79, 0xbb, 0x1f, 0xf0, 0x6f, 0xf7, 0xe5, 0xff, + 0x07, 0x00, 0x00, 0xff, 0xff, 0x25, 0x12, 0xcf, 0x1a, 0x2c, 0x25, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2265,9 +2266,9 @@ type QueryClient interface { // Queries a list of send items. CctxAll(ctx context.Context, in *QueryAllCctxRequest, opts ...grpc.CallOption) (*QueryAllCctxResponse, error) // Queries a list of pending cctxs. - CctxListPending(ctx context.Context, in *QueryListCctxPendingRequest, opts ...grpc.CallOption) (*QueryListCctxPendingResponse, error) - // Queries a list of pending cctxs with rate limit. - CctxListPendingWithinRateLimit(ctx context.Context, in *QueryListCctxPendingWithRateLimitRequest, opts ...grpc.CallOption) (*QueryListCctxPendingWithRateLimitResponse, error) + ListPendingCctx(ctx context.Context, in *QueryListPendingCctxRequest, opts ...grpc.CallOption) (*QueryListPendingCctxResponse, error) + // Queries a list of pending cctxs within rate limit. + ListPendingCctxWithinRateLimit(ctx context.Context, in *QueryListPendingCctxWithinRateLimitRequest, opts ...grpc.CallOption) (*QueryListPendingCctxWithinRateLimitResponse, error) ZetaAccounting(ctx context.Context, in *QueryZetaAccountingRequest, opts ...grpc.CallOption) (*QueryZetaAccountingResponse, error) // Queries a list of lastMetaHeight items. LastZetaHeight(ctx context.Context, in *QueryLastZetaHeightRequest, opts ...grpc.CallOption) (*QueryLastZetaHeightResponse, error) @@ -2436,18 +2437,18 @@ func (c *queryClient) CctxAll(ctx context.Context, in *QueryAllCctxRequest, opts return out, nil } -func (c *queryClient) CctxListPending(ctx context.Context, in *QueryListCctxPendingRequest, opts ...grpc.CallOption) (*QueryListCctxPendingResponse, error) { - out := new(QueryListCctxPendingResponse) - err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/CctxListPending", in, out, opts...) +func (c *queryClient) ListPendingCctx(ctx context.Context, in *QueryListPendingCctxRequest, opts ...grpc.CallOption) (*QueryListPendingCctxResponse, error) { + out := new(QueryListPendingCctxResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/ListPendingCctx", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *queryClient) CctxListPendingWithinRateLimit(ctx context.Context, in *QueryListCctxPendingWithRateLimitRequest, opts ...grpc.CallOption) (*QueryListCctxPendingWithRateLimitResponse, error) { - out := new(QueryListCctxPendingWithRateLimitResponse) - err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/CctxListPendingWithinRateLimit", in, out, opts...) +func (c *queryClient) ListPendingCctxWithinRateLimit(ctx context.Context, in *QueryListPendingCctxWithinRateLimitRequest, opts ...grpc.CallOption) (*QueryListPendingCctxWithinRateLimitResponse, error) { + out := new(QueryListPendingCctxWithinRateLimitResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/ListPendingCctxWithinRateLimit", in, out, opts...) if err != nil { return nil, err } @@ -2513,9 +2514,9 @@ type QueryServer interface { // Queries a list of send items. CctxAll(context.Context, *QueryAllCctxRequest) (*QueryAllCctxResponse, error) // Queries a list of pending cctxs. - CctxListPending(context.Context, *QueryListCctxPendingRequest) (*QueryListCctxPendingResponse, error) - // Queries a list of pending cctxs with rate limit. - CctxListPendingWithinRateLimit(context.Context, *QueryListCctxPendingWithRateLimitRequest) (*QueryListCctxPendingWithRateLimitResponse, error) + ListPendingCctx(context.Context, *QueryListPendingCctxRequest) (*QueryListPendingCctxResponse, error) + // Queries a list of pending cctxs within rate limit. + ListPendingCctxWithinRateLimit(context.Context, *QueryListPendingCctxWithinRateLimitRequest) (*QueryListPendingCctxWithinRateLimitResponse, error) ZetaAccounting(context.Context, *QueryZetaAccountingRequest) (*QueryZetaAccountingResponse, error) // Queries a list of lastMetaHeight items. LastZetaHeight(context.Context, *QueryLastZetaHeightRequest) (*QueryLastZetaHeightResponse, error) @@ -2578,11 +2579,11 @@ func (*UnimplementedQueryServer) CctxByNonce(ctx context.Context, req *QueryGetC func (*UnimplementedQueryServer) CctxAll(ctx context.Context, req *QueryAllCctxRequest) (*QueryAllCctxResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CctxAll not implemented") } -func (*UnimplementedQueryServer) CctxListPending(ctx context.Context, req *QueryListCctxPendingRequest) (*QueryListCctxPendingResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CctxListPending not implemented") +func (*UnimplementedQueryServer) ListPendingCctx(ctx context.Context, req *QueryListPendingCctxRequest) (*QueryListPendingCctxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListPendingCctx not implemented") } -func (*UnimplementedQueryServer) CctxListPendingWithinRateLimit(ctx context.Context, req *QueryListCctxPendingWithRateLimitRequest) (*QueryListCctxPendingWithRateLimitResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CctxListPendingWithinRateLimit not implemented") +func (*UnimplementedQueryServer) ListPendingCctxWithinRateLimit(ctx context.Context, req *QueryListPendingCctxWithinRateLimitRequest) (*QueryListPendingCctxWithinRateLimitResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListPendingCctxWithinRateLimit not implemented") } func (*UnimplementedQueryServer) ZetaAccounting(ctx context.Context, req *QueryZetaAccountingRequest) (*QueryZetaAccountingResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ZetaAccounting not implemented") @@ -2904,38 +2905,38 @@ func _Query_CctxAll_Handler(srv interface{}, ctx context.Context, dec func(inter return interceptor(ctx, in, info, handler) } -func _Query_CctxListPending_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryListCctxPendingRequest) +func _Query_ListPendingCctx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryListPendingCctxRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).CctxListPending(ctx, in) + return srv.(QueryServer).ListPendingCctx(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/zetachain.zetacore.crosschain.Query/CctxListPending", + FullMethod: "/zetachain.zetacore.crosschain.Query/ListPendingCctx", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).CctxListPending(ctx, req.(*QueryListCctxPendingRequest)) + return srv.(QueryServer).ListPendingCctx(ctx, req.(*QueryListPendingCctxRequest)) } return interceptor(ctx, in, info, handler) } -func _Query_CctxListPendingWithinRateLimit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryListCctxPendingWithRateLimitRequest) +func _Query_ListPendingCctxWithinRateLimit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryListPendingCctxWithinRateLimitRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).CctxListPendingWithinRateLimit(ctx, in) + return srv.(QueryServer).ListPendingCctxWithinRateLimit(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/zetachain.zetacore.crosschain.Query/CctxListPendingWithinRateLimit", + FullMethod: "/zetachain.zetacore.crosschain.Query/ListPendingCctxWithinRateLimit", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).CctxListPendingWithinRateLimit(ctx, req.(*QueryListCctxPendingWithRateLimitRequest)) + return srv.(QueryServer).ListPendingCctxWithinRateLimit(ctx, req.(*QueryListPendingCctxWithinRateLimitRequest)) } return interceptor(ctx, in, info, handler) } @@ -3067,12 +3068,12 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Handler: _Query_CctxAll_Handler, }, { - MethodName: "CctxListPending", - Handler: _Query_CctxListPending_Handler, + MethodName: "ListPendingCctx", + Handler: _Query_ListPendingCctx_Handler, }, { - MethodName: "CctxListPendingWithinRateLimit", - Handler: _Query_CctxListPendingWithinRateLimit_Handler, + MethodName: "ListPendingCctxWithinRateLimit", + Handler: _Query_ListPendingCctxWithinRateLimit_Handler, }, { MethodName: "ZetaAccounting", @@ -4250,7 +4251,7 @@ func (m *QueryAllCctxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *QueryListCctxPendingRequest) Marshal() (dAtA []byte, err error) { +func (m *QueryListPendingCctxRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -4260,12 +4261,12 @@ func (m *QueryListCctxPendingRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryListCctxPendingRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryListPendingCctxRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryListCctxPendingRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryListPendingCctxRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -4283,7 +4284,7 @@ func (m *QueryListCctxPendingRequest) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } -func (m *QueryListCctxPendingResponse) Marshal() (dAtA []byte, err error) { +func (m *QueryListPendingCctxResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -4293,12 +4294,12 @@ func (m *QueryListCctxPendingResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryListCctxPendingResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryListPendingCctxResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryListCctxPendingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryListPendingCctxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -4325,7 +4326,7 @@ func (m *QueryListCctxPendingResponse) MarshalToSizedBuffer(dAtA []byte) (int, e return len(dAtA) - i, nil } -func (m *QueryListCctxPendingWithRateLimitRequest) Marshal() (dAtA []byte, err error) { +func (m *QueryListPendingCctxWithinRateLimitRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -4335,12 +4336,12 @@ func (m *QueryListCctxPendingWithRateLimitRequest) Marshal() (dAtA []byte, err e return dAtA[:n], nil } -func (m *QueryListCctxPendingWithRateLimitRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryListPendingCctxWithinRateLimitRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryListCctxPendingWithRateLimitRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryListPendingCctxWithinRateLimitRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -4348,12 +4349,12 @@ func (m *QueryListCctxPendingWithRateLimitRequest) MarshalToSizedBuffer(dAtA []b if m.Limit != 0 { i = encodeVarintQuery(dAtA, i, uint64(m.Limit)) i-- - dAtA[i] = 0x10 + dAtA[i] = 0x8 } return len(dAtA) - i, nil } -func (m *QueryListCctxPendingWithRateLimitResponse) Marshal() (dAtA []byte, err error) { +func (m *QueryListPendingCctxWithinRateLimitResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -4363,12 +4364,12 @@ func (m *QueryListCctxPendingWithRateLimitResponse) Marshal() (dAtA []byte, err return dAtA[:n], nil } -func (m *QueryListCctxPendingWithRateLimitResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryListPendingCctxWithinRateLimitResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryListCctxPendingWithRateLimitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryListPendingCctxWithinRateLimitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -5108,7 +5109,7 @@ func (m *QueryAllCctxResponse) Size() (n int) { return n } -func (m *QueryListCctxPendingRequest) Size() (n int) { +func (m *QueryListPendingCctxRequest) Size() (n int) { if m == nil { return 0 } @@ -5123,7 +5124,7 @@ func (m *QueryListCctxPendingRequest) Size() (n int) { return n } -func (m *QueryListCctxPendingResponse) Size() (n int) { +func (m *QueryListPendingCctxResponse) Size() (n int) { if m == nil { return 0 } @@ -5141,7 +5142,7 @@ func (m *QueryListCctxPendingResponse) Size() (n int) { return n } -func (m *QueryListCctxPendingWithRateLimitRequest) Size() (n int) { +func (m *QueryListPendingCctxWithinRateLimitRequest) Size() (n int) { if m == nil { return 0 } @@ -5153,7 +5154,7 @@ func (m *QueryListCctxPendingWithRateLimitRequest) Size() (n int) { return n } -func (m *QueryListCctxPendingWithRateLimitResponse) Size() (n int) { +func (m *QueryListPendingCctxWithinRateLimitResponse) Size() (n int) { if m == nil { return 0 } @@ -8191,7 +8192,7 @@ func (m *QueryAllCctxResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryListCctxPendingRequest) Unmarshal(dAtA []byte) error { +func (m *QueryListPendingCctxRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -8214,10 +8215,10 @@ func (m *QueryListCctxPendingRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryListCctxPendingRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryListPendingCctxRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryListCctxPendingRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryListPendingCctxRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -8279,7 +8280,7 @@ func (m *QueryListCctxPendingRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryListCctxPendingResponse) Unmarshal(dAtA []byte) error { +func (m *QueryListPendingCctxResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -8302,10 +8303,10 @@ func (m *QueryListCctxPendingResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryListCctxPendingResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryListPendingCctxResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryListCctxPendingResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryListPendingCctxResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -8382,7 +8383,7 @@ func (m *QueryListCctxPendingResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryListCctxPendingWithRateLimitRequest) Unmarshal(dAtA []byte) error { +func (m *QueryListPendingCctxWithinRateLimitRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -8405,13 +8406,13 @@ func (m *QueryListCctxPendingWithRateLimitRequest) Unmarshal(dAtA []byte) error fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryListCctxPendingWithRateLimitRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryListPendingCctxWithinRateLimitRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryListCctxPendingWithRateLimitRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryListPendingCctxWithinRateLimitRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 2: + case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) } @@ -8451,7 +8452,7 @@ func (m *QueryListCctxPendingWithRateLimitRequest) Unmarshal(dAtA []byte) error } return nil } -func (m *QueryListCctxPendingWithRateLimitResponse) Unmarshal(dAtA []byte) error { +func (m *QueryListPendingCctxWithinRateLimitResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -8474,10 +8475,10 @@ func (m *QueryListCctxPendingWithRateLimitResponse) Unmarshal(dAtA []byte) error fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryListCctxPendingWithRateLimitResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryListPendingCctxWithinRateLimitResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryListCctxPendingWithRateLimitResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryListPendingCctxWithinRateLimitResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: diff --git a/x/crosschain/types/query.pb.gw.go b/x/crosschain/types/query.pb.gw.go index c4b3a02e5d..6a4aa48cdd 100644 --- a/x/crosschain/types/query.pb.gw.go +++ b/x/crosschain/types/query.pb.gw.go @@ -870,73 +870,73 @@ func local_request_Query_CctxAll_0(ctx context.Context, marshaler runtime.Marsha } var ( - filter_Query_CctxListPending_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + filter_Query_ListPendingCctx_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) -func request_Query_CctxListPending_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryListCctxPendingRequest +func request_Query_ListPendingCctx_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListPendingCctxRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CctxListPending_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListPendingCctx_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.CctxListPending(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.ListPendingCctx(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_CctxListPending_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryListCctxPendingRequest +func local_request_Query_ListPendingCctx_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListPendingCctxRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CctxListPending_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListPendingCctx_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.CctxListPending(ctx, &protoReq) + msg, err := server.ListPendingCctx(ctx, &protoReq) return msg, metadata, err } var ( - filter_Query_CctxListPendingWithinRateLimit_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + filter_Query_ListPendingCctxWithinRateLimit_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) -func request_Query_CctxListPendingWithinRateLimit_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryListCctxPendingWithRateLimitRequest +func request_Query_ListPendingCctxWithinRateLimit_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListPendingCctxWithinRateLimitRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CctxListPendingWithinRateLimit_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListPendingCctxWithinRateLimit_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.CctxListPendingWithinRateLimit(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.ListPendingCctxWithinRateLimit(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_CctxListPendingWithinRateLimit_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryListCctxPendingWithRateLimitRequest +func local_request_Query_ListPendingCctxWithinRateLimit_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryListPendingCctxWithinRateLimitRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CctxListPendingWithinRateLimit_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_ListPendingCctxWithinRateLimit_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.CctxListPendingWithinRateLimit(ctx, &protoReq) + msg, err := server.ListPendingCctxWithinRateLimit(ctx, &protoReq) return msg, metadata, err } @@ -1392,7 +1392,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) - mux.Handle("GET", pattern_Query_CctxListPending_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ListPendingCctx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -1403,7 +1403,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_CctxListPending_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_ListPendingCctx_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { @@ -1411,11 +1411,11 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } - forward_Query_CctxListPending_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ListPendingCctx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_CctxListPendingWithinRateLimit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ListPendingCctxWithinRateLimit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -1426,7 +1426,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_CctxListPendingWithinRateLimit_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_ListPendingCctxWithinRateLimit_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { @@ -1434,7 +1434,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } - forward_Query_CctxListPendingWithinRateLimit_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ListPendingCctxWithinRateLimit_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -1888,7 +1888,7 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) - mux.Handle("GET", pattern_Query_CctxListPending_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ListPendingCctx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1897,18 +1897,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_CctxListPending_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_ListPendingCctx_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_CctxListPending_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ListPendingCctx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_CctxListPendingWithinRateLimit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_ListPendingCctxWithinRateLimit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -1917,14 +1917,14 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_CctxListPendingWithinRateLimit_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_ListPendingCctxWithinRateLimit_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_Query_CctxListPendingWithinRateLimit_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_ListPendingCctxWithinRateLimit_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -2026,9 +2026,9 @@ var ( pattern_Query_CctxAll_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "cctx"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_CctxListPending_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "cctxPending"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_ListPendingCctx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "pendingCctx"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_CctxListPendingWithinRateLimit_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "cctxPendingWithRateLimit"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_ListPendingCctxWithinRateLimit_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "pendingCctxWithinRateLimit"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_ZetaAccounting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "zetaAccounting"}, "", runtime.AssumeColonVerbOpt(false))) @@ -2072,9 +2072,9 @@ var ( forward_Query_CctxAll_0 = runtime.ForwardResponseMessage - forward_Query_CctxListPending_0 = runtime.ForwardResponseMessage + forward_Query_ListPendingCctx_0 = runtime.ForwardResponseMessage - forward_Query_CctxListPendingWithinRateLimit_0 = runtime.ForwardResponseMessage + forward_Query_ListPendingCctxWithinRateLimit_0 = runtime.ForwardResponseMessage forward_Query_ZetaAccounting_0 = runtime.ForwardResponseMessage diff --git a/zetaclient/interfaces/interfaces.go b/zetaclient/interfaces/interfaces.go index 0c803c3497..0fbaba26b3 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) - ListPendingCctxWithRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, bool, error) + ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, 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 51c2afb7ef..e66c309cc6 100644 --- a/zetaclient/testutils/stub/core_bridge.go +++ b/zetaclient/testutils/stub/core_bridge.go @@ -121,7 +121,7 @@ func (z *MockZetaCoreBridge) ListPendingCctx(_ int64) ([]*cctxtypes.CrossChainTx return []*cctxtypes.CrossChainTx{}, 0, nil } -func (z *MockZetaCoreBridge) ListPendingCctxWithRatelimit() ([]*cctxtypes.CrossChainTx, uint64, bool, error) { +func (z *MockZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*cctxtypes.CrossChainTx, uint64, bool, error) { if z.paused { return nil, 0, false, errors.New(ErrMsgPaused) } diff --git a/zetaclient/zetabridge/query.go b/zetaclient/zetabridge/query.go index fab225c571..09d3b20e82 100644 --- a/zetaclient/zetabridge/query.go +++ b/zetaclient/zetabridge/query.go @@ -125,9 +125,9 @@ func (b *ZetaCoreBridge) GetObserverList() ([]string, error) { func (b *ZetaCoreBridge) ListPendingCctx(chainID int64) ([]*types.CrossChainTx, uint64, error) { client := types.NewQueryClient(b.grpcConn) maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) - resp, err := client.CctxListPending( + resp, err := client.ListPendingCctx( context.Background(), - &types.QueryListCctxPendingRequest{ChainId: chainID}, + &types.QueryListPendingCctxRequest{ChainId: chainID}, maxSizeOption, ) if err != nil { @@ -136,15 +136,15 @@ func (b *ZetaCoreBridge) ListPendingCctx(chainID int64) ([]*types.CrossChainTx, return resp.CrossChainTx, resp.TotalPending, nil } -// ListPendingCctxWithRatelimit returns a list of pending cctxs that do not exceed the outbound rate limit +// 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) ListPendingCctxWithRatelimit() ([]*types.CrossChainTx, uint64, bool, error) { +func (b *ZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*types.CrossChainTx, uint64, bool, error) { client := types.NewQueryClient(b.grpcConn) maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) - resp, err := client.CctxListPendingWithinRateLimit( + resp, err := client.ListPendingCctxWithinRateLimit( context.Background(), - &types.QueryListCctxPendingWithRateLimitRequest{}, + &types.QueryListPendingCctxWithinRateLimitRequest{}, maxSizeOption, ) if err != nil { diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index a40b7f22b8..c66f15f50a 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -191,7 +191,7 @@ 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, error) { - cctxList, totalPending, rateLimitExceeded, err := co.bridge.ListPendingCctxWithRatelimit() + cctxList, totalPending, rateLimitExceeded, err := co.bridge.ListPendingCctxWithinRatelimit() if err != nil { return nil, err } From 0eb3b8a2e7fb864f0ceb09e1b2a90c5e7ddeec7b Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 19 Apr 2024 15:23:45 -0500 Subject: [PATCH 11/40] fixed unit test compile --- zetaclient/zetabridge/query_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zetaclient/zetabridge/query_test.go b/zetaclient/zetabridge/query_test.go index 3844482dd3..ea76fa78fd 100644 --- a/zetaclient/zetabridge/query_test.go +++ b/zetaclient/zetabridge/query_test.go @@ -267,7 +267,7 @@ func TestZetaCoreBridge_GetObserverList(t *testing.T) { } func TestZetaCoreBridge_ListPendingCctx(t *testing.T) { - expectedOutput := crosschainTypes.QueryListCctxPendingResponse{ + expectedOutput := crosschainTypes.QueryListPendingCctxResponse{ CrossChainTx: []*crosschainTypes.CrossChainTx{ { Index: "cross-chain4456", @@ -275,8 +275,8 @@ func TestZetaCoreBridge_ListPendingCctx(t *testing.T) { }, TotalPending: 1, } - input := crosschainTypes.QueryListCctxPendingRequest{ChainId: 7000} - method := "/zetachain.zetacore.crosschain.Query/CctxListPending" + input := crosschainTypes.QueryListPendingCctxRequest{ChainId: 7000} + method := "/zetachain.zetacore.crosschain.Query/ListPendingCctx" server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) server.Serve() defer closeMockServer(t, server) From 60e5dcc5408777b9e0e746b9999316954003a444 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 19 Apr 2024 19:45:12 -0500 Subject: [PATCH 12/40] added unit test for fallback query --- testutil/sample/fungible.go | 30 ++++ x/crosschain/keeper/cctx_utils_test.go | 8 +- x/crosschain/keeper/evm_deposit_test.go | 22 +-- x/crosschain/keeper/gas_payment_test.go | 28 +-- .../keeper/grpc_query_cctx_rate_limit_test.go | 168 +++++++++++++++++- x/crosschain/keeper/grpc_query_cctx_test.go | 14 +- .../msg_server_add_to_intx_tracker_test.go | 16 +- .../msg_server_migrate_tss_funds_test.go | 28 +-- .../msg_server_refund_aborted_tx_test.go | 26 +-- .../keeper/msg_server_vote_inbound_tx_test.go | 4 +- .../msg_server_vote_outbound_tx_test.go | 12 +- .../keeper/msg_server_whitelist_erc20_test.go | 10 +- x/crosschain/keeper/process_inbound_test.go | 20 +-- x/crosschain/keeper/process_outbound_test.go | 16 +- x/crosschain/keeper/refund_test.go | 32 ++-- x/crosschain/keeper/utils_test.go | 6 +- 16 files changed, 318 insertions(+), 122 deletions(-) diff --git a/testutil/sample/fungible.go b/testutil/sample/fungible.go index 73fdc2b2e3..3be91d06a3 100644 --- a/testutil/sample/fungible.go +++ b/testutil/sample/fungible.go @@ -3,6 +3,7 @@ package sample import ( "testing" + "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/fungible/types" ) @@ -22,6 +23,35 @@ func ForeignCoins(t *testing.T, address string) types.ForeignCoins { } } +func ForeignCoinList(t *testing.T, zrc20ETH, zrc20BTC, zrc20ERC20, erc20Asset string) []types.ForeignCoins { + // eth and btc chain id + ethChainID := chains.GoerliLocalnetChain().ChainId + btcChainID := chains.BtcRegtestChain().ChainId + + // add zrc20 ETH + fcGas := ForeignCoins(t, zrc20ETH) + fcGas.Asset = "" + fcGas.ForeignChainId = ethChainID + fcGas.Decimals = 18 + fcGas.CoinType = coin.CoinType_Gas + + // add zrc20 BTC + fcBTC := ForeignCoins(t, zrc20BTC) + fcBTC.Asset = "" + fcBTC.ForeignChainId = btcChainID + fcBTC.Decimals = 8 + fcBTC.CoinType = coin.CoinType_Gas + + // add zrc20 ERC20 + fcERC20 := ForeignCoins(t, zrc20ERC20) + fcERC20.Asset = erc20Asset + fcERC20.ForeignChainId = ethChainID + fcERC20.Decimals = 6 + fcERC20.CoinType = coin.CoinType_ERC20 + + return []types.ForeignCoins{fcGas, fcBTC, fcERC20} +} + func SystemContract() *types.SystemContract { return &types.SystemContract{ SystemContract: EthAddress().String(), diff --git a/x/crosschain/keeper/cctx_utils_test.go b/x/crosschain/keeper/cctx_utils_test.go index e0dedbb41d..af6445744c 100644 --- a/x/crosschain/keeper/cctx_utils_test.go +++ b/x/crosschain/keeper/cctx_utils_test.go @@ -42,7 +42,7 @@ func TestGetRevertGasLimit(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) gas := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foo", "FOO") @@ -63,7 +63,7 @@ func TestGetRevertGasLimit(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) asset := sample.EthAddress().String() zrc20Addr := deployZRC20( @@ -107,7 +107,7 @@ func TestGetRevertGasLimit(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() zk.FungibleKeeper.SetForeignCoins(ctx, fungibletypes.ForeignCoins{ Zrc20ContractAddress: sample.EthAddress().String(), @@ -141,7 +141,7 @@ func TestGetRevertGasLimit(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() asset := sample.EthAddress().String() zk.FungibleKeeper.SetForeignCoins(ctx, fungibletypes.ForeignCoins{ diff --git a/x/crosschain/keeper/evm_deposit_test.go b/x/crosschain/keeper/evm_deposit_test.go index 1f6cd4fad4..d30424697c 100644 --- a/x/crosschain/keeper/evm_deposit_test.go +++ b/x/crosschain/keeper/evm_deposit_test.go @@ -84,7 +84,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() @@ -127,7 +127,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() @@ -186,7 +186,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() @@ -273,7 +273,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() @@ -317,7 +317,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() @@ -361,7 +361,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() @@ -404,7 +404,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() @@ -447,7 +447,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() @@ -487,7 +487,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() cctx := sample.CrossChainTx(t, "foo") cctx.GetCurrentOutTxParam().Receiver = sample.EthAddress().String() @@ -509,7 +509,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() @@ -555,7 +555,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { UseFungibleMock: true, }) - senderChain := getValidEthChainID(t) + senderChain := getValidEthChainID() fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() diff --git a/x/crosschain/keeper/gas_payment_test.go b/x/crosschain/keeper/gas_payment_test.go index 569a360327..8079c3c67a 100644 --- a/x/crosschain/keeper/gas_payment_test.go +++ b/x/crosschain/keeper/gas_payment_test.go @@ -30,7 +30,7 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) @@ -72,7 +72,7 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { t.Run("should fail if not coin type gas", func(t *testing.T) { k, ctx, _, _ := testkeeper.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() cctx := types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_Zeta, @@ -98,7 +98,7 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") @@ -127,7 +127,7 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") @@ -170,7 +170,7 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin, erc20 and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) assetAddress := sample.EthAddress().String() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) @@ -236,7 +236,7 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { t.Run("should fail if not coin type erc20", func(t *testing.T) { k, ctx, _, _ := testkeeper.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() cctx := types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_Gas, @@ -262,7 +262,7 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") @@ -291,7 +291,7 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin, erc20 and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) assetAddress := sample.EthAddress().String() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) @@ -333,7 +333,7 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin, erc20 and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) assetAddress := sample.EthAddress().String() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) @@ -385,7 +385,7 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin, erc20 and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) assetAddress := sample.EthAddress().String() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) @@ -453,7 +453,7 @@ func TestKeeper_PayGasInZetaAndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") @@ -514,7 +514,7 @@ func TestKeeper_PayGasInZetaAndUpdateCctx(t *testing.T) { t.Run("should fail if pay gas in zeta with coin type other than zeta", func(t *testing.T) { k, ctx, _, _ := testkeeper.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() cctx := types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_Gas, @@ -540,7 +540,7 @@ func TestKeeper_PayGasInZetaAndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") @@ -571,7 +571,7 @@ func TestKeeper_PayGasInZetaAndUpdateCctx(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) // deploy gas coin and set fee params - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") 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 5c2eee4066..31939862b5 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -1,16 +1,182 @@ package keeper_test import ( + "fmt" + "sort" "testing" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/coin" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/keeper" + "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) -func TestKeeper_CctxListPendingWithRateLimit(t *testing.T) { +// createTestRateLimiterFlags creates a custom rate limiter flags +func createTestRateLimiterFlags( + zrc20ETH string, + zrc20BTC string, + zrc20USDT string, + ethRate string, + btcRate string, + usdtRate string, +) types.RateLimiterFlags { + var rateLimiterFlags = types.RateLimiterFlags{ + Enabled: true, + Window: 100, // 100 zeta blocks, 10 minutes + Rate: math.NewUint(1000), // 1000 ZETA + Conversions: []types.Conversion{ + // ETH + { + Zrc20: zrc20ETH, + Rate: sdk.MustNewDecFromStr(ethRate), + }, + // BTC + { + Zrc20: zrc20BTC, + Rate: sdk.MustNewDecFromStr(btcRate), + }, + // USDT + { + Zrc20: zrc20USDT, + Rate: sdk.MustNewDecFromStr(usdtRate), + }, + }, + } + return rateLimiterFlags +} + +// createCctxWithCopyTypeAndBlockRange +// - create 1 cctx per block from lowBlock to highBlock (inclusive) +// +// return created cctxs +func createCctxWithCopyTypeAndHeightRange( + t *testing.T, + ctx sdk.Context, + k keeper.Keeper, + zk keepertest.ZetaKeepers, + tss observertypes.TSS, + lowBlock uint64, + highBlock uint64, + chainID int64, + coinType coin.CoinType, + asset string, + amount uint64, + status types.CctxStatus, +) (cctxs []*types.CrossChainTx) { + // create 1 pending cctxs per block + for i := lowBlock; i <= highBlock; i++ { + nonce := i - 1 + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", chainID, nonce)) + cctx.CctxStatus.Status = status + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = coinType + cctx.InboundTxParams.Asset = asset + cctx.InboundTxParams.InboundTxObservedExternalHeight = i + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(amount) + cctx.GetCurrentOutTxParam().OutboundTxTssNonce = nonce + k.SetCrossChainTx(ctx, *cctx) + zk.ObserverKeeper.SetNonceToCctx(ctx, observertypes.NonceToCctx{ + ChainId: chainID, + // #nosec G701 always in range for tests + Nonce: int64(nonce), + CctxIndex: cctx.Index, + Tss: tss.TssPubkey, + }) + cctxs = append(cctxs, cctx) + } + return cctxs +} + +// setPendingNonces sets the pending nonces for the given chainID +func setPendingNonces( + ctx sdk.Context, + zk keepertest.ZetaKeepers, + chainID int64, + nonceLow int64, + nonceHigh int64, + tssPubKey string, +) { + zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ + ChainId: chainID, + NonceLow: nonceLow, + NonceHigh: nonceHigh, + Tss: tssPubKey, + }) +} + +// setupForeignCoins adds ETH, BTC, USDT to the foreign coins store +func setupForeignCoins( + t *testing.T, + ctx sdk.Context, + zk keepertest.ZetaKeepers, + zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT string, +) { + // set foreign coins + fCoins := sample.ForeignCoinList(t, zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT) + for _, fc := range fCoins { + zk.FungibleKeeper.SetForeignCoins(ctx, fc) + } +} + +func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { + // chain IDs + ethChainID := getValidEthChainID() + + // define cctx status + statusPending := types.CctxStatus_PendingOutbound + statusMined := types.CctxStatus_OutboundMined + t.Run("should fail for empty req", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) _, err := k.ListPendingCctxWithinRateLimit(ctx, nil) require.ErrorContains(t, err, "invalid request") }) + t.Run("should use fallback query", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // Set TSS + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + + // Set rate limiter flags as disabled + rateLimiterFlags := sample.RateLimiterFlags() + rateLimiterFlags.Enabled = false + k.SetRateLimiterFlags(ctx, rateLimiterFlags) + + // Create cctxs [0~999] and [1000~1199] for Eth chain, 0.001 ETH per cctx + _ = createCctxWithCopyTypeAndHeightRange(t, ctx, *k, zk, tss, 1, 1000, ethChainID, coin.CoinType_Gas, "", uint64(1e15), statusMined) + cctxETH := createCctxWithCopyTypeAndHeightRange(t, ctx, *k, zk, tss, 1001, 1200, ethChainID, coin.CoinType_Gas, "", uint64(1e15), statusPending) + + // Set Eth chain pending nonces which contains 100 missing cctxs + setPendingNonces(ctx, zk, ethChainID, 1100, 1200, tss.TssPubkey) + + // Query pending cctxs use small limit + res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{Limit: 100}) + require.NoError(t, err) + require.Equal(t, 100, len(res.CrossChainTx)) + + // sort res.CrossChainTx by outbound nonce ascending so that we can compare with cctxETH + sort.Slice(res.CrossChainTx, func(i, j int) bool { + return res.CrossChainTx[i].GetCurrentOutTxParam().OutboundTxTssNonce < res.CrossChainTx[j].GetCurrentOutTxParam().OutboundTxTssNonce + }) + require.EqualValues(t, cctxETH[:100], res.CrossChainTx) + require.EqualValues(t, uint64(200), res.TotalPending) + + // Query pending cctxs use max limit + res, err = k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{Limit: keeper.MaxPendingCctxs}) + require.NoError(t, err) + require.Equal(t, 200, len(res.CrossChainTx)) + + // sort res.CrossChainTx by outbound nonce ascending so that we can compare with cctxETH + sort.Slice(res.CrossChainTx, func(i, j int) bool { + return res.CrossChainTx[i].GetCurrentOutTxParam().OutboundTxTssNonce < res.CrossChainTx[j].GetCurrentOutTxParam().OutboundTxTssNonce + }) + require.EqualValues(t, cctxETH, res.CrossChainTx) + require.EqualValues(t, uint64(200), res.TotalPending) + }) } diff --git a/x/crosschain/keeper/grpc_query_cctx_test.go b/x/crosschain/keeper/grpc_query_cctx_test.go index 240d0bfe92..f530ef305b 100644 --- a/x/crosschain/keeper/grpc_query_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_test.go @@ -94,7 +94,7 @@ func TestKeeper_CctxListPending(t *testing.T) { t.Run("can retrieve pending cctx in range", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() tss := sample.Tss() zk.ObserverKeeper.SetTSS(ctx, tss) cctxs := createCctxWithNonceRange(t, ctx, *k, 1000, 2000, chainID, tss, zk) @@ -114,7 +114,7 @@ func TestKeeper_CctxListPending(t *testing.T) { t.Run("can retrieve pending cctx with range smaller than max", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() tss := sample.Tss() zk.ObserverKeeper.SetTSS(ctx, tss) cctxs := createCctxWithNonceRange(t, ctx, *k, 1000, 1100, chainID, tss, zk) @@ -128,7 +128,7 @@ func TestKeeper_CctxListPending(t *testing.T) { t.Run("can retrieve pending cctx with pending cctx below nonce low", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() tss := sample.Tss() zk.ObserverKeeper.SetTSS(ctx, tss) cctxs := createCctxWithNonceRange(t, ctx, *k, 1000, 2000, chainID, tss, zk) @@ -157,7 +157,7 @@ func TestKeeper_CctxListPending(t *testing.T) { t.Run("error if some before low nonce are missing", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() tss := sample.Tss() zk.ObserverKeeper.SetTSS(ctx, tss) cctxs := createCctxWithNonceRange(t, ctx, *k, 1000, 2000, chainID, tss, zk) @@ -226,7 +226,7 @@ func TestKeeper_CctxByNonce(t *testing.T) { t.Run("should error if nonce to cctx not found", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() tss := sample.Tss() zk.ObserverKeeper.SetTSS(ctx, tss) @@ -239,7 +239,7 @@ func TestKeeper_CctxByNonce(t *testing.T) { t.Run("should error if crosschain tx not found", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() tss := sample.Tss() zk.ObserverKeeper.SetTSS(ctx, tss) nonce := 1000 @@ -262,7 +262,7 @@ func TestKeeper_CctxByNonce(t *testing.T) { t.Run("should return if crosschain tx found", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() tss := sample.Tss() zk.ObserverKeeper.SetTSS(ctx, tss) nonce := 1000 diff --git a/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go b/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go index a18d7e737a..9c0547e9fb 100644 --- a/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go +++ b/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go @@ -36,7 +36,7 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) txHash := "string" - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: nonAdmin, @@ -63,7 +63,7 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(nil) txHash := "string" - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: sample.AccAddress(), @@ -95,7 +95,7 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) txHash := "string" - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ @@ -128,7 +128,7 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(true) txHash := "string" - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: admin, @@ -163,7 +163,7 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("error")) txHash := "string" - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: admin, @@ -197,7 +197,7 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything).Return(nil, false) txHash := "string" - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: admin, @@ -232,7 +232,7 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(nil, errors.New("error")) txHash := "string" - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ @@ -272,7 +272,7 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { lightclientMock.On("VerifyProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte("invalid"), nil) txHash := "string" - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go index f13a0065ae..5865c6ccca 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go @@ -89,7 +89,7 @@ func TestKeeper_MigrateTSSFundsForChain(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) gp, found := k.GetMedianGasPriceInUint(ctx, chain.ChainId) @@ -149,7 +149,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, @@ -172,7 +172,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) observerMock.On("IsInboundEnabled", mock.Anything).Return(true) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, @@ -197,7 +197,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { observerMock.On("GetTSS", mock.Anything).Return(observertypes.TSS{}, false) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, @@ -223,7 +223,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { observerMock.On("GetAllTSS", mock.Anything).Return([]observertypes.TSS{}) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, @@ -250,7 +250,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { observerMock.On("GetAllTSS", mock.Anything).Return([]observertypes.TSS{tss}) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, @@ -280,7 +280,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { observerMock.On("GetAllTSS", mock.Anything).Return([]observertypes.TSS{tss2}) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, @@ -311,7 +311,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { observerMock.On("GetPendingNonces", mock.Anything, mock.Anything, mock.Anything).Return(observertypes.PendingNonces{}, false) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, @@ -331,7 +331,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ @@ -359,7 +359,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("100") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ @@ -384,7 +384,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, false, true) _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ @@ -409,7 +409,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") indexString, tssPubkey := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) k.GetObserverKeeper().SetPendingNonces(ctx, observertypes.PendingNonces{ @@ -441,7 +441,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") indexString, tssPubkey := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) k.GetObserverKeeper().SetPendingNonces(ctx, observertypes.PendingNonces{ @@ -482,7 +482,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) msgServer := keeper.NewMsgServerImpl(*k) - chain := getValidEthChain(t) + chain := getValidEthChain() amount := sdkmath.NewUintFromString("10000000000000000000") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, false, false) currentTss, found := k.GetObserverKeeper().GetTSS(ctx) diff --git a/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go index f5615c8963..0b54a93c69 100644 --- a/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go +++ b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go @@ -42,7 +42,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -81,7 +81,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -120,7 +120,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -160,7 +160,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -192,7 +192,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -224,7 +224,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -263,7 +263,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() asset := sample.EthAddress().String() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -352,7 +352,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -383,7 +383,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -414,7 +414,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -447,7 +447,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -507,7 +507,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) @@ -537,7 +537,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) admin := sample.AccAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, false) diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go index f8d6a4bcb8..379f24741b 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go @@ -312,7 +312,7 @@ func TestKeeper_SaveInbound(t *testing.T) { zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) eventIndex := sample.Uint64InRange(1, 100) k.SaveInbound(ctx, cctx, eventIndex) @@ -326,7 +326,7 @@ func TestKeeper_SaveInbound(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) hash := sample.Hash() cctx.InboundTxParams.InboundTxObservedHash = hash.String() diff --git a/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go b/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go index fa9884f25a..11ea50a514 100644 --- a/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go +++ b/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go @@ -129,7 +129,7 @@ func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" observer := sample.AccAddress() tss := sample.Tss() @@ -180,7 +180,7 @@ func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" observer := sample.AccAddress() tss := sample.Tss() @@ -239,7 +239,7 @@ func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" observer := sample.AccAddress() tss := sample.Tss() @@ -302,7 +302,7 @@ func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" observer := sample.AccAddress() tss := sample.Tss() @@ -354,7 +354,7 @@ func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) { // Setup mock data receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) @@ -401,7 +401,7 @@ func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) { // Setup mock data receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20_test.go b/x/crosschain/keeper/msg_server_whitelist_erc20_test.go index 2d193e8f5b..6db8243405 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20_test.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20_test.go @@ -26,7 +26,7 @@ func TestKeeper_WhitelistERC20(t *testing.T) { msgServer := crosschainkeeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() setSupportedChain(ctx, zk, chainID) admin := sample.AccAddress() @@ -104,7 +104,7 @@ func TestKeeper_WhitelistERC20(t *testing.T) { _, err := msgServer.WhitelistERC20(ctx, &types.MsgWhitelistERC20{ Creator: admin, Erc20Address: sample.EthAddress().Hex(), - ChainId: getValidEthChainID(t), + ChainId: getValidEthChainID(), Name: "foo", Symbol: "FOO", Decimals: 18, @@ -128,7 +128,7 @@ func TestKeeper_WhitelistERC20(t *testing.T) { _, err := msgServer.WhitelistERC20(ctx, &types.MsgWhitelistERC20{ Creator: admin, Erc20Address: "invalid", - ChainId: getValidEthChainID(t), + ChainId: getValidEthChainID(), Name: "foo", Symbol: "FOO", Decimals: 18, @@ -149,7 +149,7 @@ func TestKeeper_WhitelistERC20(t *testing.T) { authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() asset := sample.EthAddress().Hex() fc := sample.ForeignCoins(t, sample.EthAddress().Hex()) fc.Asset = asset @@ -176,7 +176,7 @@ func TestKeeper_WhitelistERC20(t *testing.T) { msgServer := crosschainkeeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() admin := sample.AccAddress() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) diff --git a/x/crosschain/keeper/process_inbound_test.go b/x/crosschain/keeper/process_inbound_test.go index ec3bcaea94..3e86cd68c5 100644 --- a/x/crosschain/keeper/process_inbound_test.go +++ b/x/crosschain/keeper/process_inbound_test.go @@ -83,7 +83,7 @@ func TestKeeper_ProcessInboundZEVMDeposit(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() errDeposit := fmt.Errorf("deposit failed") // Setup expected calls @@ -113,7 +113,7 @@ func TestKeeper_ProcessInboundZEVMDeposit(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" errDeposit := fmt.Errorf("deposit failed") @@ -145,7 +145,7 @@ func TestKeeper_ProcessInboundZEVMDeposit(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" // Setup expected calls @@ -182,7 +182,7 @@ func TestKeeper_ProcessInboundZEVMDeposit(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" // Setup expected calls @@ -220,7 +220,7 @@ func TestKeeper_ProcessInboundZEVMDeposit(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" errDeposit := fmt.Errorf("deposit failed") @@ -260,7 +260,7 @@ func TestKeeper_ProcessInboundZEVMDeposit(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" errDeposit := fmt.Errorf("deposit failed") @@ -299,7 +299,7 @@ func TestKeeper_ProcessInboundZEVMDeposit(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" errDeposit := fmt.Errorf("deposit failed") @@ -335,7 +335,7 @@ func TestKeeper_ProcessInboundProcessCrosschainMsgPassing(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - receiverChain := getValidEthChain(t) + receiverChain := getValidEthChain() // mock successful PayGasAndUpdateCctx keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *receiverChain, "") @@ -360,7 +360,7 @@ func TestKeeper_ProcessInboundProcessCrosschainMsgPassing(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - receiverChain := getValidEthChain(t) + receiverChain := getValidEthChain() // mock unsuccessful PayGasAndUpdateCctx observerMock.On("GetSupportedChainFromChainID", mock.Anything, receiverChain.ChainId). @@ -384,7 +384,7 @@ func TestKeeper_ProcessInboundProcessCrosschainMsgPassing(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - receiverChain := getValidEthChain(t) + receiverChain := getValidEthChain() // mock successful PayGasAndUpdateCctx keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *receiverChain, "") diff --git a/x/crosschain/keeper/process_outbound_test.go b/x/crosschain/keeper/process_outbound_test.go index 2bd17818c6..b4dbbef200 100644 --- a/x/crosschain/keeper/process_outbound_test.go +++ b/x/crosschain/keeper/process_outbound_test.go @@ -137,7 +137,7 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" // mock successful GetRevertGasLimit for ERC20 @@ -169,7 +169,7 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" // mock successful GetRevertGasLimit for ERC20 @@ -201,7 +201,7 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" // mock successful GetRevertGasLimit for ERC20 @@ -232,7 +232,7 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" // mock successful GetRevertGasLimit for ERC20 @@ -258,7 +258,7 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" // mock failed GetRevertGasLimit for ERC20 @@ -324,7 +324,7 @@ func TestKeeper_ProcessOutbound(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) @@ -353,7 +353,7 @@ func TestKeeper_ProcessOutbound(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) @@ -381,7 +381,7 @@ func TestKeeper_ProcessOutbound(t *testing.T) { observerMock := keepertest.GetCrosschainObserverMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) - senderChain := getValidEthChain(t) + senderChain := getValidEthChain() asset := "" cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) diff --git a/x/crosschain/keeper/refund_test.go b/x/crosschain/keeper/refund_test.go index 09732bd23a..5f00310be3 100644 --- a/x/crosschain/keeper/refund_test.go +++ b/x/crosschain/keeper/refund_test.go @@ -23,7 +23,7 @@ func TestKeeper_RefundAmountOnZetaChainGas(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") @@ -54,7 +54,7 @@ func TestKeeper_RefundAmountOnZetaChainGas(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() fungibleMock.On("GetGasCoinForForeignCoin", mock.Anything, mock.Anything).Return(fungibletypes.ForeignCoins{ Zrc20ContractAddress: "0x", @@ -84,7 +84,7 @@ func TestKeeper_RefundAmountOnZetaChainGas(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() fungibleMock.On("GetGasCoinForForeignCoin", mock.Anything, mock.Anything).Return(fungibletypes.ForeignCoins{ Zrc20ContractAddress: sample.EthAddress().Hex(), @@ -113,7 +113,7 @@ func TestKeeper_RefundAmountOnZetaChainGas(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") @@ -137,7 +137,7 @@ func TestKeeper_RefundAmountOnZetaChainGas(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) err := k.RefundAmountOnZetaChainGas(ctx, types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ @@ -160,7 +160,7 @@ func TestKeeper_RefundAmountOnZetaChainGas(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) _ = setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") @@ -188,7 +188,7 @@ func TestKeeper_RefundAmountOnZetaChainZeta(t *testing.T) { k, ctx, sdkk, _ := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() err := k.RefundAmountOnZetaChainZeta(ctx, types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ @@ -240,7 +240,7 @@ func TestKeeper_RefundAmountOnZetaChainZeta(t *testing.T) { fungibleMock.On("DepositCoinZeta", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("err")) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() err := k.RefundAmountOnZetaChainZeta(ctx, types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ @@ -263,7 +263,7 @@ func TestKeeper_RefundAmountOnZetaChainZeta(t *testing.T) { k, ctx, sdkk, _ := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() err := k.RefundAmountOnZetaChainZeta(ctx, types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ @@ -284,7 +284,7 @@ func TestKeeper_RefundAmountOnZetaChainZeta(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() err := k.RefundAmountOnZetaChainZeta(ctx, types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ @@ -310,7 +310,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) asset := sample.EthAddress().String() sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() // deploy zrc20 deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) @@ -376,7 +376,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) asset := sample.EthAddress().String() sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() err := k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ @@ -403,7 +403,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) asset := sample.EthAddress().String() sender := sample.EthAddress() - chainID := getValidEthChainID(t) + chainID := getValidEthChainID() fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, mock.Anything, mock.Anything).Return(fungibletypes.ForeignCoins{ Zrc20ContractAddress: sample.EthAddress().Hex(), @@ -462,7 +462,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_ERC20, - SenderChainId: getValidEthChainID(t), + SenderChainId: getValidEthChainID(), Sender: sample.EthAddress().String(), Amount: math.Uint{}, }}, @@ -473,7 +473,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_ERC20, - SenderChainId: getValidEthChainID(t), + SenderChainId: getValidEthChainID(), Sender: sample.EthAddress().String(), Amount: math.ZeroUint(), }}, @@ -485,7 +485,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_ERC20, - SenderChainId: getValidEthChainID(t), + SenderChainId: getValidEthChainID(), Sender: sample.EthAddress().String(), Asset: sample.EthAddress().String(), Amount: math.NewUint(42), diff --git a/x/crosschain/keeper/utils_test.go b/x/crosschain/keeper/utils_test.go index 732ec5d0bd..3b3ab6967c 100644 --- a/x/crosschain/keeper/utils_test.go +++ b/x/crosschain/keeper/utils_test.go @@ -22,12 +22,12 @@ import ( ) // getValidEthChainID get a valid eth chain id -func getValidEthChainID(t *testing.T) int64 { - return getValidEthChain(t).ChainId +func getValidEthChainID() int64 { + return getValidEthChain().ChainId } // getValidEthChain get a valid eth chain -func getValidEthChain(_ *testing.T) *chains.Chain { +func getValidEthChain() *chains.Chain { goerli := chains.GoerliLocalnetChain() return &goerli } From 101457c9c0287ef1d74e135ea84aea33fde87654 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Sat, 20 Apr 2024 00:47:43 -0500 Subject: [PATCH 13/40] added unit tests for cctx value conversion --- testutil/keeper/mocks/crosschain/fungible.go | 6 +- .../keeper/grpc_query_cctx_rate_limit.go | 55 ++++--- .../keeper/grpc_query_cctx_rate_limit_test.go | 141 ++++++++++++++++++ x/crosschain/types/expected_keepers.go | 2 +- x/fungible/keeper/foreign_coins.go | 16 +- 5 files changed, 179 insertions(+), 41 deletions(-) diff --git a/testutil/keeper/mocks/crosschain/fungible.go b/testutil/keeper/mocks/crosschain/fungible.go index 587c1676a6..a695f0d35d 100644 --- a/testutil/keeper/mocks/crosschain/fungible.go +++ b/testutil/keeper/mocks/crosschain/fungible.go @@ -254,12 +254,12 @@ func (_m *CrosschainFungibleKeeper) GetAllForeignCoinsForChain(ctx types.Context return r0 } -// GetAllForeignERC20CoinMap provides a mock function with given fields: ctx -func (_m *CrosschainFungibleKeeper) GetAllForeignERC20CoinMap(ctx types.Context) map[int64]map[string]fungibletypes.ForeignCoins { +// GetAllForeignCoinMap provides a mock function with given fields: ctx +func (_m *CrosschainFungibleKeeper) GetAllForeignCoinMap(ctx types.Context) map[int64]map[string]fungibletypes.ForeignCoins { ret := _m.Called(ctx) if len(ret) == 0 { - panic("no return value specified for GetAllForeignERC20CoinMap") + panic("no return value specified for GetAllForeignCoinMap") } var r0 map[int64]map[string]fungibletypes.ForeignCoins diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index b3b30ca939..eafd1364b5 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -76,11 +76,11 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que // get the conversion rates for all foreign coins var gasCoinRates map[int64]sdk.Dec var erc20CoinRates map[int64]map[string]sdk.Dec - var erc20Coins map[int64]map[string]fungibletypes.ForeignCoins + var foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins var rateLimitInZeta sdk.Dec if applyLimit { gasCoinRates, erc20CoinRates = k.GetRateLimiterRates(ctx) - erc20Coins = k.fungibleKeeper.GetAllForeignERC20CoinMap(ctx) + foreignCoinMap = k.fungibleKeeper.GetAllForeignCoinMap(ctx) rateLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()) } @@ -130,7 +130,7 @@ LoopBackwards: break } // criteria #2: we should finish the RPC call if the rate limit is exceeded - if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { + if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true break LoopBackwards } @@ -167,7 +167,7 @@ LoopForwards: break LoopForwards } // criteria #2: we should finish the RPC call if the rate limit is exceeded - if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, erc20Coins, &totalCctxValueInZeta, rateLimitInZeta) { + if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true break LoopForwards } @@ -182,39 +182,26 @@ LoopForwards: }, nil } -// convertCctxValue converts the value of the cctx in ZETA using given conversion rates -func convertCctxValue( +// ConvertCctxValue converts the value of the cctx in ZETA using given conversion rates +func ConvertCctxValue( chainID int64, cctx *types.CrossChainTx, gasCoinRates map[int64]sdk.Dec, erc20CoinRates map[int64]map[string]sdk.Dec, - erc20Coins map[int64]map[string]fungibletypes.ForeignCoins, + foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins, ) sdk.Dec { var rate sdk.Dec var decimals uint64 switch cctx.InboundTxParams.CoinType { case coin.CoinType_Zeta: // no conversion needed for ZETA - rate = sdk.NewDec(1) + amountCctx := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) + return amountCctx.Quo(sdk.NewDec(10).Power(18)) case coin.CoinType_Gas: rate = gasCoinRates[chainID] case coin.CoinType_ERC20: - // get the ERC20 coin decimals - _, found := erc20Coins[chainID] - if !found { - // skip if no coin found for this chainID - return sdk.NewDec(0) - } - fCoin, found := erc20Coins[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] - if !found { - // skip if no coin found for this Asset - return sdk.NewDec(0) - } - // #nosec G701 always in range - decimals = uint64(fCoin.Decimals) - // get the ERC20 coin rate - _, found = erc20CoinRates[chainID] + _, found := erc20CoinRates[chainID] if !found { // skip if no rate found for this chainID return sdk.NewDec(0) @@ -224,16 +211,28 @@ func convertCctxValue( // skip CoinType_Cmd return sdk.NewDec(0) } - // should not happen, return 0 to skip if it happens - if rate.LTE(sdk.NewDec(0)) { + if rate.IsNil() || rate.LTE(sdk.NewDec(0)) { + return sdk.NewDec(0) + } + + // get foreign coin decimals + _, found := foreignCoinMap[chainID] + if !found { + // skip if no coin found for this chainID + return sdk.NewDec(0) + } + fCoin, found := foreignCoinMap[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] + if !found { + // skip if no coin found for this Asset return sdk.NewDec(0) } + decimals = uint64(fCoin.Decimals) // the reciprocal of `rate` is the amount of zrc20 needed to buy 1 ZETA // for example, given rate = 0.8, the reciprocal is 1.25, which means 1.25 ZRC20 can buy 1 ZETA // given decimals = 6, the `oneZeta` amount will be 1.25 * 10^6 = 1250000 - oneZrc20 := sdk.NewDec(1).Power(decimals) + oneZrc20 := sdk.NewDec(10).Power(decimals) oneZeta := oneZrc20.Quo(rate) // convert asset amount into ZETA @@ -249,11 +248,11 @@ func rateLimitExceeded( cctx *types.CrossChainTx, gasCoinRates map[int64]sdk.Dec, erc20CoinRates map[int64]map[string]sdk.Dec, - erc20Coins map[int64]map[string]fungibletypes.ForeignCoins, + foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins, currentCctxValue *sdk.Dec, rateLimitValue sdk.Dec, ) bool { - amountZeta := convertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates, erc20Coins) + amountZeta := ConvertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) *currentCctxValue = currentCctxValue.Add(amountZeta) return currentCctxValue.GT(rateLimitValue) } 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 31939862b5..dfe37ea4b3 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "fmt" "sort" + "strings" "testing" "cosmossdk.io/math" @@ -123,6 +124,146 @@ func setupForeignCoins( } } +func Test_ConvertCctxValue(t *testing.T) { + // chain IDs + ethChainID := getValidEthChainID() + btcChainID := getValidBtcChainID() + + // zrc20 addresses for ETH, BTC, USDT and asset for USDT + zrc20ETH := sample.EthAddress().Hex() + zrc20BTC := sample.EthAddress().Hex() + zrc20USDT := sample.EthAddress().Hex() + assetUSDT := sample.EthAddress().Hex() + + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // Set TSS + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + + // Set foreign coins + setupForeignCoins(t, ctx, zk, zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT) + + // Set rate limiter flags + rateLimiterFlags := createTestRateLimiterFlags(zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8") + k.SetRateLimiterFlags(ctx, rateLimiterFlags) + + // get rate limiter rates + gasCoinRates, erc20CoinRates := k.GetRateLimiterRates(ctx) + foreignCoinMap := zk.FungibleKeeper.GetAllForeignCoinMap(ctx) + + t.Run("should convert cctx ZETA value correctly", func(t *testing.T) { + // create cctx with 0.3 ZETA + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) + cctx.InboundTxParams.CoinType = coin.CoinType_Zeta + cctx.InboundTxParams.Asset = "" + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(3e17) // 0.3 ZETA + + // convert cctx value + value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) + require.Equal(t, sdk.MustNewDecFromStr("0.3"), value) + }) + t.Run("should convert cctx ETH value correctly", func(t *testing.T) { + // create cctx with 0.003 ETH + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) + cctx.InboundTxParams.CoinType = coin.CoinType_Gas + cctx.InboundTxParams.Asset = "" + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(3e15) // 0.003 ETH + + // convert cctx value + value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) + require.Equal(t, sdk.MustNewDecFromStr("7.5"), value) + }) + t.Run("should convert cctx BTC value correctly", func(t *testing.T) { + // create cctx with 0.0007 BTC + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", btcChainID, 1)) + cctx.InboundTxParams.CoinType = coin.CoinType_Gas + cctx.InboundTxParams.Asset = "" + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(70000) // 0.0007 BTC + + // convert cctx value + value := keeper.ConvertCctxValue(btcChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) + require.Equal(t, sdk.MustNewDecFromStr("35.0"), value) + }) + t.Run("should convert cctx USDT value correctly", func(t *testing.T) { + // create cctx with 3 USDT + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) + cctx.InboundTxParams.CoinType = coin.CoinType_ERC20 + cctx.InboundTxParams.Asset = assetUSDT + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(3e6) // 3 USDT + + // convert cctx value + value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) + require.Equal(t, sdk.MustNewDecFromStr("2.4"), value) + }) + t.Run("should return 0 if no rate found for chainID", func(t *testing.T) { + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) + cctx.InboundTxParams.CoinType = coin.CoinType_ERC20 + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) + + // use nil erc20CoinRates map to convert cctx value + value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, nil, foreignCoinMap) + require.Equal(t, sdk.NewDec(0), value) + }) + t.Run("should return 0 if coinType is CoinType_Cmd", func(t *testing.T) { + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) + cctx.InboundTxParams.CoinType = coin.CoinType_Cmd + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) + + // convert cctx value + value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) + require.Equal(t, sdk.NewDec(0), value) + }) + t.Run("should return 0 on nil rate or rate <= 0", func(t *testing.T) { + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) + cctx.InboundTxParams.CoinType = coin.CoinType_Gas + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) + + // use nil gasCoinRates map to convert cctx value + value := keeper.ConvertCctxValue(ethChainID, cctx, nil, erc20CoinRates, foreignCoinMap) + require.Equal(t, sdk.NewDec(0), value) + + // set rate to 0 + zeroCoinRates, _ := k.GetRateLimiterRates(ctx) + zeroCoinRates[ethChainID] = sdk.NewDec(0) + + // convert cctx value + value = keeper.ConvertCctxValue(ethChainID, cctx, zeroCoinRates, erc20CoinRates, foreignCoinMap) + require.Equal(t, sdk.NewDec(0), value) + + // set rate to -1 + negativeCoinRates, _ := k.GetRateLimiterRates(ctx) + negativeCoinRates[ethChainID] = sdk.NewDec(-1) + + // convert cctx value + value = keeper.ConvertCctxValue(ethChainID, cctx, negativeCoinRates, erc20CoinRates, foreignCoinMap) + require.Equal(t, sdk.NewDec(0), value) + }) + t.Run("should return 0 if no coin found for chainID", func(t *testing.T) { + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) + cctx.InboundTxParams.CoinType = coin.CoinType_Gas + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) + + // use empty foreignCoinMap to convert cctx value + value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, nil) + require.Equal(t, sdk.NewDec(0), value) + }) + t.Run("should return 0 if no coin found for asset", func(t *testing.T) { + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) + cctx.InboundTxParams.CoinType = coin.CoinType_ERC20 + cctx.InboundTxParams.Asset = assetUSDT + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) + + // delete assetUSDT from foreignCoinMap for ethChainID + tempCoinMap := zk.FungibleKeeper.GetAllForeignCoinMap(ctx) + delete(tempCoinMap[ethChainID], strings.ToLower(assetUSDT)) + + // convert cctx value + value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, tempCoinMap) + require.Equal(t, sdk.NewDec(0), value) + }) +} + func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { // chain IDs ethChainID := getValidEthChainID() diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index af4f580794..fb03a86281 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -96,7 +96,7 @@ type ObserverKeeper interface { type FungibleKeeper interface { GetForeignCoins(ctx sdk.Context, zrc20Addr string) (val fungibletypes.ForeignCoins, found bool) GetAllForeignCoins(ctx sdk.Context) (list []fungibletypes.ForeignCoins) - GetAllForeignERC20CoinMap(ctx sdk.Context) map[int64]map[string]fungibletypes.ForeignCoins + GetAllForeignCoinMap(ctx sdk.Context) map[int64]map[string]fungibletypes.ForeignCoins SetForeignCoins(ctx sdk.Context, foreignCoins fungibletypes.ForeignCoins) GetAllForeignCoinsForChain(ctx sdk.Context, foreignChainID int64) (list []fungibletypes.ForeignCoins) GetForeignCoinFromAsset(ctx sdk.Context, asset string, chainID int64) (fungibletypes.ForeignCoins, bool) diff --git a/x/fungible/keeper/foreign_coins.go b/x/fungible/keeper/foreign_coins.go index 5f0a31da4a..2f3eaf7918 100644 --- a/x/fungible/keeper/foreign_coins.go +++ b/x/fungible/keeper/foreign_coins.go @@ -81,20 +81,18 @@ func (k Keeper) GetAllForeignCoins(ctx sdk.Context) (list []types.ForeignCoins) return } -// GetAllForeignERC20CoinMap returns all foreign ERC20 coins in a map of chainID -> asset -> coin -func (k Keeper) GetAllForeignERC20CoinMap(ctx sdk.Context) map[int64]map[string]types.ForeignCoins { +// GetAllForeignCoinMap returns all foreign ERC20 coins in a map of chainID -> asset -> coin +func (k Keeper) GetAllForeignCoinMap(ctx sdk.Context) map[int64]map[string]types.ForeignCoins { allForeignCoins := k.GetAllForeignCoins(ctx) - erc20CoinMap := make(map[int64]map[string]types.ForeignCoins) + fCoinMap := make(map[int64]map[string]types.ForeignCoins) for _, c := range allForeignCoins { - if c.CoinType == coin.CoinType_ERC20 { - if _, found := erc20CoinMap[c.ForeignChainId]; !found { - erc20CoinMap[c.ForeignChainId] = make(map[string]types.ForeignCoins) - } - erc20CoinMap[c.ForeignChainId][strings.ToLower(c.Asset)] = c + if _, found := fCoinMap[c.ForeignChainId]; !found { + fCoinMap[c.ForeignChainId] = make(map[string]types.ForeignCoins) } + fCoinMap[c.ForeignChainId][strings.ToLower(c.Asset)] = c } - return erc20CoinMap + return fCoinMap } // GetGasCoinForForeignCoin returns the gas coin for a given chain From 874d5f3dc705c3df5c1a33bd2c319e2ee2ac03d1 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Sat, 20 Apr 2024 00:59:31 -0500 Subject: [PATCH 14/40] add changelog entry --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index ede402b460..23074d6e1f 100644 --- a/changelog.md +++ b/changelog.md @@ -81,6 +81,7 @@ * [1985](https://github.com/zeta-chain/node/pull/1985) - improve fungible module coverage * [1992](https://github.com/zeta-chain/node/pull/1992) - remove setupKeeper from crosschain module * [2008](https://github.com/zeta-chain/node/pull/2008) - add test for connector bytecode update +* [2060](https://github.com/zeta-chain/node/pull/2060) - add unit test for rate limiter query ### Fixes From 16955b899c64b29890145c8156d90a975d1e6bd3 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Sat, 20 Apr 2024 13:35:44 -0500 Subject: [PATCH 15/40] added unit tests for query pending cctxs within rate limit --- .../keeper/grpc_query_cctx_rate_limit.go | 48 +++-- .../keeper/grpc_query_cctx_rate_limit_test.go | 197 +++++++++++++++--- 2 files changed, 203 insertions(+), 42 deletions(-) diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index eafd1364b5..41ebd2aa65 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -2,6 +2,7 @@ package keeper import ( "context" + "sort" "strings" sdk "github.com/cosmos/cosmos-sdk/types" @@ -90,6 +91,12 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que return uint32(len(cctxs)) >= limit } + // if a cctx falls within the rate limiter window + isCctxInWindow := func(cctx *types.CrossChainTx) bool { + // #nosec G701 checked positive + return cctx.InboundTxParams.InboundTxObservedExternalHeight >= uint64(leftWindowBoundary) + } + // query pending nonces for each foreign chain // Note: The pending nonces could change during the RPC call, so query them beforehand pendingNoncesMap := make(map[int64]*observertypes.PendingNonces) @@ -114,23 +121,29 @@ 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) if err != nil { return nil, err } + 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 - // #nosec G701 always positive - if nonce < endNonce && cctx.InboundTxParams.InboundTxObservedExternalHeight < uint64(leftWindowBoundary) { + if nonce < endNonce && !inWindow { break } // criteria #2: we should finish the RPC call if the rate limit is exceeded - if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, rateLimitInZeta) { + if inWindow && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true break LoopBackwards } @@ -143,12 +156,6 @@ LoopBackwards: } } } - - // 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 forwards for pending cctxs for each foreign chain @@ -161,13 +168,14 @@ LoopForwards: if err != nil { return nil, err } + inWindow := isCctxInWindow(cctx) // 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, rateLimitInZeta) { + if inWindow && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, rateLimitInZeta) { limitExceeded = true break LoopForwards } @@ -175,6 +183,14 @@ LoopForwards: } } + // 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 { + return cctxs[i].GetCurrentOutTxParam().OutboundTxTssNonce < cctxs[j].GetCurrentOutTxParam().OutboundTxTssNonce + } + return cctxs[i].GetCurrentOutTxParam().ReceiverChainId < cctxs[j].GetCurrentOutTxParam().ReceiverChainId + }) + return &types.QueryListPendingCctxWithinRateLimitResponse{ CrossChainTx: cctxs, TotalPending: totalPending, @@ -229,15 +245,17 @@ func ConvertCctxValue( } decimals = uint64(fCoin.Decimals) - // the reciprocal of `rate` is the amount of zrc20 needed to buy 1 ZETA - // for example, given rate = 0.8, the reciprocal is 1.25, which means 1.25 ZRC20 can buy 1 ZETA - // given decimals = 6, the `oneZeta` amount will be 1.25 * 10^6 = 1250000 + // given decimals = 6, the `oneZrc20` amount will be 10^6 = 1000000 oneZrc20 := sdk.NewDec(10).Power(decimals) - oneZeta := oneZrc20.Quo(rate) // convert asset amount into ZETA + // step 1: convert the amount into ZRC20 integer amount + // step 2: convert the ZRC20 integer amount into decimal amount + // given amountCctx = 2000000, rate = 0.8, decimals = 6 + // the amountZrc20 = 2000000 * 0.8 = 1600000, the amountZeta = 1600000 / 1000000 = 1.6 amountCctx := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) - amountZeta := amountCctx.Quo(oneZeta) + amountZrc20 := amountCctx.Mul(rate) + amountZeta := amountZrc20.Quo(oneZrc20) return amountZeta } 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 dfe37ea4b3..4e6b142329 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -2,7 +2,6 @@ package keeper_test import ( "fmt" - "sort" "strings" "testing" @@ -28,8 +27,8 @@ func createTestRateLimiterFlags( ) types.RateLimiterFlags { var rateLimiterFlags = types.RateLimiterFlags{ Enabled: true, - Window: 100, // 100 zeta blocks, 10 minutes - Rate: math.NewUint(1000), // 1000 ZETA + Window: 500, // 500 zeta blocks, 50 minutes + Rate: math.NewUint(5000), // 5000 ZETA Conversions: []types.Conversion{ // ETH { @@ -51,11 +50,11 @@ func createTestRateLimiterFlags( return rateLimiterFlags } -// createCctxWithCopyTypeAndBlockRange +// createCctxsWithCoinTypeAndHeightRange // - create 1 cctx per block from lowBlock to highBlock (inclusive) // // return created cctxs -func createCctxWithCopyTypeAndHeightRange( +func createCctxsWithCoinTypeAndHeightRange( t *testing.T, ctx sdk.Context, k keeper.Keeper, @@ -78,6 +77,7 @@ func createCctxWithCopyTypeAndHeightRange( cctx.InboundTxParams.CoinType = coinType cctx.InboundTxParams.Asset = asset cctx.InboundTxParams.InboundTxObservedExternalHeight = i + cctx.GetCurrentOutTxParam().ReceiverChainId = chainID cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(amount) cctx.GetCurrentOutTxParam().OutboundTxTssNonce = nonce k.SetCrossChainTx(ctx, *cctx) @@ -124,6 +124,59 @@ func setupForeignCoins( } } +// createKeeperForRateLimiterTest creates a keeper filled with cctxs for rate limiter test +func createKeeperForRateLimiterTest(t *testing.T) (k *keeper.Keeper, ctx sdk.Context, cctxsETH, cctxsBTC []*types.CrossChainTx, rateLimiterFlags types.RateLimiterFlags) { + // chain IDs + ethChainID := getValidEthChainID() + btcChainID := getValidBtcChainID() + + // zrc20 addresses for ETH, BTC, USDT and asset for USDT + zrc20ETH := sample.EthAddress().Hex() + zrc20BTC := sample.EthAddress().Hex() + zrc20USDT := sample.EthAddress().Hex() + assetUSDT := sample.EthAddress().Hex() + + // create test rate limiter flags + rateLimiterFlags = createTestRateLimiterFlags(zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8") + + // define cctx status + statusPending := types.CctxStatus_PendingOutbound + statusMined := types.CctxStatus_OutboundMined + + // create test keepers + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // Set TSS + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + + // Set foreign coins + setupForeignCoins(t, ctx, zk, zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT) + + // Set rate limiter flags + k.SetRateLimiterFlags(ctx, rateLimiterFlags) + + // Create cctxs [0~999] and [1000~1199] for Eth chain, 0.001 ETH (2.5 ZETA) per cctx + createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1, 1000, ethChainID, coin.CoinType_Gas, "", uint64(1e15), statusMined) + cctxsETH = createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1001, 1200, ethChainID, coin.CoinType_Gas, "", uint64(1e15), statusPending) + + // Set Eth chain pending nonces, [1000~1099] are missed cctxs + setPendingNonces(ctx, zk, ethChainID, 1100, 1200, tss.TssPubkey) + + // Create cctxs [0~999] and [1000~1199] for Btc chain, 0.00001 BTC (0.5 ZETA) per cctx + createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1, 1000, btcChainID, coin.CoinType_Gas, "", 1000, statusMined) + cctxsBTC = createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1001, 1200, btcChainID, coin.CoinType_Gas, "", 1000, statusPending) + require.NotNil(t, cctxsBTC) + + // Set Btc chain pending nonces, [1000~1099] are missed cctxs + setPendingNonces(ctx, zk, btcChainID, 1100, 1200, tss.TssPubkey) + + // Set current block height to 1201, the window is now [701, 1201], the nonces [700~1200] fall into the window + ctx = ctx.WithBlockHeight(1201) + + return k, ctx, cctxsETH, cctxsBTC, rateLimiterFlags +} + func Test_ConvertCctxValue(t *testing.T) { // chain IDs ethChainID := getValidEthChainID() @@ -265,18 +318,46 @@ func Test_ConvertCctxValue(t *testing.T) { } func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { - // chain IDs - ethChainID := getValidEthChainID() - - // define cctx status - statusPending := types.CctxStatus_PendingOutbound - statusMined := types.CctxStatus_OutboundMined - t.Run("should fail for empty req", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) _, err := k.ListPendingCctxWithinRateLimit(ctx, nil) require.ErrorContains(t, err, "invalid request") }) + t.Run("height out of range", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + + // Set rate limiter flags as disabled + rFlags := sample.RateLimiterFlags() + k.SetRateLimiterFlags(ctx, rFlags) + + ctx = ctx.WithBlockHeight(0) + _, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) + require.ErrorContains(t, err, "height out of range") + }) + t.Run("tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + + // Set rate limiter flags as disabled + rFlags := sample.RateLimiterFlags() + k.SetRateLimiterFlags(ctx, rFlags) + + _, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) + require.ErrorContains(t, err, "tss not found") + }) + t.Run("pending nonces not found", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // Set rate limiter flags as disabled + rFlags := sample.RateLimiterFlags() + k.SetRateLimiterFlags(ctx, rFlags) + + // Set TSS + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + + _, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) + require.ErrorContains(t, err, "pending nonces not found") + }) t.Run("should use fallback query", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) @@ -285,13 +366,14 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { zk.ObserverKeeper.SetTSS(ctx, tss) // Set rate limiter flags as disabled - rateLimiterFlags := sample.RateLimiterFlags() - rateLimiterFlags.Enabled = false - k.SetRateLimiterFlags(ctx, rateLimiterFlags) + rFlags := sample.RateLimiterFlags() + rFlags.Enabled = false + k.SetRateLimiterFlags(ctx, rFlags) // Create cctxs [0~999] and [1000~1199] for Eth chain, 0.001 ETH per cctx - _ = createCctxWithCopyTypeAndHeightRange(t, ctx, *k, zk, tss, 1, 1000, ethChainID, coin.CoinType_Gas, "", uint64(1e15), statusMined) - cctxETH := createCctxWithCopyTypeAndHeightRange(t, ctx, *k, zk, tss, 1001, 1200, ethChainID, coin.CoinType_Gas, "", uint64(1e15), statusPending) + ethChainID := getValidEthChainID() + _ = createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1, 1000, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_OutboundMined) + cctxsETH := createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1001, 1200, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_PendingOutbound) // Set Eth chain pending nonces which contains 100 missing cctxs setPendingNonces(ctx, zk, ethChainID, 1100, 1200, tss.TssPubkey) @@ -301,11 +383,8 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { require.NoError(t, err) require.Equal(t, 100, len(res.CrossChainTx)) - // sort res.CrossChainTx by outbound nonce ascending so that we can compare with cctxETH - sort.Slice(res.CrossChainTx, func(i, j int) bool { - return res.CrossChainTx[i].GetCurrentOutTxParam().OutboundTxTssNonce < res.CrossChainTx[j].GetCurrentOutTxParam().OutboundTxTssNonce - }) - require.EqualValues(t, cctxETH[:100], res.CrossChainTx) + // check response + require.EqualValues(t, cctxsETH[:100], res.CrossChainTx) require.EqualValues(t, uint64(200), res.TotalPending) // Query pending cctxs use max limit @@ -313,11 +392,75 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { require.NoError(t, err) require.Equal(t, 200, len(res.CrossChainTx)) - // sort res.CrossChainTx by outbound nonce ascending so that we can compare with cctxETH - sort.Slice(res.CrossChainTx, func(i, j int) bool { - return res.CrossChainTx[i].GetCurrentOutTxParam().OutboundTxTssNonce < res.CrossChainTx[j].GetCurrentOutTxParam().OutboundTxTssNonce - }) - require.EqualValues(t, cctxETH, res.CrossChainTx) + // check response + require.EqualValues(t, cctxsETH, res.CrossChainTx) require.EqualValues(t, uint64(200), res.TotalPending) }) + t.Run("can retrieve pending cctx in range without exceeding rate limit", func(t *testing.T) { + k, ctx, cctxsETH, cctxsBTC, _ := createKeeperForRateLimiterTest(t) + + res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) + require.NoError(t, err) + require.Equal(t, 400, len(res.CrossChainTx)) + require.EqualValues(t, cctxsETH, res.CrossChainTx[0:200]) + require.EqualValues(t, cctxsBTC, res.CrossChainTx[200:400]) + require.EqualValues(t, uint64(400), res.TotalPending) + require.False(t, res.RateLimitExceeded) + }) + t.Run("Set rate to a lower value (< 1200) to early break the LoopBackwards with criteria #2", func(t *testing.T) { + k, ctx, cctxsETH, cctxsBTC, rlFlags := createKeeperForRateLimiterTest(t) + + rlFlags.Rate = math.NewUint(1000) // 1000 ZETA + k.SetRateLimiterFlags(ctx, rlFlags) + + res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) + require.NoError(t, err) + require.Equal(t, 200, len(res.CrossChainTx)) + require.EqualValues(t, cctxsETH[:100], res.CrossChainTx[0:100]) + require.EqualValues(t, cctxsBTC[:100], res.CrossChainTx[100:200]) + require.EqualValues(t, uint64(400), res.TotalPending) + require.True(t, res.RateLimitExceeded) + }) + t.Run("Set high rate and big window to early to break inner loop with the criteria #1", func(t *testing.T) { + k, ctx, cctxsETH, cctxsBTC, rlFlags := createKeeperForRateLimiterTest(t) + + // The left boundary will be 51 (1201-1150), less than the endNonce 100 (1100 - 10000) + rlFlags.Rate = math.NewUint(10000) + rlFlags.Window = 1150 + k.SetRateLimiterFlags(ctx, rlFlags) + + res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) + require.NoError(t, err) + require.Equal(t, 400, len(res.CrossChainTx)) + require.EqualValues(t, cctxsETH, res.CrossChainTx[0:200]) + require.EqualValues(t, cctxsBTC, res.CrossChainTx[200:400]) + require.EqualValues(t, uint64(400), res.TotalPending) + require.False(t, res.RateLimitExceeded) + }) + t.Run("Set lower request limit to early break the LoopForwards loop", func(t *testing.T) { + k, ctx, cctxsETH, cctxsBTC, _ := createKeeperForRateLimiterTest(t) + + res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{Limit: 300}) + require.NoError(t, err) + require.Equal(t, 300, len(res.CrossChainTx)) + require.EqualValues(t, cctxsETH[:100], res.CrossChainTx[0:100]) + require.EqualValues(t, cctxsBTC, res.CrossChainTx[100:300]) + require.EqualValues(t, uint64(400), res.TotalPending) + require.False(t, res.RateLimitExceeded) + }) + t.Run("Set rate to middle value (1200 < rate < 1500) to early break the LoopForwards loop with criteria #2", func(t *testing.T) { + k, ctx, cctxsETH, cctxsBTC, rlFlags := createKeeperForRateLimiterTest(t) + + rlFlags.Window = 500 + rlFlags.Rate = math.NewUint(1300) // 1300 ZETA + k.SetRateLimiterFlags(ctx, rlFlags) + + res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) + require.NoError(t, err) + require.Equal(t, 320, len(res.CrossChainTx)) // 120 ETH cctx + 200 BTC cctx + require.EqualValues(t, cctxsETH[:120], res.CrossChainTx[0:120]) + require.EqualValues(t, cctxsBTC, res.CrossChainTx[120:320]) + require.EqualValues(t, uint64(400), res.TotalPending) + require.True(t, res.RateLimitExceeded) + }) } From 249bcaa3ebed890cfb87804a9872bb5f839ab654 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Sat, 20 Apr 2024 14:24:29 -0500 Subject: [PATCH 16/40] added total value in rate limiter window for monitoring purpose --- docs/openapi/openapi.swagger.yaml | 3 + proto/crosschain/query.proto | 3 +- typescript/crosschain/query_pb.d.ts | 7 +- .../keeper/grpc_query_cctx_rate_limit.go | 1 + .../keeper/grpc_query_cctx_rate_limit_test.go | 8 +- x/crosschain/types/query.pb.go | 279 ++++++++++-------- zetaclient/interfaces/interfaces.go | 2 +- zetaclient/testutils/stub/core_bridge.go | 6 +- zetaclient/zetabridge/query.go | 6 +- zetaclient/zetacore_observer.go | 14 +- 10 files changed, 193 insertions(+), 136 deletions(-) diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 1eb8fe4593..139ee8110a 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -54013,6 +54013,9 @@ definitions: total_pending: type: string format: uint64 + value_within_window: + type: string + format: uint64 rate_limit_exceeded: type: boolean crosschainQueryMessagePassingProtocolFeeResponse: diff --git a/proto/crosschain/query.proto b/proto/crosschain/query.proto index b017e2c523..1ecd041c48 100644 --- a/proto/crosschain/query.proto +++ b/proto/crosschain/query.proto @@ -270,7 +270,8 @@ message QueryListPendingCctxWithinRateLimitRequest { message QueryListPendingCctxWithinRateLimitResponse { repeated CrossChainTx cross_chain_tx = 1; uint64 total_pending = 2; - bool rate_limit_exceeded = 3; + uint64 value_within_window = 3; + bool rate_limit_exceeded = 4; } message QueryLastZetaHeightRequest {} diff --git a/typescript/crosschain/query_pb.d.ts b/typescript/crosschain/query_pb.d.ts index 34b4a44f38..62e8fdfe3a 100644 --- a/typescript/crosschain/query_pb.d.ts +++ b/typescript/crosschain/query_pb.d.ts @@ -910,7 +910,12 @@ export declare class QueryListPendingCctxWithinRateLimitResponse extends Message totalPending: bigint; /** - * @generated from field: bool rate_limit_exceeded = 3; + * @generated from field: uint64 value_within_window = 3; + */ + valueWithinWindow: bigint; + + /** + * @generated from field: bool rate_limit_exceeded = 4; */ 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 41ebd2aa65..287ebe1147 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -194,6 +194,7 @@ LoopForwards: return &types.QueryListPendingCctxWithinRateLimitResponse{ CrossChainTx: cctxs, TotalPending: totalPending, + ValueWithinWindow: totalCctxValueInZeta.TruncateInt().Uint64(), RateLimitExceeded: limitExceeded, }, nil } 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 4e6b142329..8b53958ad3 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -406,11 +406,13 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { require.EqualValues(t, cctxsBTC, res.CrossChainTx[200:400]) require.EqualValues(t, uint64(400), res.TotalPending) require.False(t, res.RateLimitExceeded) + require.Equal(t, uint64(1500), res.ValueWithinWindow) // 500 * (2.5 + 0.5) }) t.Run("Set rate to a lower value (< 1200) to early break the LoopBackwards with criteria #2", func(t *testing.T) { k, ctx, cctxsETH, cctxsBTC, rlFlags := createKeeperForRateLimiterTest(t) - rlFlags.Rate = math.NewUint(1000) // 1000 ZETA + rate := uint64(1000) // 1000 ZETA + rlFlags.Rate = math.NewUint(rate) k.SetRateLimiterFlags(ctx, rlFlags) res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) @@ -420,6 +422,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { require.EqualValues(t, cctxsBTC[:100], res.CrossChainTx[100:200]) require.EqualValues(t, uint64(400), res.TotalPending) require.True(t, res.RateLimitExceeded) + require.True(t, res.ValueWithinWindow >= rate) }) t.Run("Set high rate and big window to early to break inner loop with the criteria #1", func(t *testing.T) { k, ctx, cctxsETH, cctxsBTC, rlFlags := createKeeperForRateLimiterTest(t) @@ -436,6 +439,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { require.EqualValues(t, cctxsBTC, res.CrossChainTx[200:400]) require.EqualValues(t, uint64(400), res.TotalPending) require.False(t, res.RateLimitExceeded) + require.EqualValues(t, uint64(3450), res.ValueWithinWindow) // 1150 * (2.5 + 0.5) }) t.Run("Set lower request limit to early break the LoopForwards loop", func(t *testing.T) { k, ctx, cctxsETH, cctxsBTC, _ := createKeeperForRateLimiterTest(t) @@ -447,6 +451,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { require.EqualValues(t, cctxsBTC, res.CrossChainTx[100:300]) require.EqualValues(t, uint64(400), res.TotalPending) require.False(t, res.RateLimitExceeded) + require.EqualValues(t, uint64(1250), res.ValueWithinWindow) // 500 * 0.5 + 400 * 2.5 }) t.Run("Set rate to middle value (1200 < rate < 1500) to early break the LoopForwards loop with criteria #2", func(t *testing.T) { k, ctx, cctxsETH, cctxsBTC, rlFlags := createKeeperForRateLimiterTest(t) @@ -462,5 +467,6 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { require.EqualValues(t, cctxsBTC, res.CrossChainTx[120:320]) require.EqualValues(t, uint64(400), res.TotalPending) require.True(t, res.RateLimitExceeded) + require.True(t, res.ValueWithinWindow >= 1300) }) } diff --git a/x/crosschain/types/query.pb.go b/x/crosschain/types/query.pb.go index 879dcce901..4d7c826736 100644 --- a/x/crosschain/types/query.pb.go +++ b/x/crosschain/types/query.pb.go @@ -1638,7 +1638,8 @@ 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"` - RateLimitExceeded bool `protobuf:"varint,3,opt,name=rate_limit_exceeded,json=rateLimitExceeded,proto3" json:"rate_limit_exceeded,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"` } func (m *QueryListPendingCctxWithinRateLimitResponse) Reset() { @@ -1692,6 +1693,13 @@ func (m *QueryListPendingCctxWithinRateLimitResponse) GetTotalPending() uint64 { return 0 } +func (m *QueryListPendingCctxWithinRateLimitResponse) GetValueWithinWindow() uint64 { + if m != nil { + return m.ValueWithinWindow + } + return 0 +} + func (m *QueryListPendingCctxWithinRateLimitResponse) GetRateLimitExceeded() bool { if m != nil { return m.RateLimitExceeded @@ -2102,126 +2110,128 @@ func init() { func init() { proto.RegisterFile("crosschain/query.proto", fileDescriptor_65a992045e92a606) } var fileDescriptor_65a992045e92a606 = []byte{ - // 1901 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xc1, 0x6f, 0xdc, 0x4a, - 0x19, 0xcf, 0x64, 0x9b, 0xf7, 0xd2, 0x49, 0xda, 0xbc, 0xcc, 0x0b, 0x25, 0xf8, 0x25, 0x9b, 0xd6, - 0xa1, 0x4d, 0x48, 0xc8, 0xfa, 0x25, 0x7d, 0xcd, 0xa3, 0x6d, 0x1e, 0x62, 0x93, 0xbe, 0xa4, 0x81, - 0xb4, 0x4d, 0x57, 0x41, 0x45, 0x45, 0xc8, 0x9a, 0x78, 0xa7, 0x5e, 0xab, 0x8e, 0x9d, 0xae, 0xbd, - 0xd5, 0xa6, 0x51, 0x2e, 0x3d, 0x70, 0x46, 0xea, 0x81, 0x0b, 0x57, 0x44, 0x0f, 0x1c, 0x38, 0x20, - 0x38, 0x20, 0x15, 0x21, 0xa0, 0xf4, 0x58, 0x09, 0x81, 0x10, 0x48, 0x08, 0xb5, 0xfc, 0x05, 0xfc, - 0x05, 0xc8, 0xe3, 0xcf, 0xbb, 0x63, 0xaf, 0xbd, 0x3b, 0xd9, 0x6c, 0x0f, 0x3d, 0x75, 0xed, 0x99, - 0xef, 0x9b, 0xdf, 0xef, 0x9b, 0x6f, 0x3e, 0x7f, 0xf3, 0x6b, 0xf0, 0x39, 0xa3, 0xea, 0x7a, 0x9e, - 0x51, 0xa1, 0x96, 0xa3, 0x3d, 0xaa, 0xb1, 0xea, 0x41, 0x61, 0xbf, 0xea, 0xfa, 0x2e, 0x99, 0x7c, - 0xc2, 0x7c, 0xca, 0x5f, 0x17, 0xf8, 0x2f, 0xb7, 0xca, 0x0a, 0xcd, 0xa9, 0xca, 0x9c, 0xe1, 0x7a, - 0x7b, 0xae, 0xa7, 0xed, 0x52, 0x8f, 0x85, 0x76, 0xda, 0xe3, 0xc5, 0x5d, 0xe6, 0xd3, 0x45, 0x6d, - 0x9f, 0x9a, 0x96, 0x43, 0x7d, 0xcb, 0x75, 0x42, 0x57, 0xca, 0x94, 0xb0, 0x04, 0xff, 0xa9, 0xf3, - 0xdf, 0xba, 0x5f, 0x87, 0x09, 0x8a, 0x30, 0xc1, 0xa4, 0x9e, 0xbe, 0x5f, 0xb5, 0x0c, 0x06, 0x63, - 0xd3, 0xc2, 0x18, 0xb7, 0xd1, 0x2b, 0xd4, 0xab, 0xe8, 0xbe, 0xab, 0x1b, 0x46, 0xc3, 0x41, 0xbe, - 0x65, 0x92, 0x5f, 0xa5, 0xc6, 0x43, 0x56, 0x85, 0x71, 0x55, 0x18, 0xb7, 0xa9, 0xe7, 0xeb, 0xbb, - 0xb6, 0x6b, 0x3c, 0xd4, 0x2b, 0xcc, 0x32, 0x2b, 0x7e, 0x0a, 0x4a, 0xb7, 0xe6, 0xb7, 0x3a, 0x11, - 0x91, 0x54, 0xa9, 0xcf, 0x74, 0xdb, 0xda, 0xb3, 0x7c, 0x56, 0xd5, 0x1f, 0xd8, 0xd4, 0xf4, 0x60, - 0xd2, 0x98, 0xe9, 0x9a, 0x2e, 0xff, 0xa9, 0x05, 0xbf, 0xe0, 0xed, 0x84, 0xe9, 0xba, 0xa6, 0xcd, - 0x34, 0xba, 0x6f, 0x69, 0xd4, 0x71, 0x5c, 0x9f, 0x87, 0x07, 0x6c, 0xd4, 0x09, 0xac, 0xdc, 0x0d, - 0x22, 0x78, 0x9f, 0xf9, 0xb4, 0x68, 0x18, 0x6e, 0xcd, 0xf1, 0x2d, 0xc7, 0x2c, 0xb1, 0x47, 0x35, - 0xe6, 0xf9, 0xea, 0x2d, 0xfc, 0x49, 0xea, 0xa8, 0xb7, 0xef, 0x3a, 0x1e, 0x23, 0x05, 0xfc, 0x31, - 0xdd, 0x75, 0xab, 0x3e, 0x2b, 0xeb, 0xc1, 0x3e, 0xe9, 0x74, 0x2f, 0x98, 0x31, 0x8e, 0xce, 0xa3, - 0xd9, 0xd3, 0xa5, 0x51, 0x18, 0xe2, 0xb6, 0x7c, 0xa0, 0xe1, 0x6e, 0x83, 0xf9, 0x77, 0x6a, 0xfe, - 0x4e, 0x7d, 0x27, 0xe4, 0x08, 0xab, 0x91, 0x71, 0xfc, 0x21, 0x67, 0xb8, 0x79, 0x83, 0xbb, 0xc8, - 0x95, 0xa2, 0x47, 0x32, 0x86, 0x07, 0x1c, 0xd7, 0x31, 0xd8, 0x78, 0xff, 0x79, 0x34, 0x7b, 0xaa, - 0x14, 0x3e, 0xa8, 0x35, 0x3c, 0x91, 0xee, 0x0e, 0xe0, 0x7d, 0x1f, 0x0f, 0xbb, 0xc2, 0x7b, 0xee, - 0x74, 0x68, 0x69, 0xbe, 0xd0, 0x36, 0xbb, 0x0a, 0xa2, 0xab, 0xd5, 0x53, 0xaf, 0xfe, 0x3d, 0xd5, - 0x57, 0x8a, 0xb9, 0x51, 0x19, 0xb0, 0x28, 0xda, 0x76, 0x1a, 0x8b, 0x75, 0x8c, 0x9b, 0x59, 0x08, - 0x6b, 0x5e, 0x2a, 0x84, 0x29, 0x5b, 0x08, 0x52, 0xb6, 0x10, 0xa6, 0x3a, 0xa4, 0x6c, 0x61, 0x9b, - 0x9a, 0x0c, 0x6c, 0x4b, 0x82, 0xa5, 0xfa, 0x02, 0x01, 0xbd, 0x96, 0x75, 0x32, 0xe9, 0xe5, 0x7a, - 0x40, 0x8f, 0x6c, 0xc4, 0xf0, 0xf7, 0x73, 0xfc, 0x33, 0x1d, 0xf1, 0x87, 0x98, 0x62, 0x04, 0x9e, - 0x22, 0xac, 0xa6, 0x11, 0x58, 0x3d, 0x58, 0x0b, 0x90, 0x44, 0xf1, 0x1a, 0xc3, 0x03, 0x1c, 0x19, - 0xec, 0x79, 0xf8, 0x90, 0x88, 0x62, 0x7f, 0xd7, 0x51, 0xfc, 0x33, 0xc2, 0xd3, 0x6d, 0x41, 0xbc, - 0x27, 0xc1, 0xfc, 0x31, 0xc2, 0x17, 0x22, 0x1e, 0x9b, 0x4e, 0x56, 0x2c, 0xbf, 0x86, 0x07, 0xc3, - 0xf2, 0x66, 0x95, 0xe3, 0x47, 0xa8, 0xdc, 0xb3, 0x80, 0xfe, 0x41, 0xd8, 0xd5, 0x34, 0x20, 0x10, - 0xcf, 0x12, 0x1e, 0xb2, 0x9c, 0x64, 0x38, 0xe7, 0x3a, 0x84, 0x53, 0xf4, 0x17, 0x46, 0x53, 0x74, - 0xd2, 0xbb, 0x60, 0x0a, 0x27, 0x58, 0x58, 0xd2, 0xeb, 0xf5, 0x09, 0xfe, 0x9d, 0x70, 0x82, 0xe3, - 0xeb, 0xbc, 0x0f, 0x41, 0xba, 0x8e, 0x27, 0xa3, 0xea, 0x1a, 0x2c, 0x79, 0x93, 0x7a, 0x95, 0x1d, - 0x77, 0xcd, 0xf0, 0xeb, 0x51, 0x98, 0x14, 0x3c, 0x68, 0xc1, 0x00, 0x94, 0xfc, 0xc6, 0xb3, 0x7a, - 0x84, 0xf3, 0x59, 0xc6, 0xc0, 0xfd, 0x87, 0xf8, 0xac, 0x15, 0x1b, 0x81, 0x40, 0x2f, 0x48, 0xd0, - 0x6f, 0x1a, 0x41, 0x04, 0x12, 0xae, 0xd4, 0x15, 0x58, 0x3e, 0x3e, 0xf9, 0x06, 0xf5, 0xa9, 0x0c, - 0xf8, 0x27, 0x78, 0x2a, 0xd3, 0x1a, 0xd0, 0xdf, 0xc3, 0x67, 0xd6, 0x02, 0x4c, 0x3c, 0xe9, 0x77, - 0xea, 0x9e, 0x64, 0xbd, 0x10, 0x6d, 0x00, 0x7a, 0xdc, 0x8f, 0x6a, 0x42, 0xd4, 0x21, 0x65, 0x5a, - 0xa3, 0xde, 0xab, 0xe4, 0x7c, 0x89, 0x20, 0x46, 0x29, 0x2b, 0xb5, 0xd9, 0xa2, 0x5c, 0x8f, 0xb6, - 0xa8, 0x77, 0x79, 0xaa, 0xe1, 0xaf, 0x46, 0xa9, 0xb6, 0x41, 0xbd, 0xed, 0xa0, 0x7d, 0x13, 0x3e, - 0x2d, 0x96, 0x53, 0x66, 0x75, 0xd8, 0xe1, 0xf0, 0x41, 0xd5, 0xf1, 0x78, 0xab, 0x01, 0x50, 0x5e, - 0xc3, 0x83, 0xd1, 0x3b, 0x88, 0xed, 0x4c, 0x07, 0xb2, 0x0d, 0x17, 0x0d, 0x43, 0x95, 0x02, 0xa2, - 0xa2, 0x6d, 0x27, 0x11, 0xf5, 0x6a, 0xf7, 0x9e, 0x23, 0x20, 0x11, 0x5b, 0x23, 0x95, 0x44, 0xae, - 0x2b, 0x12, 0xbd, 0xdb, 0x9f, 0xe5, 0x66, 0x29, 0xd8, 0xa2, 0x9e, 0xbf, 0x1a, 0x74, 0xbf, 0x37, - 0x79, 0xf3, 0xdb, 0x7e, 0x9b, 0x0e, 0xe1, 0x14, 0xa6, 0xd9, 0x01, 0xd1, 0x1f, 0xe0, 0x91, 0xc4, - 0x10, 0x84, 0xb4, 0xd0, 0x81, 0x6f, 0xd2, 0x61, 0xd2, 0x8d, 0x5a, 0x69, 0x1e, 0x8e, 0x0c, 0xd0, - 0xbd, 0xda, 0xc9, 0x3f, 0x21, 0xe0, 0x99, 0xb6, 0x54, 0x3b, 0x9e, 0xb9, 0x1e, 0xf0, 0xec, 0xdd, - 0x2e, 0xcf, 0xe3, 0x8f, 0xa3, 0xdd, 0x12, 0xab, 0x55, 0xfa, 0xd6, 0x6e, 0xc1, 0xa5, 0x03, 0x26, - 0xaf, 0x1e, 0xdc, 0x0e, 0xfa, 0xf9, 0x6e, 0xaf, 0x01, 0x26, 0x1e, 0x8b, 0x2f, 0x0d, 0x51, 0xbb, - 0x83, 0x87, 0xc5, 0xda, 0x2a, 0xd9, 0xfe, 0x8b, 0x26, 0xa5, 0x98, 0x03, 0xf5, 0x47, 0xc0, 0xb1, - 0x68, 0xdb, 0xef, 0xa2, 0x22, 0xff, 0x0a, 0x01, 0x91, 0x86, 0xff, 0x4c, 0x22, 0xb9, 0x13, 0x11, - 0xe9, 0xdd, 0xae, 0xdf, 0x86, 0x46, 0x6a, 0xcb, 0xf2, 0xfc, 0x6d, 0xe6, 0x94, 0x2d, 0xc7, 0x14, - 0x23, 0xd3, 0xa6, 0x1d, 0x1d, 0xc3, 0x03, 0xfc, 0x0a, 0xcb, 0x57, 0x3f, 0x53, 0x0a, 0x1f, 0xd4, - 0x67, 0x51, 0xc7, 0xd4, 0xe2, 0xf0, 0x5d, 0x85, 0x42, 0xc5, 0xc3, 0xbe, 0xeb, 0x53, 0x1b, 0x16, - 0x83, 0xcc, 0x8a, 0xbd, 0x53, 0x57, 0xf1, 0x5c, 0x1a, 0xa8, 0x7b, 0x96, 0x5f, 0xb1, 0x9c, 0x12, - 0xf5, 0xd9, 0x56, 0x00, 0x5e, 0x48, 0xf9, 0x90, 0x19, 0x12, 0x99, 0xfd, 0x0d, 0xe1, 0x79, 0x29, - 0x27, 0x40, 0xf4, 0x2e, 0x3e, 0x1b, 0x97, 0x2b, 0xba, 0xa2, 0x6a, 0x88, 0x54, 0xa7, 0xf1, 0x19, - 0x4e, 0x4b, 0xdf, 0xcf, 0xe6, 0x1a, 0x5c, 0xe9, 0x9b, 0xfa, 0x82, 0xce, 0xea, 0x06, 0x63, 0x65, - 0x56, 0x1e, 0xcf, 0x9d, 0x47, 0xb3, 0x83, 0xa5, 0xd1, 0x6a, 0x84, 0xf3, 0x4b, 0x18, 0x68, 0xe8, - 0x07, 0x41, 0x61, 0x09, 0x6e, 0xfa, 0xb1, 0x22, 0xa9, 0x5e, 0x89, 0xf2, 0x23, 0x31, 0x0a, 0x24, - 0xcf, 0xe1, 0x0f, 0x84, 0xb2, 0x9d, 0x2b, 0xc1, 0x93, 0xba, 0x03, 0x59, 0xb0, 0xe6, 0x3a, 0x8f, - 0x59, 0x35, 0xf8, 0x4a, 0xef, 0xb8, 0x81, 0x79, 0x4b, 0x85, 0x68, 0x49, 0x2b, 0x05, 0x0f, 0x9a, - 0xd4, 0xdb, 0x6a, 0x64, 0xd6, 0xe9, 0x52, 0xe3, 0x59, 0xfd, 0x39, 0x82, 0xde, 0xaa, 0xd5, 0x2d, - 0xe0, 0xf9, 0x26, 0x1e, 0x75, 0x6b, 0xfe, 0xae, 0x5b, 0x73, 0xca, 0x1b, 0xd4, 0xdb, 0x74, 0x82, - 0xc1, 0x48, 0xcd, 0x68, 0x19, 0x08, 0x66, 0x73, 0x0d, 0xc5, 0x70, 0xed, 0x75, 0xc6, 0x60, 0x76, - 0xb8, 0x68, 0xeb, 0x00, 0x99, 0xc5, 0x23, 0xc1, 0xbf, 0x62, 0x0d, 0xcf, 0xf1, 0xf8, 0x27, 0x5f, - 0xab, 0x33, 0xf8, 0x22, 0x87, 0x79, 0x8b, 0x79, 0x1e, 0x35, 0xd9, 0x36, 0xf5, 0x3c, 0xcb, 0x31, - 0xb7, 0x9b, 0x1e, 0xa3, 0xe8, 0xae, 0xe3, 0x4b, 0x9d, 0x26, 0x02, 0xb1, 0x09, 0x7c, 0xfa, 0x41, - 0x03, 0x62, 0x48, 0xa8, 0xf9, 0x42, 0xcd, 0x43, 0xb8, 0x1b, 0x59, 0xc8, 0xaa, 0xeb, 0x36, 0x35, - 0xa3, 0xfb, 0x50, 0x70, 0x91, 0x9f, 0xcc, 0x98, 0x00, 0xfe, 0x29, 0xfe, 0xa8, 0x9a, 0x18, 0x83, - 0x42, 0xa8, 0x75, 0xc8, 0xd7, 0xa4, 0x4b, 0xe8, 0x16, 0x5b, 0xdc, 0x2d, 0x3d, 0xbf, 0x80, 0x07, - 0x38, 0x08, 0xf2, 0x12, 0xe1, 0x61, 0xf1, 0xe2, 0x4d, 0xae, 0x75, 0x58, 0xa3, 0x8d, 0xe6, 0xa4, - 0x5c, 0xef, 0xca, 0x36, 0xa4, 0xad, 0x7e, 0xf1, 0xf4, 0xaf, 0xff, 0x7d, 0xd6, 0xff, 0x39, 0xb9, - 0xa2, 0x05, 0xa6, 0x0b, 0x82, 0xca, 0xd8, 0x90, 0xf2, 0x1a, 0x46, 0xda, 0x21, 0x7c, 0xc5, 0x8e, - 0xb4, 0x43, 0xfe, 0xdd, 0x3a, 0x22, 0xbf, 0x45, 0x78, 0x44, 0xf4, 0x5b, 0xb4, 0x6d, 0x39, 0x2e, - 0xe9, 0xca, 0x93, 0x1c, 0x97, 0x0c, 0x35, 0x49, 0x9d, 0xe7, 0x5c, 0x2e, 0x92, 0x69, 0x09, 0x2e, - 0xe4, 0x5f, 0x08, 0x9f, 0x4b, 0x20, 0x07, 0x01, 0x80, 0x14, 0xbb, 0x00, 0x11, 0x57, 0x31, 0x94, - 0xd5, 0x93, 0xb8, 0x00, 0x3a, 0xd7, 0x38, 0x9d, 0xcf, 0xc8, 0x92, 0x04, 0x1d, 0xb0, 0x85, 0x1d, - 0x3a, 0x22, 0xff, 0x44, 0xf8, 0x2b, 0xc2, 0x2d, 0x5b, 0x20, 0xf7, 0x1d, 0x49, 0x64, 0x99, 0x0a, - 0x8d, 0x52, 0x3c, 0x81, 0x07, 0xa0, 0xb6, 0xc2, 0xa9, 0x2d, 0x93, 0xcf, 0x32, 0xa8, 0x59, 0x4e, - 0x06, 0x33, 0xdd, 0x2a, 0x1f, 0x91, 0xdf, 0x20, 0x7c, 0x36, 0x4e, 0x4e, 0x3a, 0xe7, 0x52, 0xb4, - 0x12, 0xe9, 0x9c, 0x4b, 0xd3, 0x3f, 0x3a, 0xe6, 0x9c, 0xc0, 0xc4, 0x23, 0x7f, 0x01, 0xe0, 0xc2, - 0x1d, 0x72, 0x45, 0xf2, 0xf0, 0xa6, 0xde, 0xa4, 0x95, 0x2f, 0xba, 0xb4, 0x06, 0xf0, 0xdf, 0xe2, - 0xe0, 0x97, 0xc8, 0xa7, 0x6d, 0xc0, 0x37, 0xcd, 0xb4, 0xc3, 0xe8, 0xf9, 0x88, 0xfc, 0x1d, 0x61, - 0xd2, 0xaa, 0x2d, 0x10, 0x29, 0x3c, 0x99, 0x8a, 0x86, 0xf2, 0xed, 0x6e, 0xcd, 0x81, 0x4f, 0x91, - 0xf3, 0xb9, 0x4e, 0xae, 0x66, 0xf2, 0x49, 0xfe, 0x07, 0x88, 0x5e, 0xa6, 0x3e, 0x15, 0x89, 0xfd, - 0x1e, 0xe1, 0xd1, 0xf8, 0x0a, 0x41, 0x7a, 0xad, 0x1c, 0x23, 0x45, 0xba, 0xdc, 0xa5, 0x4c, 0x0d, - 0x43, 0x5d, 0xe0, 0xac, 0x66, 0xc8, 0x45, 0xa9, 0x5d, 0x22, 0xbf, 0x44, 0xcd, 0xbb, 0x33, 0x59, - 0x96, 0x4c, 0x90, 0xc4, 0x25, 0x5f, 0xf9, 0xfc, 0xd8, 0x76, 0x00, 0x56, 0xe3, 0x60, 0xbf, 0x41, - 0x66, 0x32, 0xc0, 0x9a, 0x60, 0x10, 0xc4, 0xbc, 0xcc, 0xea, 0x47, 0xe4, 0x17, 0x08, 0x0f, 0x45, - 0x5e, 0x82, 0x50, 0x2f, 0x4b, 0x06, 0xab, 0x2b, 0xc4, 0x29, 0x52, 0x83, 0x3a, 0xc3, 0x11, 0x5f, - 0x20, 0x53, 0x1d, 0x10, 0x93, 0x17, 0x08, 0x7f, 0x94, 0xec, 0xbb, 0x88, 0x54, 0xf1, 0xc8, 0x68, - 0x02, 0x95, 0x95, 0xee, 0x8c, 0x25, 0x43, 0x6d, 0x24, 0xb1, 0xbe, 0x44, 0x78, 0x48, 0x68, 0xad, - 0xc8, 0x0d, 0x99, 0xe5, 0x3b, 0xb5, 0x70, 0xca, 0x97, 0x27, 0xf4, 0x02, 0x6c, 0xe6, 0x38, 0x9b, - 0xaf, 0x13, 0x35, 0x83, 0x8d, 0xd0, 0x8e, 0x92, 0x57, 0xa8, 0x45, 0x4d, 0x20, 0xb2, 0xa5, 0x30, - 0x5d, 0x0b, 0x91, 0x2b, 0x3d, 0xd9, 0x3a, 0x8e, 0xba, 0xcc, 0xe1, 0x7f, 0x4a, 0x0a, 0x19, 0xf0, - 0xed, 0xb8, 0x5d, 0x23, 0xfd, 0xff, 0x88, 0x30, 0x49, 0xf8, 0x0c, 0x4e, 0x81, 0x6c, 0xc9, 0x38, - 0x09, 0x9b, 0x6c, 0xb5, 0x46, 0x2d, 0x70, 0x36, 0xb3, 0xe4, 0x92, 0x1c, 0x1b, 0xf2, 0x33, 0x84, - 0x4f, 0xf1, 0xe2, 0xb3, 0x24, 0x19, 0x46, 0xb1, 0x3c, 0x5e, 0x3e, 0x96, 0x8d, 0xe4, 0x77, 0xd7, - 0x80, 0x0f, 0x16, 0x0f, 0xf2, 0xaf, 0x11, 0x1e, 0x12, 0x54, 0x1a, 0x72, 0xf5, 0x18, 0x2b, 0xc6, - 0x95, 0x9d, 0xee, 0xc0, 0x5e, 0xe1, 0x60, 0x35, 0xb2, 0xd0, 0x16, 0x6c, 0x4b, 0x73, 0xfd, 0x53, - 0x84, 0x3f, 0x8c, 0xbe, 0x40, 0x4b, 0x92, 0x3b, 0x7a, 0xec, 0xc0, 0x26, 0x94, 0x1a, 0x75, 0x9a, - 0x63, 0x9d, 0x24, 0x9f, 0xb4, 0xc1, 0x1a, 0x74, 0x60, 0x23, 0x09, 0x15, 0x40, 0xae, 0x05, 0x4b, - 0x57, 0x59, 0xe4, 0x5a, 0xb0, 0x0c, 0x41, 0xa5, 0x73, 0xe5, 0x10, 0x40, 0xfe, 0x0f, 0xe1, 0x7c, - 0x7b, 0xf9, 0x82, 0x6c, 0x76, 0x81, 0x25, 0x5d, 0x47, 0x51, 0xbe, 0xdb, 0x0b, 0x57, 0xc0, 0xf2, - 0x2a, 0x67, 0x79, 0x99, 0x2c, 0x76, 0x66, 0x99, 0x64, 0x14, 0xf4, 0xcb, 0xf1, 0x3f, 0x7f, 0x90, - 0x3b, 0x01, 0xa9, 0x7f, 0x50, 0xa1, 0x5c, 0xeb, 0xc6, 0x54, 0xb2, 0x95, 0x79, 0x12, 0x47, 0x19, - 0x00, 0x8f, 0xeb, 0x2e, 0x72, 0xc0, 0x53, 0x95, 0x1c, 0x39, 0xe0, 0xe9, 0x32, 0x4f, 0x47, 0xe0, - 0x76, 0x1c, 0x65, 0xd0, 0x2a, 0x24, 0x65, 0x01, 0xb9, 0x56, 0x21, 0x43, 0xc0, 0x90, 0x6b, 0x15, - 0xb2, 0xc4, 0x8d, 0x8e, 0xad, 0x42, 0x52, 0xaa, 0x58, 0xfd, 0xde, 0xab, 0x37, 0x79, 0xf4, 0xfa, - 0x4d, 0x1e, 0xfd, 0xe7, 0x4d, 0x1e, 0xfd, 0xe4, 0x6d, 0xbe, 0xef, 0xf5, 0xdb, 0x7c, 0xdf, 0x3f, - 0xde, 0xe6, 0xfb, 0xee, 0x2f, 0x9a, 0x96, 0x5f, 0xa9, 0xed, 0x16, 0x0c, 0x77, 0x4f, 0x74, 0x16, - 0x61, 0xd2, 0xea, 0xa2, 0x5f, 0xff, 0x60, 0x9f, 0x79, 0xbb, 0x1f, 0xf0, 0x6f, 0xf7, 0xe5, 0xff, - 0x07, 0x00, 0x00, 0xff, 0xff, 0x25, 0x12, 0xcf, 0x1a, 0x2c, 0x25, 0x00, 0x00, + // 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, } // Reference imports to suppress errors if they are not otherwise used. @@ -4382,6 +4392,11 @@ func (m *QueryListPendingCctxWithinRateLimitResponse) MarshalToSizedBuffer(dAtA dAtA[i] = 0 } i-- + dAtA[i] = 0x20 + } + if m.ValueWithinWindow != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ValueWithinWindow)) + i-- dAtA[i] = 0x18 } if m.TotalPending != 0 { @@ -5169,6 +5184,9 @@ 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.RateLimitExceeded { n += 2 } @@ -8535,6 +8553,25 @@ func (m *QueryListPendingCctxWithinRateLimitResponse) Unmarshal(dAtA []byte) err } } case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValueWithinWindow", wireType) + } + m.ValueWithinWindow = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValueWithinWindow |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: 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 0fbaba26b3..a122a19131 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, bool, error) + ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, uint64, 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 e66c309cc6..ca3f45a16b 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, bool, error) { +func (z *MockZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*cctxtypes.CrossChainTx, uint64, uint64, bool, error) { if z.paused { - return nil, 0, false, errors.New(ErrMsgPaused) + return nil, 0, 0, false, errors.New(ErrMsgPaused) } - return []*cctxtypes.CrossChainTx{}, 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 09d3b20e82..afed840fef 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, bool, error) { +func (b *ZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*types.CrossChainTx, uint64, uint64, 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, false, err + return nil, 0, 0, false, err } - return resp.CrossChainTx, resp.TotalPending, resp.RateLimitExceeded, nil + return resp.CrossChainTx, resp.TotalPending, resp.ValueWithinWindow, resp.RateLimitExceeded, nil } func (b *ZetaCoreBridge) GetAbortedZetaAmount() (string, error) { diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index c66f15f50a..230e1ba894 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -134,10 +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, err := co.getAllPendingCctxWithRatelimit() + cctxMap, valueWithinWindow, 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) + } // schedule keysign for pending cctxs on each chain coreContext := appContext.ZetaCoreContext() @@ -190,10 +194,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, error) { - cctxList, totalPending, rateLimitExceeded, err := co.bridge.ListPendingCctxWithinRatelimit() +func (co *CoreObserver) getAllPendingCctxWithRatelimit() (map[int64][]*types.CrossChainTx, uint64, error) { + cctxList, totalPending, valueWithinWindow, rateLimitExceeded, err := co.bridge.ListPendingCctxWithinRatelimit() if err != nil { - return nil, err + return nil, 0, err } if rateLimitExceeded { co.logger.ZetaChainWatcher.Warn().Msgf("rate limit exceeded, fetched %d cctxs out of %d", len(cctxList), totalPending) @@ -209,7 +213,7 @@ func (co *CoreObserver) getAllPendingCctxWithRatelimit() (map[int64][]*types.Cro cctxMap[chainID] = append(cctxMap[chainID], cctx) } - return cctxMap, nil + return cctxMap, valueWithinWindow, nil } // scheduleCctxEVM schedules evm outtx keysign on each ZetaChain block (the ticker) From 7fe367fcc998e2c6124c153f434b91c8003b74aa Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Mon, 22 Apr 2024 09:52:18 -0500 Subject: [PATCH 17/40] Update x/crosschain/keeper/grpc_query_cctx_rate_limit.go Co-authored-by: Lucas Bertrand --- x/crosschain/keeper/grpc_query_cctx_rate_limit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 287ebe1147..d4650e1707 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -234,12 +234,12 @@ func ConvertCctxValue( } // get foreign coin decimals - _, found := foreignCoinMap[chainID] + foreignCoinFromChainMap, found := foreignCoinMap[chainID] if !found { // skip if no coin found for this chainID return sdk.NewDec(0) } - fCoin, found := foreignCoinMap[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] + fCoin, found := foreignCoinFromChainMap[strings.ToLower(cctx.InboundTxParams.Asset)] if !found { // skip if no coin found for this Asset return sdk.NewDec(0) From e5e4e28a8d27e5b48c808d978ed11f25b98d2ea0 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 22 Apr 2024 09:55:02 -0500 Subject: [PATCH 18/40] change variable name fCoin to foreignCoin --- x/crosschain/keeper/grpc_query_cctx_rate_limit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index d4650e1707..b9e30a34c8 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -239,12 +239,12 @@ func ConvertCctxValue( // skip if no coin found for this chainID return sdk.NewDec(0) } - fCoin, found := foreignCoinFromChainMap[strings.ToLower(cctx.InboundTxParams.Asset)] + foreignCoin, found := foreignCoinFromChainMap[strings.ToLower(cctx.InboundTxParams.Asset)] if !found { // skip if no coin found for this Asset return sdk.NewDec(0) } - decimals = uint64(fCoin.Decimals) + decimals = uint64(foreignCoin.Decimals) // given decimals = 6, the `oneZrc20` amount will be 10^6 = 1000000 oneZrc20 := sdk.NewDec(10).Power(decimals) From 5e28dafd2cbe5184b3bdf823240d9b1ad4009e4c Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Mon, 22 Apr 2024 09:55:56 -0500 Subject: [PATCH 19/40] Update x/fungible/keeper/foreign_coins.go Co-authored-by: Lucas Bertrand --- x/fungible/keeper/foreign_coins.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/fungible/keeper/foreign_coins.go b/x/fungible/keeper/foreign_coins.go index 2f3eaf7918..c695dcee1b 100644 --- a/x/fungible/keeper/foreign_coins.go +++ b/x/fungible/keeper/foreign_coins.go @@ -85,7 +85,7 @@ func (k Keeper) GetAllForeignCoins(ctx sdk.Context) (list []types.ForeignCoins) func (k Keeper) GetAllForeignCoinMap(ctx sdk.Context) map[int64]map[string]types.ForeignCoins { allForeignCoins := k.GetAllForeignCoins(ctx) - fCoinMap := make(map[int64]map[string]types.ForeignCoins) + foreignCoinMap := make(map[int64]map[string]types.ForeignCoins) for _, c := range allForeignCoins { if _, found := fCoinMap[c.ForeignChainId]; !found { fCoinMap[c.ForeignChainId] = make(map[string]types.ForeignCoins) From da27a8488d99c6020bc8a33be1f4353289827a89 Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Mon, 22 Apr 2024 09:56:08 -0500 Subject: [PATCH 20/40] Update x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go Co-authored-by: Lucas Bertrand --- x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8b53958ad3..0e0364dea7 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -366,7 +366,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { zk.ObserverKeeper.SetTSS(ctx, tss) // Set rate limiter flags as disabled - rFlags := sample.RateLimiterFlags() + rateLimiterFlags := sample.RateLimiterFlags() rFlags.Enabled = false k.SetRateLimiterFlags(ctx, rFlags) From f9eed1570eccc447230fe15c25ec99d41f1946fb Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 22 Apr 2024 17:19:45 -0500 Subject: [PATCH 21/40] converted rate limiter query unit tests to table test --- .../keeper/grpc_query_cctx_rate_limit.go | 2 +- .../keeper/grpc_query_cctx_rate_limit_test.go | 498 +++++++++++------- x/fungible/keeper/foreign_coins.go | 8 +- 3 files changed, 308 insertions(+), 200 deletions(-) diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index b9e30a34c8..7a05e9341d 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -69,7 +69,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que } // calculate the rate limiter sliding window left boundary (inclusive) - leftWindowBoundary := height - rateLimitFlags.Window + leftWindowBoundary := height - rateLimitFlags.Window + 1 if leftWindowBoundary < 0 { leftWindowBoundary = 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 0e0364dea7..cd69e663d4 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -16,19 +16,29 @@ import ( observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) +var ( + // local eth chain ID + ethChainID = getValidEthChainID() + + // local btc chain ID + btcChainID = getValidBtcChainID() +) + // createTestRateLimiterFlags creates a custom rate limiter flags func createTestRateLimiterFlags( + window int64, + rate math.Uint, zrc20ETH string, zrc20BTC string, zrc20USDT string, ethRate string, btcRate string, usdtRate string, -) types.RateLimiterFlags { - var rateLimiterFlags = types.RateLimiterFlags{ +) *types.RateLimiterFlags { + return &types.RateLimiterFlags{ Enabled: true, - Window: 500, // 500 zeta blocks, 50 minutes - Rate: math.NewUint(5000), // 5000 ZETA + Window: window, // for instance: 500 zeta blocks, 50 minutes + Rate: rate, Conversions: []types.Conversion{ // ETH { @@ -47,7 +57,6 @@ func createTestRateLimiterFlags( }, }, } - return rateLimiterFlags } // createCctxsWithCoinTypeAndHeightRange @@ -56,10 +65,6 @@ func createTestRateLimiterFlags( // return created cctxs func createCctxsWithCoinTypeAndHeightRange( t *testing.T, - ctx sdk.Context, - k keeper.Keeper, - zk keepertest.ZetaKeepers, - tss observertypes.TSS, lowBlock uint64, highBlock uint64, chainID int64, @@ -80,34 +85,29 @@ func createCctxsWithCoinTypeAndHeightRange( cctx.GetCurrentOutTxParam().ReceiverChainId = chainID cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(amount) cctx.GetCurrentOutTxParam().OutboundTxTssNonce = nonce - k.SetCrossChainTx(ctx, *cctx) - zk.ObserverKeeper.SetNonceToCctx(ctx, observertypes.NonceToCctx{ - ChainId: chainID, - // #nosec G701 always in range for tests - Nonce: int64(nonce), - CctxIndex: cctx.Index, - Tss: tss.TssPubkey, - }) cctxs = append(cctxs, cctx) } return cctxs } -// setPendingNonces sets the pending nonces for the given chainID -func setPendingNonces( +// setCctxsInKeeper sets the given cctxs to the keeper +func setCctxsInKeeper( ctx sdk.Context, + k keeper.Keeper, zk keepertest.ZetaKeepers, - chainID int64, - nonceLow int64, - nonceHigh int64, - tssPubKey string, + tss observertypes.TSS, + cctxs []*types.CrossChainTx, ) { - zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ - ChainId: chainID, - NonceLow: nonceLow, - NonceHigh: nonceHigh, - Tss: tssPubKey, - }) + for _, cctx := range cctxs { + k.SetCrossChainTx(ctx, *cctx) + zk.ObserverKeeper.SetNonceToCctx(ctx, observertypes.NonceToCctx{ + ChainId: cctx.InboundTxParams.SenderChainId, + // #nosec G701 always in range for tests + Nonce: int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce), + CctxIndex: cctx.Index, + Tss: tss.TssPubkey, + }) + } } // setupForeignCoins adds ETH, BTC, USDT to the foreign coins store @@ -124,59 +124,6 @@ func setupForeignCoins( } } -// createKeeperForRateLimiterTest creates a keeper filled with cctxs for rate limiter test -func createKeeperForRateLimiterTest(t *testing.T) (k *keeper.Keeper, ctx sdk.Context, cctxsETH, cctxsBTC []*types.CrossChainTx, rateLimiterFlags types.RateLimiterFlags) { - // chain IDs - ethChainID := getValidEthChainID() - btcChainID := getValidBtcChainID() - - // zrc20 addresses for ETH, BTC, USDT and asset for USDT - zrc20ETH := sample.EthAddress().Hex() - zrc20BTC := sample.EthAddress().Hex() - zrc20USDT := sample.EthAddress().Hex() - assetUSDT := sample.EthAddress().Hex() - - // create test rate limiter flags - rateLimiterFlags = createTestRateLimiterFlags(zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8") - - // define cctx status - statusPending := types.CctxStatus_PendingOutbound - statusMined := types.CctxStatus_OutboundMined - - // create test keepers - k, ctx, _, zk := keepertest.CrosschainKeeper(t) - - // Set TSS - tss := sample.Tss() - zk.ObserverKeeper.SetTSS(ctx, tss) - - // Set foreign coins - setupForeignCoins(t, ctx, zk, zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT) - - // Set rate limiter flags - k.SetRateLimiterFlags(ctx, rateLimiterFlags) - - // Create cctxs [0~999] and [1000~1199] for Eth chain, 0.001 ETH (2.5 ZETA) per cctx - createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1, 1000, ethChainID, coin.CoinType_Gas, "", uint64(1e15), statusMined) - cctxsETH = createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1001, 1200, ethChainID, coin.CoinType_Gas, "", uint64(1e15), statusPending) - - // Set Eth chain pending nonces, [1000~1099] are missed cctxs - setPendingNonces(ctx, zk, ethChainID, 1100, 1200, tss.TssPubkey) - - // Create cctxs [0~999] and [1000~1199] for Btc chain, 0.00001 BTC (0.5 ZETA) per cctx - createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1, 1000, btcChainID, coin.CoinType_Gas, "", 1000, statusMined) - cctxsBTC = createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1001, 1200, btcChainID, coin.CoinType_Gas, "", 1000, statusPending) - require.NotNil(t, cctxsBTC) - - // Set Btc chain pending nonces, [1000~1099] are missed cctxs - setPendingNonces(ctx, zk, btcChainID, 1100, 1200, tss.TssPubkey) - - // Set current block height to 1201, the window is now [701, 1201], the nonces [700~1200] fall into the window - ctx = ctx.WithBlockHeight(1201) - - return k, ctx, cctxsETH, cctxsBTC, rateLimiterFlags -} - func Test_ConvertCctxValue(t *testing.T) { // chain IDs ethChainID := getValidEthChainID() @@ -198,8 +145,8 @@ func Test_ConvertCctxValue(t *testing.T) { setupForeignCoins(t, ctx, zk, zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT) // Set rate limiter flags - rateLimiterFlags := createTestRateLimiterFlags(zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8") - k.SetRateLimiterFlags(ctx, rateLimiterFlags) + rateLimiterFlags := createTestRateLimiterFlags(500, math.NewUint(5000), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8") + k.SetRateLimiterFlags(ctx, *rateLimiterFlags) // get rate limiter rates gasCoinRates, erc20CoinRates := k.GetRateLimiterRates(ctx) @@ -318,6 +265,278 @@ func Test_ConvertCctxValue(t *testing.T) { } func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { + // create sample TSS + tss := sample.Tss() + + // create sample zrc20 addresses for ETH, BTC, USDT + zrc20ETH := sample.EthAddress().Hex() + 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) + 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 + 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) + + // define test cases + tests := []struct { + name string + rateLimitFlags *types.RateLimiterFlags + + // Eth chain cctxs setup + ethMindedCctxs []*types.CrossChainTx + ethPendingCctxs []*types.CrossChainTx + ethPendingNonces observertypes.PendingNonces + + // Btc chain cctxs setup + btcMinedCctxs []*types.CrossChainTx + btcPendingCctxs []*types.CrossChainTx + btcPendingNonces observertypes.PendingNonces + + // current block height and limit + currentHeight int64 + queryLimit uint32 + + // expected results + expectedCctxs []*types.CrossChainTx + expectedTotalPending uint64 + expectedTotalValue uint64 + rateLimitExceeded bool + }{ + { + name: "should use fallback query if rate limiter is disabled", + rateLimitFlags: nil, // no rate limiter flags set in the keeper + ethMindedCctxs: ethMindedCctxs, + 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"), + ethMindedCctxs: ethMindedCctxs, + 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"), + ethMindedCctxs: ethMindedCctxs, + 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 + ethMindedCctxs: ethMindedCctxs, + 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, + }, + { + name: "set high rate and big window to early to break inner loop with the criteria #1", + // 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, + 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"), + ethMindedCctxs: ethMindedCctxs, + 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, + }, + { + name: "set rate to middle value (1200 < rate < 1500) to early break the LoopForwards loop with criteria #2", + rateLimitFlags: createTestRateLimiterFlags(500, math.NewUint(1300), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8"), // 1200 < 1300 < 1500 + ethMindedCctxs: ethMindedCctxs, + 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, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create test keepers + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // Set TSS + zk.ObserverKeeper.SetTSS(ctx, tss) + + // Set foreign coins + assetUSDT := sample.EthAddress().Hex() + setupForeignCoins(t, ctx, zk, zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT) + + // Set rate limiter flags + if tt.rateLimitFlags != nil { + k.SetRateLimiterFlags(ctx, *tt.rateLimitFlags) + } + + // Set Eth chain mined cctxs, pending ccxts and pending nonces + setCctxsInKeeper(ctx, *k, zk, tss, tt.ethMindedCctxs) + setCctxsInKeeper(ctx, *k, zk, tss, tt.ethPendingCctxs) + zk.ObserverKeeper.SetPendingNonces(ctx, tt.ethPendingNonces) + + // Set Btc chain mined cctxs, pending ccxts and pending nonces + setCctxsInKeeper(ctx, *k, zk, tss, tt.btcMinedCctxs) + setCctxsInKeeper(ctx, *k, zk, tss, tt.btcPendingCctxs) + zk.ObserverKeeper.SetPendingNonces(ctx, tt.btcPendingNonces) + + // Set current block height + ctx = ctx.WithBlockHeight(tt.currentHeight) + + // Query pending cctxs + 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) + + // 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()) + } + } + }) + } +} + +func TestKeeper_ListPendingCctxWithinRateLimit_Errors(t *testing.T) { t.Run("should fail for empty req", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) _, err := k.ListPendingCctxWithinRateLimit(ctx, nil) @@ -358,115 +577,4 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { _, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) require.ErrorContains(t, err, "pending nonces not found") }) - t.Run("should use fallback query", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) - - // Set TSS - tss := sample.Tss() - zk.ObserverKeeper.SetTSS(ctx, tss) - - // Set rate limiter flags as disabled - rateLimiterFlags := sample.RateLimiterFlags() - rFlags.Enabled = false - k.SetRateLimiterFlags(ctx, rFlags) - - // Create cctxs [0~999] and [1000~1199] for Eth chain, 0.001 ETH per cctx - ethChainID := getValidEthChainID() - _ = createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1, 1000, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_OutboundMined) - cctxsETH := createCctxsWithCoinTypeAndHeightRange(t, ctx, *k, zk, tss, 1001, 1200, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_PendingOutbound) - - // Set Eth chain pending nonces which contains 100 missing cctxs - setPendingNonces(ctx, zk, ethChainID, 1100, 1200, tss.TssPubkey) - - // Query pending cctxs use small limit - res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{Limit: 100}) - require.NoError(t, err) - require.Equal(t, 100, len(res.CrossChainTx)) - - // check response - require.EqualValues(t, cctxsETH[:100], res.CrossChainTx) - require.EqualValues(t, uint64(200), res.TotalPending) - - // Query pending cctxs use max limit - res, err = k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{Limit: keeper.MaxPendingCctxs}) - require.NoError(t, err) - require.Equal(t, 200, len(res.CrossChainTx)) - - // check response - require.EqualValues(t, cctxsETH, res.CrossChainTx) - require.EqualValues(t, uint64(200), res.TotalPending) - }) - t.Run("can retrieve pending cctx in range without exceeding rate limit", func(t *testing.T) { - k, ctx, cctxsETH, cctxsBTC, _ := createKeeperForRateLimiterTest(t) - - res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) - require.NoError(t, err) - require.Equal(t, 400, len(res.CrossChainTx)) - require.EqualValues(t, cctxsETH, res.CrossChainTx[0:200]) - require.EqualValues(t, cctxsBTC, res.CrossChainTx[200:400]) - require.EqualValues(t, uint64(400), res.TotalPending) - require.False(t, res.RateLimitExceeded) - require.Equal(t, uint64(1500), res.ValueWithinWindow) // 500 * (2.5 + 0.5) - }) - t.Run("Set rate to a lower value (< 1200) to early break the LoopBackwards with criteria #2", func(t *testing.T) { - k, ctx, cctxsETH, cctxsBTC, rlFlags := createKeeperForRateLimiterTest(t) - - rate := uint64(1000) // 1000 ZETA - rlFlags.Rate = math.NewUint(rate) - k.SetRateLimiterFlags(ctx, rlFlags) - - res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) - require.NoError(t, err) - require.Equal(t, 200, len(res.CrossChainTx)) - require.EqualValues(t, cctxsETH[:100], res.CrossChainTx[0:100]) - require.EqualValues(t, cctxsBTC[:100], res.CrossChainTx[100:200]) - require.EqualValues(t, uint64(400), res.TotalPending) - require.True(t, res.RateLimitExceeded) - require.True(t, res.ValueWithinWindow >= rate) - }) - t.Run("Set high rate and big window to early to break inner loop with the criteria #1", func(t *testing.T) { - k, ctx, cctxsETH, cctxsBTC, rlFlags := createKeeperForRateLimiterTest(t) - - // The left boundary will be 51 (1201-1150), less than the endNonce 100 (1100 - 10000) - rlFlags.Rate = math.NewUint(10000) - rlFlags.Window = 1150 - k.SetRateLimiterFlags(ctx, rlFlags) - - res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) - require.NoError(t, err) - require.Equal(t, 400, len(res.CrossChainTx)) - require.EqualValues(t, cctxsETH, res.CrossChainTx[0:200]) - require.EqualValues(t, cctxsBTC, res.CrossChainTx[200:400]) - require.EqualValues(t, uint64(400), res.TotalPending) - require.False(t, res.RateLimitExceeded) - require.EqualValues(t, uint64(3450), res.ValueWithinWindow) // 1150 * (2.5 + 0.5) - }) - t.Run("Set lower request limit to early break the LoopForwards loop", func(t *testing.T) { - k, ctx, cctxsETH, cctxsBTC, _ := createKeeperForRateLimiterTest(t) - - res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{Limit: 300}) - require.NoError(t, err) - require.Equal(t, 300, len(res.CrossChainTx)) - require.EqualValues(t, cctxsETH[:100], res.CrossChainTx[0:100]) - require.EqualValues(t, cctxsBTC, res.CrossChainTx[100:300]) - require.EqualValues(t, uint64(400), res.TotalPending) - require.False(t, res.RateLimitExceeded) - require.EqualValues(t, uint64(1250), res.ValueWithinWindow) // 500 * 0.5 + 400 * 2.5 - }) - t.Run("Set rate to middle value (1200 < rate < 1500) to early break the LoopForwards loop with criteria #2", func(t *testing.T) { - k, ctx, cctxsETH, cctxsBTC, rlFlags := createKeeperForRateLimiterTest(t) - - rlFlags.Window = 500 - rlFlags.Rate = math.NewUint(1300) // 1300 ZETA - k.SetRateLimiterFlags(ctx, rlFlags) - - res, err := k.ListPendingCctxWithinRateLimit(ctx, &types.QueryListPendingCctxWithinRateLimitRequest{}) - require.NoError(t, err) - require.Equal(t, 320, len(res.CrossChainTx)) // 120 ETH cctx + 200 BTC cctx - require.EqualValues(t, cctxsETH[:120], res.CrossChainTx[0:120]) - require.EqualValues(t, cctxsBTC, res.CrossChainTx[120:320]) - require.EqualValues(t, uint64(400), res.TotalPending) - require.True(t, res.RateLimitExceeded) - require.True(t, res.ValueWithinWindow >= 1300) - }) } diff --git a/x/fungible/keeper/foreign_coins.go b/x/fungible/keeper/foreign_coins.go index c695dcee1b..5dfe744829 100644 --- a/x/fungible/keeper/foreign_coins.go +++ b/x/fungible/keeper/foreign_coins.go @@ -87,12 +87,12 @@ func (k Keeper) GetAllForeignCoinMap(ctx sdk.Context) map[int64]map[string]types foreignCoinMap := make(map[int64]map[string]types.ForeignCoins) for _, c := range allForeignCoins { - if _, found := fCoinMap[c.ForeignChainId]; !found { - fCoinMap[c.ForeignChainId] = make(map[string]types.ForeignCoins) + if _, found := foreignCoinMap[c.ForeignChainId]; !found { + foreignCoinMap[c.ForeignChainId] = make(map[string]types.ForeignCoins) } - fCoinMap[c.ForeignChainId][strings.ToLower(c.Asset)] = c + foreignCoinMap[c.ForeignChainId][strings.ToLower(c.Asset)] = c } - return fCoinMap + return foreignCoinMap } // GetGasCoinForForeignCoin returns the gas coin for a given chain From f40cf454d51e86f86fd4bd963a0d89c3e740b545 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 24 Apr 2024 00:31:23 -0500 Subject: [PATCH 22/40] handle edge case when pending cctxs span wider block range than sliding window --- .../keeper/grpc_query_cctx_rate_limit.go | 40 +++++++++++++++---- .../keeper/grpc_query_cctx_rate_limit_test.go | 31 +++++++++++++- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 7a05e9341d..ce9987d2a1 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -78,11 +78,13 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que var gasCoinRates map[int64]sdk.Dec var erc20CoinRates map[int64]map[string]sdk.Dec var foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins - var rateLimitInZeta sdk.Dec + var windowLimitInZeta sdk.Dec + var blockLimitInZeta sdk.Dec if applyLimit { gasCoinRates, erc20CoinRates = k.GetRateLimiterRates(ctx) foreignCoinMap = k.fungibleKeeper.GetAllForeignCoinMap(ctx) - rateLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()) + windowLimitInZeta = sdk.NewDecFromBigInt(rateLimitFlags.Rate.BigInt()) + blockLimitInZeta = windowLimitInZeta.Quo(sdk.NewDec(rateLimitFlags.Window)) } // the criteria to stop adding cctxs to the rpc response @@ -97,15 +99,40 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que return cctx.InboundTxParams.InboundTxObservedExternalHeight >= uint64(leftWindowBoundary) } - // query pending nonces for each foreign chain + // query pending nonces for each foreign chain and get the lowest height of the pending cctxs // Note: The pending nonces could change during the RPC call, so query them beforehand + lowestPendingCctxHeight := int64(0) pendingNoncesMap := make(map[int64]*observertypes.PendingNonces) for _, chain := range chains { pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) if !found { return nil, status.Error(codes.Internal, "pending nonces not found") } - pendingNoncesMap[chain.ChainId] = &pendingNonces + + // insert pending nonces and update lowest height + if pendingNonces.NonceLow < pendingNonces.NonceHigh { + pendingNoncesMap[chain.ChainId] = &pendingNonces + cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, chain.ChainId, pendingNonces.NonceLow) + if err != nil { + return nil, err + } + // #nosec G701 len always in range + cctxHeight := int64(cctx.InboundTxParams.InboundTxObservedExternalHeight) + if lowestPendingCctxHeight == 0 || cctxHeight < lowestPendingCctxHeight { + lowestPendingCctxHeight = cctxHeight + } + } + } + + // invariant: for period of time > window, the average rate per block cannot exceed `blockLimitInZeta` + pendingCctxsLimitInZeta := 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 + pendingCctxWindow := height - lowestPendingCctxHeight + 1 + if pendingCctxWindow > rateLimitFlags.Window { + pendingCctxsLimitInZeta = blockLimitInZeta.Mul(sdk.NewDec(pendingCctxWindow)) + } } // query backwards for potential missed pending cctxs for each foreign chain @@ -143,7 +170,7 @@ LoopBackwards: 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, rateLimitInZeta) { + if inWindow && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, windowLimitInZeta) { limitExceeded = true break LoopBackwards } @@ -168,14 +195,13 @@ LoopForwards: if err != nil { return nil, err } - inWindow := isCctxInWindow(cctx) // 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 inWindow && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, rateLimitInZeta) { + if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalCctxValueInZeta, pendingCctxsLimitInZeta) { limitExceeded = true break LoopForwards } 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 cd69e663d4..c47317edad 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -408,7 +408,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { rateLimitExceeded: true, }, { - name: "set high rate and big window to early to break inner loop with the criteria #1", + name: "set high rate and wide window to early to break inner loop with the criteria #1", // 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, @@ -486,6 +486,35 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { expectedTotalPending: 400, rateLimitExceeded: true, }, + { + name: "set low rate and narrow window to early break the LoopForwards loop with criteria #2", + // 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, + 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, + // 160 ETH cctx + 200 BTC cctx + expectedCctxs: append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:160]...), btcPendingCctxs...), + expectedTotalPending: 400, + rateLimitExceeded: true, + }, } for _, tt := range tests { From 578afccb54c86839c9c505dab05893132164b7ee Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 24 Apr 2024 13:43:44 -0500 Subject: [PATCH 23/40] 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 24/40] 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 25/40] 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 514cea70195eadffb3b11fd1f8a19c03ff3e1e03 Mon Sep 17 00:00:00 2001 From: Tanmay Date: Thu, 25 Apr 2024 11:17:19 -0400 Subject: [PATCH 26/40] refactor: allow zeta deposits to new zevm address (#2076) * allow zevm coin deposit to unknow addresses * add e2e tests * add changelog * add comments --- changelog.md | 1 + cmd/zetae2e/local/local.go | 2 ++ e2e/e2etests/e2etests.go | 9 ++++++++ e2e/e2etests/test_zeta_deposit.go | 19 +++++++++++++++++ x/crosschain/keeper/process_outbound_test.go | 15 ++++++++++++- .../keeper/zevm_message_passing_test.go | 20 ++++++++++-------- x/fungible/keeper/zevm_msg_passing.go | 21 ++++++------------- 7 files changed, 62 insertions(+), 25 deletions(-) diff --git a/changelog.md b/changelog.md index 61b85985bf..41aa1b32fd 100644 --- a/changelog.md +++ b/changelog.md @@ -44,6 +44,7 @@ * [1989](https://github.com/zeta-chain/node/pull/1989) - simplify `IsSendOutTxProcessed` method and add unit tests * [2013](https://github.com/zeta-chain/node/pull/2013) - rename `GasPriceVoter` message to `VoteGasPrice` * [2059](https://github.com/zeta-chain/node/pull/2059) - Remove unused params from all functions in zetanode +* [2076](https://github.com/zeta-chain/node/pull/2076) - automatically deposit native zeta to an address if it doesn't exist on ZEVM. ### Features diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 0fee34f8a9..62e0182ecf 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -257,6 +257,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestMessagePassingEVMtoZEVMName, e2etests.TestMessagePassingEVMtoZEVMRevertName, e2etests.TestMessagePassingZEVMtoEVMRevertName, + e2etests.TestZetaDepositName, + e2etests.TestZetaDepositNewAddressName, } bitcoinTests := []string{ e2etests.TestBitcoinWithdrawSegWitName, diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 09197c874a..583606ec50 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -44,6 +44,7 @@ const ( TestEtherWithdrawRestrictedName = "eth_withdraw_restricted" TestBitcoinDepositName = "bitcoin_deposit" TestZetaDepositName = "zeta_deposit" + TestZetaDepositNewAddressName = "zeta_deposit_new_address" TestZetaDepositRestrictedName = "zeta_deposit_restricted" TestDonationEtherName = "donation_ether" @@ -120,6 +121,14 @@ var AllE2ETests = []runner.E2ETest{ }, TestZetaDeposit, ), + runner.NewE2ETest( + TestZetaDepositNewAddressName, + "deposit ZETA from Ethereum to a new ZEVM address which does not exist yet", + []runner.ArgDefinition{ + runner.ArgDefinition{Description: "amount in azeta", DefaultValue: "1000000000000000000"}, + }, + TestZetaDepositNewAddress, + ), runner.NewE2ETest( TestZetaWithdrawBTCRevertName, "sending ZETA from ZEVM to Bitcoin with a message that should revert cctxs", diff --git a/e2e/e2etests/test_zeta_deposit.go b/e2e/e2etests/test_zeta_deposit.go index 237cc66d0d..48ac5e97f4 100644 --- a/e2e/e2etests/test_zeta_deposit.go +++ b/e2e/e2etests/test_zeta_deposit.go @@ -6,6 +6,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/testutil/sample" "github.com/zeta-chain/zetacore/zetaclient/testutils" ) @@ -26,6 +27,24 @@ func TestZetaDeposit(r *runner.E2ERunner, args []string) { r.Logger.CCTX(*cctx, "deposit") } +func TestZetaDepositNewAddress(r *runner.E2ERunner, args []string) { + if len(args) != 1 { + panic("TestZetaDepositNewAddress requires exactly one argument for the amount.") + } + + amount, ok := big.NewInt(0).SetString(args[0], 10) + if !ok { + panic("Invalid amount specified for TestZetaDepositNewAddress.") + } + + newAddress := sample.EthAddress() + hash := r.DepositZetaWithAmount(newAddress, amount) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, hash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") +} + func TestZetaDepositRestricted(r *runner.E2ERunner, args []string) { if len(args) != 1 { panic("TestZetaDepositRestricted requires exactly one argument for the amount.") diff --git a/x/crosschain/keeper/process_outbound_test.go b/x/crosschain/keeper/process_outbound_test.go index b4dbbef200..7ec80270f5 100644 --- a/x/crosschain/keeper/process_outbound_test.go +++ b/x/crosschain/keeper/process_outbound_test.go @@ -5,6 +5,7 @@ import ( "math/big" "testing" + "cosmossdk.io/errors" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/evmos/ethermint/x/evm/statedb" "github.com/stretchr/testify/mock" @@ -77,10 +78,22 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { }) t.Run("unable to process failed outbound if ZETARevertAndCallContract fails", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() + errorFailedZETARevertAndCallContract := errors.New("test", 999, "failed ZETARevertAndCallContract") cctx := GetERC20Cctx(t, receiver, chains.GoerliChain(), "", big.NewInt(42)) cctx.InboundTxParams.SenderChainId = chains.ZetaChainMainnet().ChainId + fungibleMock.On("ZETARevertAndCallContract", mock.Anything, + ethcommon.HexToAddress(cctx.InboundTxParams.Sender), + ethcommon.HexToAddress(cctx.GetCurrentOutTxParam().Receiver), + cctx.InboundTxParams.SenderChainId, + cctx.GetCurrentOutTxParam().ReceiverChainId, + cctx.GetCurrentOutTxParam().Amount.BigInt(), + mock.Anything, + mock.Anything).Return(nil, errorFailedZETARevertAndCallContract).Once() err := k.ProcessFailedOutbound(ctx, cctx, sample.String()) require.ErrorContains(t, err, "failed ZETARevertAndCallContract") }) diff --git a/x/fungible/keeper/zevm_message_passing_test.go b/x/fungible/keeper/zevm_message_passing_test.go index b37621667d..fe03808e81 100644 --- a/x/fungible/keeper/zevm_message_passing_test.go +++ b/x/fungible/keeper/zevm_message_passing_test.go @@ -84,8 +84,8 @@ func TestKeeper_ZEVMDepositAndCallContract(t *testing.T) { require.Equal(t, inboundAmount.Int64(), b.Amount.Int64()) }) - t.Run("fail ZETADepositAndCallContract if account not found", func(t *testing.T) { - k, ctx, _, _ := keepertest.FungibleKeeper(t) + t.Run("automatically deposit coin if account not found", func(t *testing.T) { + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) zetaTxSender := sample.EthAddress() @@ -96,11 +96,12 @@ func TestKeeper_ZEVMDepositAndCallContract(t *testing.T) { cctxIndexBytes := [32]byte{} _, err := k.ZETADepositAndCallContract(ctx, zetaTxSender, zetaTxReceiver, inboundSenderChainID, inboundAmount, data, cctxIndexBytes) - require.ErrorIs(t, err, types.ErrAccountNotFound) - require.ErrorContains(t, err, "account not found") + require.NoError(t, err) + b := sdkk.BankKeeper.GetBalance(ctx, sdk.AccAddress(zetaTxReceiver.Bytes()), config.BaseDenom) + require.Equal(t, inboundAmount.Int64(), b.Amount.Int64()) }) - t.Run("fail ZETADepositAndCallContract id Deposit Fails", func(t *testing.T) { + t.Run("fail ZETADepositAndCallContract if Deposit Fails", func(t *testing.T) { k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{UseBankMock: true}) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) @@ -196,8 +197,8 @@ func TestKeeper_ZEVMRevertAndCallContract(t *testing.T) { require.Equal(t, amount.Int64(), b.Amount.Int64()) }) - t.Run("fail ZETARevertAndCallContract if account not found", func(t *testing.T) { - k, ctx, _, _ := keepertest.FungibleKeeper(t) + t.Run("automatically deposit coin if account not found", func(t *testing.T) { + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) zetaTxSender := sample.EthAddress() @@ -209,8 +210,9 @@ func TestKeeper_ZEVMRevertAndCallContract(t *testing.T) { cctxIndexBytes := [32]byte{} _, err := k.ZETARevertAndCallContract(ctx, zetaTxSender, zetaTxReceiver, senderChainID.Int64(), destinationChainID.Int64(), amount, data, cctxIndexBytes) - require.ErrorIs(t, err, types.ErrAccountNotFound) - require.ErrorContains(t, err, "account not found") + require.NoError(t, err) + b := sdkk.BankKeeper.GetBalance(ctx, sdk.AccAddress(zetaTxSender.Bytes()), config.BaseDenom) + require.Equal(t, amount.Int64(), b.Amount.Int64()) }) t.Run("fail ZETARevertAndCallContract if Deposit Fails", func(t *testing.T) { diff --git a/x/fungible/keeper/zevm_msg_passing.go b/x/fungible/keeper/zevm_msg_passing.go index ab3210c002..827a937ae8 100644 --- a/x/fungible/keeper/zevm_msg_passing.go +++ b/x/fungible/keeper/zevm_msg_passing.go @@ -1,18 +1,15 @@ package keeper import ( - "fmt" "math/big" - "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" evmtypes "github.com/evmos/ethermint/x/evm/types" - "github.com/zeta-chain/zetacore/x/fungible/types" ) -// ZEVMDepositAndCallContract deposits ZETA to the to address if its an account -// If it's not an account it calls onReceive function of the connector contract and provides the address as the destinationAddress +// ZETADepositAndCallContract deposits native ZETA to the to address if its an account or if the account does not exist yet +// If it's not an account it calls onReceive function of the connector contract and provides the address as the destinationAddress .The amount of tokens is minted to the fungible module account, wrapped and sent to the contract func (k Keeper) ZETADepositAndCallContract(ctx sdk.Context, sender ethcommon.Address, to ethcommon.Address, @@ -21,10 +18,7 @@ func (k Keeper) ZETADepositAndCallContract(ctx sdk.Context, data []byte, indexBytes [32]byte) (*evmtypes.MsgEthereumTxResponse, error) { acc := k.evmKeeper.GetAccount(ctx, to) - if acc == nil { - return nil, errors.Wrap(types.ErrAccountNotFound, fmt.Sprintf("address: %s", to.String())) - } - if !acc.IsContract() { + if acc == nil || !acc.IsContract() { err := k.DepositCoinZeta(ctx, to, inboundAmount) if err != nil { return nil, err @@ -35,8 +29,8 @@ func (k Keeper) ZETADepositAndCallContract(ctx sdk.Context, return k.CallOnReceiveZevmConnector(ctx, sender.Bytes(), big.NewInt(inboundSenderChainID), to, inboundAmount, data, indexBytes) } -// ZEVMRevertAndCallContract deposits ZETA to the sender address if its an account -// If it's not an account it calls onRevert function of the connector contract and provides the sender address as the zetaTxSenderAddress +// ZETARevertAndCallContract deposits native ZETA to the sender address if its account or if the account does not exist yet +// If it's not an account it calls onRevert function of the connector contract and provides the sender address as the zetaTxSenderAddress.The amount of tokens is minted to the fungible module account, wrapped and sent to the contract func (k Keeper) ZETARevertAndCallContract(ctx sdk.Context, sender ethcommon.Address, to ethcommon.Address, @@ -46,10 +40,7 @@ func (k Keeper) ZETARevertAndCallContract(ctx sdk.Context, data []byte, indexBytes [32]byte) (*evmtypes.MsgEthereumTxResponse, error) { acc := k.evmKeeper.GetAccount(ctx, sender) - if acc == nil { - return nil, errors.Wrap(types.ErrAccountNotFound, fmt.Sprintf("address: %s", to.String())) - } - if !acc.IsContract() { + if acc == nil || !acc.IsContract() { err := k.DepositCoinZeta(ctx, sender, remainingAmount) if err != nil { return nil, err From daba26989032619c1b8a8ee064cd88d477e81d70 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 25 Apr 2024 11:29:23 -0500 Subject: [PATCH 27/40] 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", From c011900c2fdd4e651a9e58a3f10f93aee4ec5774 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 29 Apr 2024 12:27:59 -0500 Subject: [PATCH 28/40] replace sdk.Dec with sdkmath.Int to represent cctx value in azeta --- pkg/coin/coin.go | 4 ++ pkg/coin/coin_test.go | 4 ++ .../keeper/grpc_query_cctx_rate_limit.go | 66 +++++++++---------- .../keeper/grpc_query_cctx_rate_limit_test.go | 34 +++++----- 4 files changed, 58 insertions(+), 50 deletions(-) diff --git a/pkg/coin/coin.go b/pkg/coin/coin.go index 74bbca935b..a11f5b91bd 100644 --- a/pkg/coin/coin.go +++ b/pkg/coin/coin.go @@ -7,6 +7,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +func AzetaPerZeta() sdk.Dec { + return sdk.NewDec(1e18) +} + func GetCoinType(coin string) (CoinType, error) { coinInt, err := strconv.ParseInt(coin, 10, 32) if err != nil { diff --git a/pkg/coin/coin_test.go b/pkg/coin/coin_test.go index 6110c20538..be7808d7a2 100644 --- a/pkg/coin/coin_test.go +++ b/pkg/coin/coin_test.go @@ -7,6 +7,10 @@ import ( "github.com/stretchr/testify/require" ) +func Test_AzetaPerZeta(t *testing.T) { + require.Equal(t, sdk.NewDec(1e18), AzetaPerZeta()) +} + func Test_GetAzetaDecFromAmountInZeta(t *testing.T) { tt := []struct { name string diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index aadb94674d..57ce375048 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -5,6 +5,7 @@ import ( "sort" "strings" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" @@ -31,7 +32,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) - totalWithdrawInZeta := sdk.NewDec(0) + totalWithdrawInAzeta := sdkmath.NewInt(0) cctxs := make([]*types.CrossChainTx, 0) chains := k.zetaObserverKeeper.GetSupportedForeignChains(ctx) @@ -81,15 +82,15 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que var gasCoinRates map[int64]sdk.Dec var erc20CoinRates map[int64]map[string]sdk.Dec var foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins - var windowLimitInZeta sdk.Dec - var blockLimitInZeta sdk.Dec + var blockLimitInAzeta sdkmath.Int + var windowLimitInAzeta sdkmath.Int if applyLimit { gasCoinRates, erc20CoinRates = k.GetRateLimiterRates(ctx) foreignCoinMap = k.fungibleKeeper.GetAllForeignCoinMap(ctx) - // 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)) + // initiate block limit and window limit in azeta + blockLimitInAzeta = sdkmath.NewIntFromBigInt(rateLimitFlags.Rate.BigInt()) + windowLimitInAzeta = blockLimitInAzeta.Mul(sdkmath.NewInt(rateLimitFlags.Window)) } // the criteria to stop adding cctxs to the rpc response @@ -132,14 +133,14 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que // 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 + withdrawLimitInAzeta := windowLimitInAzeta if lowestPendingCctxHeight != 0 { // `pendingCctxWindow` is the width of [lowestPendingCctxHeight, height] window // 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 { withdrawWindow = pendingCctxWindow - withdrawLimitInZeta = blockLimitInZeta.Mul(sdk.NewDec(pendingCctxWindow)) + withdrawLimitInAzeta = blockLimitInAzeta.Mul(sdk.NewInt(pendingCctxWindow)) } } @@ -170,7 +171,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que break } // 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) { + if inWindow && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { limitExceeded = true continue } @@ -203,7 +204,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que } // 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) { + if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { limitExceeded = true continue } @@ -232,26 +233,25 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que CrossChainTx: cctxs, TotalPending: totalPending, CurrentWithdrawWindow: withdrawWindow, - CurrentWithdrawRate: totalWithdrawInZeta.Mul(sdk.NewDec(10).Power(18)).Quo(sdk.NewDec(withdrawWindow)).String(), + CurrentWithdrawRate: totalWithdrawInAzeta.Quo(sdk.NewInt(withdrawWindow)).String(), RateLimitExceeded: limitExceeded, }, nil } -// ConvertCctxValue converts the value of the cctx in ZETA using given conversion rates +// ConvertCctxValue converts the value of the cctx to azeta using given conversion rates func ConvertCctxValue( chainID int64, cctx *types.CrossChainTx, gasCoinRates map[int64]sdk.Dec, erc20CoinRates map[int64]map[string]sdk.Dec, foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins, -) sdk.Dec { +) sdkmath.Int { var rate sdk.Dec var decimals uint64 switch cctx.InboundTxParams.CoinType { case coin.CoinType_Zeta: // no conversion needed for ZETA - amountCctx := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) - return amountCctx.Quo(sdk.NewDec(10).Power(18)) + return sdkmath.NewIntFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) case coin.CoinType_Gas: rate = gasCoinRates[chainID] case coin.CoinType_ERC20: @@ -259,43 +259,43 @@ func ConvertCctxValue( _, found := erc20CoinRates[chainID] if !found { // skip if no rate found for this chainID - return sdk.NewDec(0) + return sdkmath.NewInt(0) } rate = erc20CoinRates[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] default: // skip CoinType_Cmd - return sdk.NewDec(0) + return sdkmath.NewInt(0) } // should not happen, return 0 to skip if it happens if rate.IsNil() || rate.LTE(sdk.NewDec(0)) { - return sdk.NewDec(0) + return sdkmath.NewInt(0) } // get foreign coin decimals foreignCoinFromChainMap, found := foreignCoinMap[chainID] if !found { // skip if no coin found for this chainID - return sdk.NewDec(0) + return sdkmath.NewInt(0) } foreignCoin, found := foreignCoinFromChainMap[strings.ToLower(cctx.InboundTxParams.Asset)] if !found { // skip if no coin found for this Asset - return sdk.NewDec(0) + return sdkmath.NewInt(0) } decimals = uint64(foreignCoin.Decimals) - // given decimals = 6, the `oneZrc20` amount will be 10^6 = 1000000 + // the whole coin amounts of zeta and zrc20 + // given decimals = 6, the amount will be 10^6 = 1000000 + oneZeta := coin.AzetaPerZeta() oneZrc20 := sdk.NewDec(10).Power(decimals) - // convert asset amount into ZETA - // step 1: convert the amount into ZRC20 integer amount - // step 2: convert the ZRC20 integer amount into decimal amount + // convert cctx asset amount into azeta amount // given amountCctx = 2000000, rate = 0.8, decimals = 6 - // the amountZrc20 = 2000000 * 0.8 = 1600000, the amountZeta = 1600000 / 1000000 = 1.6 - amountCctx := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) - amountZrc20 := amountCctx.Mul(rate) - amountZeta := amountZrc20.Quo(oneZrc20) - return amountZeta + // amountCctxDec: 2000000 * 0.8 = 1600000.0 + // amountAzetaDec: 1600000.0 * 10e18 / 10e6 = 1600000000000000000.0 + amountCctxDec := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) + amountAzetaDec := amountCctxDec.Mul(rate).Mul(oneZeta).Quo(oneZrc20) + return amountAzetaDec.TruncateInt() } // rateLimitExceeded accumulates the cctx value and then checks if the rate limit is exceeded @@ -306,10 +306,10 @@ func rateLimitExceeded( gasCoinRates map[int64]sdk.Dec, erc20CoinRates map[int64]map[string]sdk.Dec, foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins, - currentCctxValue *sdk.Dec, - withdrawLimitInZeta sdk.Dec, + currentCctxValue *sdkmath.Int, + withdrawLimitInZeta sdkmath.Int, ) bool { - amountZeta := ConvertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) - *currentCctxValue = currentCctxValue.Add(amountZeta) + cctxValueAzeta := ConvertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) + *currentCctxValue = currentCctxValue.Add(cctxValueAzeta) 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 71a31ce2f7..6418e57c03 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -161,7 +161,7 @@ func Test_ConvertCctxValue(t *testing.T) { // convert cctx value value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.MustNewDecFromStr("0.3"), value) + require.Equal(t, sdk.NewInt(3e17), value) }) t.Run("should convert cctx ETH value correctly", func(t *testing.T) { // create cctx with 0.003 ETH @@ -172,7 +172,7 @@ func Test_ConvertCctxValue(t *testing.T) { // 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) + require.Equal(t, sdk.NewInt(75e17), value) }) t.Run("should convert cctx BTC value correctly", func(t *testing.T) { // create cctx with 0.0007 BTC @@ -183,7 +183,7 @@ func Test_ConvertCctxValue(t *testing.T) { // 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) + require.Equal(t, sdk.NewInt(35).Mul(sdk.NewInt(1e18)), value) }) t.Run("should convert cctx USDT value correctly", func(t *testing.T) { // create cctx with 3 USDT @@ -194,7 +194,7 @@ func Test_ConvertCctxValue(t *testing.T) { // 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) + require.Equal(t, sdk.NewInt(24e17), value) }) t.Run("should return 0 if no rate found for chainID", func(t *testing.T) { cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) @@ -203,7 +203,7 @@ func Test_ConvertCctxValue(t *testing.T) { // use nil erc20CoinRates map to convert cctx value value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, nil, foreignCoinMap) - require.Equal(t, sdk.NewDec(0), value) + require.Equal(t, sdk.NewInt(0), value) }) t.Run("should return 0 if coinType is CoinType_Cmd", func(t *testing.T) { cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) @@ -212,7 +212,7 @@ func Test_ConvertCctxValue(t *testing.T) { // convert cctx value value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewDec(0), value) + require.Equal(t, sdk.NewInt(0), value) }) t.Run("should return 0 on nil rate or rate <= 0", func(t *testing.T) { cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) @@ -221,7 +221,7 @@ func Test_ConvertCctxValue(t *testing.T) { // use nil gasCoinRates map to convert cctx value value := keeper.ConvertCctxValue(ethChainID, cctx, nil, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewDec(0), value) + require.Equal(t, sdk.NewInt(0), value) // set rate to 0 zeroCoinRates, _ := k.GetRateLimiterRates(ctx) @@ -229,7 +229,7 @@ func Test_ConvertCctxValue(t *testing.T) { // convert cctx value value = keeper.ConvertCctxValue(ethChainID, cctx, zeroCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewDec(0), value) + require.Equal(t, sdk.NewInt(0), value) // set rate to -1 negativeCoinRates, _ := k.GetRateLimiterRates(ctx) @@ -237,7 +237,7 @@ func Test_ConvertCctxValue(t *testing.T) { // convert cctx value value = keeper.ConvertCctxValue(ethChainID, cctx, negativeCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewDec(0), value) + require.Equal(t, sdk.NewInt(0), value) }) t.Run("should return 0 if no coin found for chainID", func(t *testing.T) { cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) @@ -246,7 +246,7 @@ func Test_ConvertCctxValue(t *testing.T) { // use empty foreignCoinMap to convert cctx value value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, nil) - require.Equal(t, sdk.NewDec(0), value) + require.Equal(t, sdk.NewInt(0), value) }) t.Run("should return 0 if no coin found for asset", func(t *testing.T) { cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) @@ -260,7 +260,7 @@ func Test_ConvertCctxValue(t *testing.T) { // convert cctx value value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, tempCoinMap) - require.Equal(t, sdk.NewDec(0), value) + require.Equal(t, sdk.NewInt(0), value) }) } @@ -384,7 +384,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { 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 + expectedWithdrawRate: sdk.NewInt(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block rateLimitExceeded: false, }, { @@ -411,7 +411,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { 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 + expectedWithdrawRate: sdk.NewInt(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block rateLimitExceeded: false, }, { @@ -439,7 +439,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { 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 + expectedWithdrawRate: sdk.NewInt(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block rateLimitExceeded: true, }, { @@ -466,7 +466,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { 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 + expectedWithdrawRate: sdk.NewInt(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block rateLimitExceeded: false, }, { @@ -494,7 +494,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { 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 + expectedWithdrawRate: sdk.NewInt(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block rateLimitExceeded: true, }, { @@ -526,7 +526,7 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { 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 + expectedWithdrawRate: sdk.NewInt(3e18).String(), // 3 ZETA, (2.5 + 0.5) per block rateLimitExceeded: true, }, } From bd13759ede05ec752b4d7900af546b3f83df5574 Mon Sep 17 00:00:00 2001 From: Lucas Bertrand Date: Mon, 29 Apr 2024 20:03:15 +0200 Subject: [PATCH 29/40] test(e2e): add rate limiter admin E2E test (#2063) * refactor and create Withdraw ZETA general function * new rate limiter test * use rate limiter for admin test * fix the test: single approval and add liquidity * make generate * fix liquidity * fix uniswap pool * change localnet chain params * fix lint * add cli query * add nil check * fix nil point * modify tests * eliminate nil pending nonce issue * fix query * set flags * Update e2e/runner/evm.go Co-authored-by: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> * add back other advanced tests * make generate * add comment * fix eth liquidity cap test * fix withdraw count --------- Co-authored-by: Charlie Chen Co-authored-by: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> --- cmd/zetae2e/local/local.go | 1 + .../localnet/orchestrator/start-zetae2e.sh | 32 +-- .../zetacored/zetacored_query_crosschain.md | 1 + ...ain_list_pending_cctx_within_rate_limit.md | 33 +++ e2e/e2etests/e2etests.go | 7 + e2e/e2etests/test_eth_deposit.go | 4 +- e2e/e2etests/test_migrate_chain_support.go | 15 +- e2e/e2etests/test_rate_limiter.go | 203 ++++++++++++++++++ .../test_update_bytecode_connector.go | 35 ++- e2e/e2etests/test_zeta_withdraw.go | 71 +----- e2e/runner/evm.go | 3 +- e2e/runner/zeta.go | 74 +++++++ e2e/utils/evm.go | 2 +- x/crosschain/client/cli/query.go | 1 + .../client/cli/query_cctx_rate_limit.go | 35 +++ .../keeper/grpc_query_cctx_rate_limit.go | 8 +- x/observer/types/chain_params.go | 6 +- 17 files changed, 434 insertions(+), 97 deletions(-) create mode 100644 docs/cli/zetacored/zetacored_query_crosschain_list_pending_cctx_within_rate_limit.md create mode 100644 e2e/e2etests/test_rate_limiter.go create mode 100644 x/crosschain/client/cli/query_cctx_rate_limit.go diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 62e0182ecf..c061f4479b 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -301,6 +301,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestUpdateBytecodeZRC20Name, e2etests.TestUpdateBytecodeConnectorName, e2etests.TestDepositEtherLiquidityCapName, + e2etests.TestRateLimiterName, // TestMigrateChainSupportName tests EVM chain migration. Currently this test doesn't work with Anvil because pre-EIP1559 txs are not supported // See issue below for details diff --git a/contrib/localnet/orchestrator/start-zetae2e.sh b/contrib/localnet/orchestrator/start-zetae2e.sh index a893c689ee..228ee297ff 100644 --- a/contrib/localnet/orchestrator/start-zetae2e.sh +++ b/contrib/localnet/orchestrator/start-zetae2e.sh @@ -14,36 +14,36 @@ sleep 2 ### Create the accounts and fund them with Ether on local Ethereum network # unlock the deployer account -echo "funding deployer address 0xE5C5367B8224807Ac2207d350E60e1b6F27a7ecC with 100 Ether" -geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xE5C5367B8224807Ac2207d350E60e1b6F27a7ecC", value: web3.toWei(100,"ether")})' attach http://eth:8545 +echo "funding deployer address 0xE5C5367B8224807Ac2207d350E60e1b6F27a7ecC with 10000 Ether" +geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xE5C5367B8224807Ac2207d350E60e1b6F27a7ecC", value: web3.toWei(10000,"ether")})' attach http://eth:8545 # unlock erc20 tester accounts -echo "funding deployer address 0x6F57D5E7c6DBb75e59F1524a3dE38Fc389ec5Fd6 with 100 Ether" -geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x6F57D5E7c6DBb75e59F1524a3dE38Fc389ec5Fd6", value: web3.toWei(100,"ether")})' attach http://eth:8545 +echo "funding deployer address 0x6F57D5E7c6DBb75e59F1524a3dE38Fc389ec5Fd6 with 10000 Ether" +geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x6F57D5E7c6DBb75e59F1524a3dE38Fc389ec5Fd6", value: web3.toWei(10000,"ether")})' attach http://eth:8545 # unlock zeta tester accounts -echo "funding deployer address 0x5cC2fBb200A929B372e3016F1925DcF988E081fd with 100 Ether" -geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x5cC2fBb200A929B372e3016F1925DcF988E081fd", value: web3.toWei(100,"ether")})' attach http://eth:8545 +echo "funding deployer address 0x5cC2fBb200A929B372e3016F1925DcF988E081fd with 10000 Ether" +geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x5cC2fBb200A929B372e3016F1925DcF988E081fd", value: web3.toWei(10000,"ether")})' attach http://eth:8545 # unlock bitcoin tester accounts -echo "funding deployer address 0x283d810090EdF4043E75247eAeBcE848806237fD with 100 Ether" -geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x283d810090EdF4043E75247eAeBcE848806237fD", value: web3.toWei(100,"ether")})' attach http://eth:8545 +echo "funding deployer address 0x283d810090EdF4043E75247eAeBcE848806237fD with 10000 Ether" +geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x283d810090EdF4043E75247eAeBcE848806237fD", value: web3.toWei(10000,"ether")})' attach http://eth:8545 # unlock ethers tester accounts -echo "funding deployer address 0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A with 100 Ether" -geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A", value: web3.toWei(100,"ether")})' attach http://eth:8545 +echo "funding deployer address 0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A with 10000 Ether" +geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A", value: web3.toWei(10000,"ether")})' attach http://eth:8545 # unlock miscellaneous tests accounts -echo "funding deployer address 0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf with 100 Ether" -geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf", value: web3.toWei(100,"ether")})' attach http://eth:8545 +echo "funding deployer address 0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf with 10000 Ether" +geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf", value: web3.toWei(10000,"ether")})' attach http://eth:8545 # unlock admin erc20 tests accounts -echo "funding deployer address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 with 100 Ether" -geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", value: web3.toWei(100,"ether")})' attach http://eth:8545 +echo "funding deployer address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 with 10000 Ether" +geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", value: web3.toWei(10000,"ether")})' attach http://eth:8545 # unlock the TSS account -echo "funding TSS address 0xF421292cb0d3c97b90EEEADfcD660B893592c6A2 with 100 Ether" -geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xF421292cb0d3c97b90EEEADfcD660B893592c6A2", value: web3.toWei(100,"ether")})' attach http://eth:8545 +echo "funding TSS address 0xF421292cb0d3c97b90EEEADfcD660B893592c6A2 with 10000 Ether" +geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xF421292cb0d3c97b90EEEADfcD660B893592c6A2", value: web3.toWei(10000,"ether")})' attach http://eth:8545 ### Run zetae2e command depending on the option passed diff --git a/docs/cli/zetacored/zetacored_query_crosschain.md b/docs/cli/zetacored/zetacored_query_crosschain.md index 48345c136d..467cce1f25 100644 --- a/docs/cli/zetacored/zetacored_query_crosschain.md +++ b/docs/cli/zetacored/zetacored_query_crosschain.md @@ -35,6 +35,7 @@ zetacored query crosschain [flags] * [zetacored query crosschain list-in-tx-tracker](zetacored_query_crosschain_list-in-tx-tracker.md) - shows a list of in tx tracker by chainId * [zetacored query crosschain list-out-tx-tracker](zetacored_query_crosschain_list-out-tx-tracker.md) - list all OutTxTracker * [zetacored query crosschain list-pending-cctx](zetacored_query_crosschain_list-pending-cctx.md) - shows pending CCTX +* [zetacored query crosschain list_pending_cctx_within_rate_limit](zetacored_query_crosschain_list_pending_cctx_within_rate_limit.md) - list all pending CCTX within rate limit * [zetacored query crosschain show-cctx](zetacored_query_crosschain_show-cctx.md) - shows a CCTX * [zetacored query crosschain show-gas-price](zetacored_query_crosschain_show-gas-price.md) - shows a gasPrice * [zetacored query crosschain show-in-tx-hash-to-cctx](zetacored_query_crosschain_show-in-tx-hash-to-cctx.md) - shows a inTxHashToCctx diff --git a/docs/cli/zetacored/zetacored_query_crosschain_list_pending_cctx_within_rate_limit.md b/docs/cli/zetacored/zetacored_query_crosschain_list_pending_cctx_within_rate_limit.md new file mode 100644 index 0000000000..aeb3fa12f1 --- /dev/null +++ b/docs/cli/zetacored/zetacored_query_crosschain_list_pending_cctx_within_rate_limit.md @@ -0,0 +1,33 @@ +# query crosschain list_pending_cctx_within_rate_limit + +list all pending CCTX within rate limit + +``` +zetacored query crosschain list_pending_cctx_within_rate_limit [flags] +``` + +### Options + +``` + --grpc-addr string the gRPC endpoint to use for this chain + --grpc-insecure allow gRPC over insecure channels, if not TLS the server must use TLS + --height int Use a specific height to query state at (this can error if the node is pruning state) + -h, --help help for list_pending_cctx_within_rate_limit + --node string [host]:[port] to Tendermint RPC interface for this chain + -o, --output string Output format (text|json) +``` + +### Options inherited from parent commands + +``` + --chain-id string The network chain ID + --home string directory for config and data + --log_format string The logging format (json|plain) + --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored query crosschain](zetacored_query_crosschain.md) - Querying commands for the crosschain module + diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 583606ec50..e822d9c74a 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -59,6 +59,7 @@ const ( TestPauseZRC20Name = "pause_zrc20" TestUpdateBytecodeZRC20Name = "update_bytecode_zrc20" TestUpdateBytecodeConnectorName = "update_bytecode_connector" + TestRateLimiterName = "rate_limiter" ) // AllE2ETests is an ordered list of all e2e tests @@ -386,6 +387,12 @@ var AllE2ETests = []runner.E2ETest{ []runner.ArgDefinition{}, TestUpdateBytecodeConnector, ), + runner.NewE2ETest( + TestRateLimiterName, + "test sending cctxs with rate limiter enabled and show logs when processing cctxs", + []runner.ArgDefinition{}, + TestRateLimiter, + ), runner.NewE2ETest( TestMessagePassingZEVMToEVMName, "zevm -> evm message passing contract call", diff --git a/e2e/e2etests/test_eth_deposit.go b/e2e/e2etests/test_eth_deposit.go index 65a79f9c15..b4382b7740 100644 --- a/e2e/e2etests/test_eth_deposit.go +++ b/e2e/e2etests/test_eth_deposit.go @@ -268,8 +268,8 @@ func TestDepositEtherLiquidityCap(r *runner.E2ERunner, args []string) { } liquidityCap := math.NewUintFromBigInt(supply).Add(liquidityCapArg) - amountLessThanCap := liquidityCap.BigInt().Div(liquidityCap.BigInt(), big.NewInt(10)) // 1/10 of the cap - amountMoreThanCap := liquidityCap.BigInt().Mul(liquidityCap.BigInt(), big.NewInt(10)) // 10 times the cap + amountLessThanCap := liquidityCapArg.BigInt().Div(liquidityCapArg.BigInt(), big.NewInt(10)) // 1/10 of the cap + amountMoreThanCap := liquidityCapArg.BigInt().Mul(liquidityCapArg.BigInt(), big.NewInt(10)) // 10 times the cap msg := fungibletypes.NewMsgUpdateZRC20LiquidityCap( r.ZetaTxServer.GetAccountAddress(0), r.ETHZRC20Addr.Hex(), diff --git a/e2e/e2etests/test_migrate_chain_support.go b/e2e/e2etests/test_migrate_chain_support.go index 75bdb29d0e..0bbb754771 100644 --- a/e2e/e2etests/test_migrate_chain_support.go +++ b/e2e/e2etests/test_migrate_chain_support.go @@ -157,7 +157,20 @@ func TestMigrateChainSupport(r *runner.E2ERunner, _ []string) { newRunner.WaitForMinedCCTX(txEtherDeposit) // perform withdrawals on the new chain - TestZetaWithdraw(newRunner, []string{"10000000000000000000"}) + amount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10)) + newRunner.DepositAndApproveWZeta(amount) + tx := newRunner.WithdrawZeta(amount, true) + cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "zeta withdraw") + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + panic(fmt.Errorf( + "expected cctx status to be %s; got %s, message %s", + crosschaintypes.CctxStatus_OutboundMined, + cctx.CctxStatus.Status.String(), + cctx.CctxStatus.StatusMessage, + )) + } + TestEtherWithdraw(newRunner, []string{"50000000000000000"}) // finally try to deposit Zeta back diff --git a/e2e/e2etests/test_rate_limiter.go b/e2e/e2etests/test_rate_limiter.go new file mode 100644 index 0000000000..e28c627574 --- /dev/null +++ b/e2e/e2etests/test_rate_limiter.go @@ -0,0 +1,203 @@ +package e2etests + +import ( + "context" + "fmt" + "math/big" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "golang.org/x/sync/errgroup" +) + +const RateLimiterWithdrawNumber = 5 + +// rateLimiterFlags are the rate limiter flags for the test +var rateLimiterFlags = crosschaintypes.RateLimiterFlags{ + Enabled: true, + Rate: sdk.NewUint(1e17).MulUint64(5), // this value is used so rate is reached + Window: 10, +} + +func TestRateLimiter(r *runner.E2ERunner, _ []string) { + r.Logger.Info("TestRateLimiter") + + // deposit and approve 50 WZETA for the tests + r.DepositAndApproveWZeta(big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(50))) + + // add liquidity in the pool to prevent high slippage in WZETA/gas pair + if err := addZetaGasLiquidity(r); err != nil { + panic(err) + } + + // Set the rate limiter to 0.5ZETA per 10 blocks + // These rate limiter flags will only allow to process 1 withdraw per 10 blocks + r.Logger.Info("setting up rate limiter flags") + if err := setupRateLimiterFlags(r, rateLimiterFlags); err != nil { + panic(err) + } + + // Test with rate limiter + // TODO: define proper assertion to check the rate limiter is working + // https://github.com/zeta-chain/node/issues/2090 + r.Logger.Print("rate limiter enabled") + if err := createAndWaitWithdraws(r); err != nil { + panic(err) + } + + // Disable rate limiter + r.Logger.Info("disabling rate limiter") + if err := setupRateLimiterFlags(r, crosschaintypes.RateLimiterFlags{Enabled: false}); err != nil { + panic(err) + } + + // Test without rate limiter again + r.Logger.Print("rate limiter disabled") + if err := createAndWaitWithdraws(r); err != nil { + panic(err) + } +} + +// setupRateLimiterFlags sets up the rate limiter flags with flags defined in the test +func setupRateLimiterFlags(r *runner.E2ERunner, flags crosschaintypes.RateLimiterFlags) error { + adminAddr, err := r.ZetaTxServer.GetAccountAddressFromName(utils.FungibleAdminName) + if err != nil { + return err + } + _, err = r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, crosschaintypes.NewMsgUpdateRateLimiterFlags( + adminAddr, + flags, + )) + if err != nil { + return err + } + + return nil +} + +// createAndWaitWithdraws performs RateLimiterWithdrawNumber withdraws +func createAndWaitWithdraws(r *runner.E2ERunner) error { + startTime := time.Now() + + r.Logger.Print("starting %d withdraws", RateLimiterWithdrawNumber) + + // Perform RateLimiterWithdrawNumber withdraws to log time for completion + txs := make([]*ethtypes.Transaction, RateLimiterWithdrawNumber) + for i := 0; i < RateLimiterWithdrawNumber; i++ { + amount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(3)) + txs[i] = r.WithdrawZeta(amount, true) + } + + // start a error group to wait for all the withdraws to be mined + g, ctx := errgroup.WithContext(r.Ctx) + for i, tx := range txs { + // capture the loop variables + tx, i := tx, i + + // start a goroutine to wait for the withdraw to be mined + g.Go(func() error { + return waitForZetaWithdrawMined(ctx, r, tx, i, startTime) + }) + } + + // wait for all the withdraws to be mined + if err := g.Wait(); err != nil { + return err + } + + duration := time.Now().Sub(startTime).Seconds() + block, err := r.ZEVMClient.BlockNumber(r.Ctx) + if err != nil { + return fmt.Errorf("error getting block number: %w", err) + } + r.Logger.Print("all withdraws completed in %vs at block %d", duration, block) + + return nil +} + +// waitForZetaWithdrawMined waits for a zeta withdraw to be mined +// we first wait to get the receipt +// NOTE: this could be a more general function but we define it here for this test because we emit in the function logs specific to this test +func waitForZetaWithdrawMined(ctx context.Context, r *runner.E2ERunner, tx *ethtypes.Transaction, index int, startTime time.Time) error { + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInTxHash(ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "zeta withdraw") + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + return fmt.Errorf( + "expected cctx status to be %s; got %s, message %s", + crosschaintypes.CctxStatus_OutboundMined, + cctx.CctxStatus.Status.String(), + cctx.CctxStatus.StatusMessage, + ) + } + + // record the time for completion + duration := time.Now().Sub(startTime).Seconds() + block, err := r.ZEVMClient.BlockNumber(ctx) + if err != nil { + return err + } + r.Logger.Print("cctx %d mined in %vs at block %d", index, duration, block) + + return nil +} + +// addZetaGasLiquidity adds liquidity to the ZETA/gas pool +func addZetaGasLiquidity(r *runner.E2ERunner) error { + // use 10 ZETA and 10 ETH for the liquidity + // this will be sufficient for the tests + amount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10)) + approveAmount := big.NewInt(0).Mul(amount, big.NewInt(10)) + + // approve uniswap router to spend gas + txETHZRC20Approve, err := r.ETHZRC20.Approve(r.ZEVMAuth, r.UniswapV2RouterAddr, approveAmount) + if err != nil { + return fmt.Errorf("error approving ZETA: %w", err) + } + + // wait for the tx to be mined + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, txETHZRC20Approve, r.Logger, r.ReceiptTimeout) + if receipt.Status != 1 { + return fmt.Errorf("approve failed") + } + + // approve uniswap router to spend ZETA + txZETAApprove, err := r.WZeta.Approve(r.ZEVMAuth, r.UniswapV2RouterAddr, approveAmount) + if err != nil { + return fmt.Errorf("error approving ZETA: %w", err) + } + + // wait for the tx to be mined + receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, txZETAApprove, r.Logger, r.ReceiptTimeout) + if receipt.Status != 1 { + return fmt.Errorf("approve failed") + } + + // add liquidity in the pool to prevent high slippage in WZETA/gas pair + r.ZEVMAuth.Value = amount + txAddLiquidity, err := r.UniswapV2Router.AddLiquidityETH( + r.ZEVMAuth, + r.ETHZRC20Addr, + amount, + big.NewInt(1e18), + big.NewInt(1e18), + r.DeployerAddress, + big.NewInt(time.Now().Add(10*time.Minute).Unix()), + ) + if err != nil { + return fmt.Errorf("error adding liquidity: %w", err) + } + r.ZEVMAuth.Value = big.NewInt(0) + + // wait for the tx to be mined + receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, txAddLiquidity, r.Logger, r.ReceiptTimeout) + if receipt.Status != 1 { + return fmt.Errorf("add liquidity failed") + } + + return nil +} diff --git a/e2e/e2etests/test_update_bytecode_connector.go b/e2e/e2etests/test_update_bytecode_connector.go index 0770e80dc4..e961c1c074 100644 --- a/e2e/e2etests/test_update_bytecode_connector.go +++ b/e2e/e2etests/test_update_bytecode_connector.go @@ -1,17 +1,33 @@ package e2etests import ( + "fmt" + "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/zeta-chain/zetacore/e2e/contracts/testconnectorzevm" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" ) // TestUpdateBytecodeConnector tests updating the bytecode of a connector and interact with it func TestUpdateBytecodeConnector(r *runner.E2ERunner, _ []string) { - // Can withdraw 0.1ZETA - TestZetaWithdraw(r, []string{"10000000000000000000"}) + // Can withdraw 10ZETA + amount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10)) + r.DepositAndApproveWZeta(amount) + tx := r.WithdrawZeta(amount, true) + cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "zeta withdraw") + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + panic(fmt.Errorf( + "expected cctx status to be %s; got %s, message %s", + crosschaintypes.CctxStatus_OutboundMined, + cctx.CctxStatus.Status.String(), + cctx.CctxStatus.StatusMessage, + )) + } // Deploy the test contract newTestConnectorAddr, tx, _, err := testconnectorzevm.DeployTestZetaConnectorZEVM( @@ -65,6 +81,17 @@ func TestUpdateBytecodeConnector(r *runner.E2ERunner, _ []string) { panic("unexpected response") } - // Can continue to interact with the connector: withdraw 0.1ZETA - TestZetaWithdraw(r, []string{"10000000000000000000"}) + // Can continue to interact with the connector: withdraw 10ZETA + r.DepositAndApproveWZeta(amount) + tx = r.WithdrawZeta(amount, true) + cctx = utils.WaitCctxMinedByInTxHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "zeta withdraw") + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + panic(fmt.Errorf( + "expected cctx status to be %s; got %s, message %s", + crosschaintypes.CctxStatus_OutboundMined, + cctx.CctxStatus.Status.String(), + cctx.CctxStatus.StatusMessage, + )) + } } diff --git a/e2e/e2etests/test_zeta_withdraw.go b/e2e/e2etests/test_zeta_withdraw.go index 6dfc664813..a51da4c6f5 100644 --- a/e2e/e2etests/test_zeta_withdraw.go +++ b/e2e/e2etests/test_zeta_withdraw.go @@ -5,12 +5,11 @@ import ( "math/big" - ethcommon "github.com/ethereum/go-ethereum/common" connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zetaconnectorzevm.sol" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" "github.com/zeta-chain/zetacore/pkg/chains" - cctxtypes "github.com/zeta-chain/zetacore/x/crosschain/types" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" ) func TestZetaWithdraw(r *runner.E2ERunner, args []string) { @@ -20,76 +19,18 @@ func TestZetaWithdraw(r *runner.E2ERunner, args []string) { amount, ok := big.NewInt(0).SetString(args[0], 10) if !ok { - panic("Invalid amount specified for TestZetaWithdraw.") + panic("invalid amount specified") } - r.ZEVMAuth.Value = amount - tx, err := r.WZeta.Deposit(r.ZEVMAuth) - if err != nil { - panic(err) - } - r.ZEVMAuth.Value = big.NewInt(0) - r.Logger.Info("wzeta deposit tx hash: %s", tx.Hash().Hex()) - - receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - r.Logger.EVMReceipt(*receipt, "wzeta deposit") - if receipt.Status == 0 { - panic("deposit failed") - } - - chainID, err := r.EVMClient.ChainID(r.Ctx) - if err != nil { - panic(err) - } - - tx, err = r.WZeta.Approve(r.ZEVMAuth, r.ConnectorZEVMAddr, amount) - if err != nil { - panic(err) - } - r.Logger.Info("wzeta approve tx hash: %s", tx.Hash().Hex()) - - receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - r.Logger.EVMReceipt(*receipt, "wzeta approve") - if receipt.Status == 0 { - panic(fmt.Sprintf("approve failed, logs: %+v", receipt.Logs)) - } - - tx, err = r.ConnectorZEVM.Send(r.ZEVMAuth, connectorzevm.ZetaInterfacesSendInput{ - DestinationChainId: chainID, - DestinationAddress: r.DeployerAddress.Bytes(), - DestinationGasLimit: big.NewInt(400_000), - Message: nil, - ZetaValueAndGas: amount, - ZetaParams: nil, - }) - if err != nil { - panic(err) - } - r.Logger.Info("send tx hash: %s", tx.Hash().Hex()) - receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - r.Logger.EVMReceipt(*receipt, "send") - if receipt.Status == 0 { - panic(fmt.Sprintf("send failed, logs: %+v", receipt.Logs)) - } - - r.Logger.Info(" Logs:") - for _, log := range receipt.Logs { - sentLog, err := r.ConnectorZEVM.ParseZetaSent(*log) - if err == nil { - r.Logger.Info(" Dest Addr: %s", ethcommon.BytesToAddress(sentLog.DestinationAddress).Hex()) - r.Logger.Info(" Dest Chain: %d", sentLog.DestinationChainId) - r.Logger.Info(" Dest Gas: %d", sentLog.DestinationGasLimit) - r.Logger.Info(" Zeta Value: %d", sentLog.ZetaValueAndGas) - } - } - r.Logger.Info("waiting for cctx status to change to final...") + r.DepositAndApproveWZeta(amount) + tx := r.WithdrawZeta(amount, true) cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "zeta withdraw") - if cctx.CctxStatus.Status != cctxtypes.CctxStatus_OutboundMined { + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { panic(fmt.Errorf( "expected cctx status to be %s; got %s, message %s", - cctxtypes.CctxStatus_OutboundMined, + crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status.String(), cctx.CctxStatus.StatusMessage, )) diff --git a/e2e/runner/evm.go b/e2e/runner/evm.go index 0bc6d77070..e878de97e7 100644 --- a/e2e/runner/evm.go +++ b/e2e/runner/evm.go @@ -127,7 +127,8 @@ func (runner *E2ERunner) DepositERC20WithAmountAndMessage(to ethcommon.Address, // DepositEther sends Ethers into ZEVM func (runner *E2ERunner) DepositEther(testHeader bool) ethcommon.Hash { - return runner.DepositEtherWithAmount(testHeader, big.NewInt(1000000000000000000)) // in wei (1 eth) + amount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(100)) // 100 eth + return runner.DepositEtherWithAmount(testHeader, amount) } // DepositEtherWithAmount sends Ethers into ZEVM diff --git a/e2e/runner/zeta.go b/e2e/runner/zeta.go index d7fdbdcdf4..58a4513923 100644 --- a/e2e/runner/zeta.go +++ b/e2e/runner/zeta.go @@ -7,6 +7,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" + connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zetaconnectorzevm.sol" "github.com/zeta-chain/zetacore/e2e/utils" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -138,3 +139,76 @@ func (runner *E2ERunner) DepositZetaWithAmount(to ethcommon.Address, amount *big return tx.Hash() } + +// DepositAndApproveWZeta deposits and approves WZETA on ZetaChain from the ZETA smart contract on ZEVM +func (runner *E2ERunner) DepositAndApproveWZeta(amount *big.Int) { + runner.ZEVMAuth.Value = amount + tx, err := runner.WZeta.Deposit(runner.ZEVMAuth) + if err != nil { + panic(err) + } + runner.ZEVMAuth.Value = big.NewInt(0) + runner.Logger.Info("wzeta deposit tx hash: %s", tx.Hash().Hex()) + + receipt := utils.MustWaitForTxReceipt(runner.Ctx, runner.ZEVMClient, tx, runner.Logger, runner.ReceiptTimeout) + runner.Logger.EVMReceipt(*receipt, "wzeta deposit") + if receipt.Status == 0 { + panic("deposit failed") + } + + tx, err = runner.WZeta.Approve(runner.ZEVMAuth, runner.ConnectorZEVMAddr, amount) + if err != nil { + panic(err) + } + runner.Logger.Info("wzeta approve tx hash: %s", tx.Hash().Hex()) + + receipt = utils.MustWaitForTxReceipt(runner.Ctx, runner.ZEVMClient, tx, runner.Logger, runner.ReceiptTimeout) + runner.Logger.EVMReceipt(*receipt, "wzeta approve") + if receipt.Status == 0 { + panic(fmt.Sprintf("approve failed, logs: %+v", receipt.Logs)) + } +} + +// WithdrawZeta withdraws ZETA from ZetaChain to the ZETA smart contract on EVM +// waitReceipt specifies whether to wait for the tx receipt and check if the tx was successful +func (runner *E2ERunner) WithdrawZeta(amount *big.Int, waitReceipt bool) *ethtypes.Transaction { + chainID, err := runner.EVMClient.ChainID(runner.Ctx) + if err != nil { + panic(err) + } + + tx, err := runner.ConnectorZEVM.Send(runner.ZEVMAuth, connectorzevm.ZetaInterfacesSendInput{ + DestinationChainId: chainID, + DestinationAddress: runner.DeployerAddress.Bytes(), + DestinationGasLimit: big.NewInt(400_000), + Message: nil, + ZetaValueAndGas: amount, + ZetaParams: nil, + }) + if err != nil { + panic(err) + } + runner.Logger.Info("send tx hash: %s", tx.Hash().Hex()) + + if waitReceipt { + receipt := utils.MustWaitForTxReceipt(runner.Ctx, runner.ZEVMClient, tx, runner.Logger, runner.ReceiptTimeout) + runner.Logger.EVMReceipt(*receipt, "send") + if receipt.Status == 0 { + panic(fmt.Sprintf("send failed, logs: %+v", receipt.Logs)) + + } + + runner.Logger.Info(" Logs:") + for _, log := range receipt.Logs { + sentLog, err := runner.ConnectorZEVM.ParseZetaSent(*log) + if err == nil { + runner.Logger.Info(" Dest Addr: %s", ethcommon.BytesToAddress(sentLog.DestinationAddress).Hex()) + runner.Logger.Info(" Dest Chain: %d", sentLog.DestinationChainId) + runner.Logger.Info(" Dest Gas: %d", sentLog.DestinationGasLimit) + runner.Logger.Info(" Zeta Value: %d", sentLog.ZetaValueAndGas) + } + } + } + + return tx +} diff --git a/e2e/utils/evm.go b/e2e/utils/evm.go index d3f34f818c..fc6792d617 100644 --- a/e2e/utils/evm.go +++ b/e2e/utils/evm.go @@ -55,7 +55,7 @@ func MustWaitForTxReceipt( receipt, err := client.TransactionReceipt(ctx, tx.Hash()) if err != nil { if !errors.Is(err, ethereum.NotFound) && i%10 == 0 { - logger.Info("fetching tx receipt error: ", err.Error()) + logger.Info("fetching tx %s receipt error: %s ", tx.Hash().Hex(), err.Error()) } time.Sleep(1 * time.Second) continue diff --git a/x/crosschain/client/cli/query.go b/x/crosschain/client/cli/query.go index 9f9ae29752..22085bfb68 100644 --- a/x/crosschain/client/cli/query.go +++ b/x/crosschain/client/cli/query.go @@ -36,6 +36,7 @@ func GetQueryCmd(_ string) *cobra.Command { CmdListInTxTrackerByChain(), CmdListInTxTrackers(), CmdGetZetaAccounting(), + CmdListPendingCCTXWithinRateLimit(), CmdShowUpdateRateLimiterFlags(), ) diff --git a/x/crosschain/client/cli/query_cctx_rate_limit.go b/x/crosschain/client/cli/query_cctx_rate_limit.go new file mode 100644 index 0000000000..251a3897de --- /dev/null +++ b/x/crosschain/client/cli/query_cctx_rate_limit.go @@ -0,0 +1,35 @@ +package cli + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func CmdListPendingCCTXWithinRateLimit() *cobra.Command { + cmd := &cobra.Command{ + Use: "list_pending_cctx_within_rate_limit", + Short: "list all pending CCTX within rate limit", + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.ListPendingCctxWithinRateLimit( + context.Background(), &types.QueryListPendingCctxWithinRateLimitRequest{}, + ) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 57ce375048..176a3dcee4 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -17,7 +17,7 @@ import ( // ListPendingCctxWithinRateLimit returns a list of pending cctxs that do not exceed the outbound rate limit // a limit for the number of cctxs to return can be specified or the default is MaxPendingCctxs -func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.QueryListPendingCctxWithinRateLimitRequest) (*types.QueryListPendingCctxWithinRateLimitResponse, error) { +func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.QueryListPendingCctxWithinRateLimitRequest) (res *types.QueryListPendingCctxWithinRateLimitResponse, err error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } @@ -108,16 +108,16 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que // query pending nonces for each foreign chain and get the lowest height of the pending cctxs // Note: The pending nonces could change during the RPC call, so query them beforehand lowestPendingCctxHeight := int64(0) - pendingNoncesMap := make(map[int64]*observertypes.PendingNonces) + pendingNoncesMap := make(map[int64]observertypes.PendingNonces) for _, chain := range chains { pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) if !found { return nil, status.Error(codes.Internal, "pending nonces not found") } + pendingNoncesMap[chain.ChainId] = pendingNonces // insert pending nonces and update lowest height if pendingNonces.NonceLow < pendingNonces.NonceHigh { - pendingNoncesMap[chain.ChainId] = &pendingNonces cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, chain.ChainId, pendingNonces.NonceLow) if err != nil { return nil, err @@ -191,12 +191,12 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que // query forwards for pending cctxs for each foreign chain 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) + // query the pending cctxs in range [NonceLow, NonceHigh) for nonce := pendingNonces.NonceLow; nonce < pendingNonces.NonceHigh; nonce++ { cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, chain.ChainId, nonce) if err != nil { diff --git a/x/observer/types/chain_params.go b/x/observer/types/chain_params.go index e32ee9e597..5664decc51 100644 --- a/x/observer/types/chain_params.go +++ b/x/observer/types/chain_params.go @@ -291,11 +291,11 @@ func GetDefaultGoerliLocalnetChainParams() *ChainParams { ConnectorContractAddress: "0xD28D6A0b8189305551a0A8bd247a6ECa9CE781Ca", Erc20CustodyContractAddress: "0xff3135df4F2775f4091b81f4c7B6359CfA07862a", InTxTicker: 2, - OutTxTicker: 2, + OutTxTicker: 1, WatchUtxoTicker: 0, GasPriceTicker: 5, - OutboundTxScheduleInterval: 2, - OutboundTxScheduleLookahead: 5, + OutboundTxScheduleInterval: 1, + OutboundTxScheduleLookahead: 50, BallotThreshold: DefaultBallotThreshold, MinObserverDelegation: DefaultMinObserverDelegation, IsSupported: false, From 4be83fb075a8f2b72a2a53f9a0bfcdf63e2855d7 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 29 Apr 2024 13:07:28 -0500 Subject: [PATCH 30/40] removed incorrect Note --- x/crosschain/keeper/grpc_query_cctx_rate_limit.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 176a3dcee4..fb5779fa5d 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -106,7 +106,6 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que } // query pending nonces for each foreign chain and get the lowest height of the pending cctxs - // Note: The pending nonces could change during the RPC call, so query them beforehand lowestPendingCctxHeight := int64(0) pendingNoncesMap := make(map[int64]observertypes.PendingNonces) for _, chain := range chains { From a450d913c075c3840899b813a0efcd623a34b596 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 2 May 2024 10:49:59 -0500 Subject: [PATCH 31/40] initiated rate limiter refactor and added metrics --- docs/openapi/openapi.swagger.yaml | 52 + pkg/math/float.go | 23 + pkg/math/float_test.go | 54 + proto/crosschain/query.proto | 20 + proto/crosschain/rate_limiter_flags.proto | 12 + testutil/sample/crosschain.go | 40 + typescript/crosschain/query_pb.d.ts | 83 ++ .../crosschain/rate_limiter_flags_pb.d.ts | 45 + x/crosschain/keeper/cctx_utils.go | 12 + x/crosschain/keeper/cctx_utils_test.go | 34 + .../keeper/grpc_query_cctx_rate_limit.go | 223 +++- .../keeper/grpc_query_cctx_rate_limit_test.go | 514 +++++++--- x/crosschain/keeper/rate_limiter_flags.go | 50 +- .../keeper/rate_limiter_flags_test.go | 119 ++- x/crosschain/types/query.pb.go | 955 +++++++++++++++--- x/crosschain/types/query.pb.gw.go | 83 ++ x/crosschain/types/rate_limiter_flags.pb.go | 373 ++++++- zetaclient/core_context/zeta_core_context.go | 12 + .../core_context/zeta_core_context_test.go | 10 + zetaclient/interfaces/interfaces.go | 2 + zetaclient/metrics/metrics.go | 6 + zetaclient/ratelimiter/rate_limiter.go | 139 +++ zetaclient/ratelimiter/rate_limiter_test.go | 265 +++++ zetaclient/testutils/stub/core_bridge.go | 87 +- zetaclient/zetabridge/query.go | 98 +- zetaclient/zetabridge/query_test.go | 48 + zetaclient/zetacore_observer.go | 89 +- zetaclient/zetacore_observer_test.go | 156 ++- 28 files changed, 3121 insertions(+), 483 deletions(-) create mode 100644 pkg/math/float.go create mode 100644 pkg/math/float_test.go create mode 100644 zetaclient/ratelimiter/rate_limiter.go create mode 100644 zetaclient/ratelimiter/rate_limiter_test.go diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 903cbd10b2..c6fc0a05f4 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -27223,6 +27223,32 @@ paths: $ref: '#/definitions/googlerpcStatus' tags: - Query + /zeta-chain/crosschain/rateLimiterInput: + get: + summary: Queries the input data of rate limiter. + operationId: Query_RateLimiterInput + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/crosschainQueryRateLimiterInputResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: limit + in: query + required: false + type: integer + format: int64 + - name: window + in: query + required: false + type: string + format: int64 + tags: + - Query /zeta-chain/crosschain/zetaAccounting: get: operationId: Query_ZetaAccounting @@ -54030,6 +54056,32 @@ definitions: properties: rateLimiterFlags: $ref: '#/definitions/crosschainRateLimiterFlags' + crosschainQueryRateLimiterInputResponse: + type: object + properties: + height: + type: string + format: int64 + cctxs_missed: + type: array + items: + type: object + $ref: '#/definitions/crosschainCrossChainTx' + cctxs_pending: + type: array + items: + type: object + $ref: '#/definitions/crosschainCrossChainTx' + total_pending: + type: string + format: uint64 + past_cctxs_value: + type: string + pending_cctxs_value: + type: string + lowest_pending_cctx_height: + type: string + format: int64 crosschainQueryZetaAccountingResponse: type: object properties: diff --git a/pkg/math/float.go b/pkg/math/float.go new file mode 100644 index 0000000000..bf17753cab --- /dev/null +++ b/pkg/math/float.go @@ -0,0 +1,23 @@ +package math + +import ( + "math/big" +) + +// Percentage calculates the percentage of A over B. +func Percentage(a, b *big.Int) *big.Float { + // if a is zero, return nil to avoid division by zero + if b.Cmp(big.NewInt(0)) == 0 { + return nil + } + + // convert a and a to big.Float + floatA := new(big.Float).SetInt(a) + floatB := new(big.Float).SetInt(b) + + // calculate the percentage of a over b + percentage := new(big.Float).Quo(floatA, floatB) + percentage.Mul(percentage, big.NewFloat(100)) + + return percentage +} diff --git a/pkg/math/float_test.go b/pkg/math/float_test.go new file mode 100644 index 0000000000..1ccdc9d589 --- /dev/null +++ b/pkg/math/float_test.go @@ -0,0 +1,54 @@ +package math + +import ( + "fmt" + "math/big" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPercentage(t *testing.T) { + testCases := []struct { + name string + numerator *big.Int + denominator *big.Int + percentage *big.Float + fail bool + }{ + { + name: "positive percentage", + numerator: big.NewInt(165), + denominator: big.NewInt(1000), + percentage: big.NewFloat(16.5), + fail: false, + }, + { + name: "negative percentage", + numerator: big.NewInt(-165), + denominator: big.NewInt(1000), + percentage: big.NewFloat(-16.5), + fail: false, + }, + { + name: "zero denominator", + numerator: big.NewInt(1), + denominator: big.NewInt(0), + percentage: nil, + fail: true, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + percentage := Percentage(tc.numerator, tc.denominator) + fmt.Printf("percentage: %v\n", percentage) + if tc.fail { + require.Nil(t, percentage) + } else { + require.True(t, percentage.Cmp(tc.percentage) == 0) + } + }) + } +} diff --git a/proto/crosschain/query.proto b/proto/crosschain/query.proto index 07c65e4c31..7cd8ec94c3 100644 --- a/proto/crosschain/query.proto +++ b/proto/crosschain/query.proto @@ -117,6 +117,11 @@ service Query { rpc RateLimiterFlags(QueryRateLimiterFlagsRequest) returns (QueryRateLimiterFlagsResponse) { option (google.api.http).get = "/zeta-chain/crosschain/rateLimiterFlags"; } + + // Queries the input data of rate limiter. + rpc RateLimiterInput(QueryRateLimiterInputRequest) returns (QueryRateLimiterInputResponse) { + option (google.api.http).get = "/zeta-chain/crosschain/rateLimiterInput"; + } } message QueryZetaAccountingRequest {} @@ -263,6 +268,21 @@ message QueryListPendingCctxResponse { uint64 totalPending = 2; } +message QueryRateLimiterInputRequest { + uint32 limit = 1; + int64 window = 2; +} + +message QueryRateLimiterInputResponse { + int64 height = 1; + repeated CrossChainTx cctxs_missed = 2; + repeated CrossChainTx cctxs_pending = 3; + uint64 total_pending = 4; + string past_cctxs_value = 5; + string pending_cctxs_value = 6; + int64 lowest_pending_cctx_height = 7; +} + message QueryListPendingCctxWithinRateLimitRequest { uint32 limit = 1; } diff --git a/proto/crosschain/rate_limiter_flags.proto b/proto/crosschain/rate_limiter_flags.proto index 0e8185713b..cfccc5ab55 100644 --- a/proto/crosschain/rate_limiter_flags.proto +++ b/proto/crosschain/rate_limiter_flags.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package zetachain.zetacore.crosschain; import "gogoproto/gogo.proto"; +import "pkg/coin/coin.proto"; option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types"; @@ -28,3 +29,14 @@ message Conversion { (gogoproto.nullable) = false ]; } + +message AssetRate { + int64 chainId = 1; + string asset = 2; + uint32 decimals = 3; + coin.CoinType coin_type = 4; + string rate = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} diff --git a/testutil/sample/crosschain.go b/testutil/sample/crosschain.go index 5a0d8aafdb..d4a8ff9d03 100644 --- a/testutil/sample/crosschain.go +++ b/testutil/sample/crosschain.go @@ -3,6 +3,7 @@ package sample import ( "encoding/base64" "encoding/json" + "fmt" "math/rand" "testing" @@ -49,6 +50,18 @@ func RateLimiterFlags() types.RateLimiterFlags { } } +func AssetRate() *types.AssetRate { + r := Rand() + + return &types.AssetRate{ + ChainId: r.Int63(), + Asset: EthAddress().Hex(), + Decimals: uint32(r.Uint64()), + CoinType: coin.CoinType_ERC20, + Rate: sdk.NewDec(r.Int63()), + } +} + func OutTxTracker(t *testing.T, index string) types.OutTxTracker { r := newRandFromStringSeed(t, index) @@ -173,6 +186,33 @@ func CrossChainTx(t *testing.T, index string) *types.CrossChainTx { } } +// CustomCctxsInBlockRange create 1 cctx per block in block range [lowBlock, highBlock] (inclusive) +func CustomCctxsInBlockRange( + t *testing.T, + lowBlock uint64, + highBlock uint64, + chainID int64, + coinType coin.CoinType, + asset string, + amount uint64, + status types.CctxStatus, +) (cctxs []*types.CrossChainTx) { + // create 1 cctx per block + for i := lowBlock; i <= highBlock; i++ { + nonce := i - 1 + cctx := CrossChainTx(t, fmt.Sprintf("%d-%d", chainID, nonce)) + cctx.CctxStatus.Status = status + cctx.InboundTxParams.CoinType = coinType + cctx.InboundTxParams.Asset = asset + cctx.InboundTxParams.InboundTxObservedExternalHeight = i + cctx.GetCurrentOutTxParam().ReceiverChainId = chainID + cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(amount) + cctx.GetCurrentOutTxParam().OutboundTxTssNonce = nonce + cctxs = append(cctxs, cctx) + } + return cctxs +} + func LastBlockHeight(t *testing.T, index string) *types.LastBlockHeight { r := newRandFromStringSeed(t, index) diff --git a/typescript/crosschain/query_pb.d.ts b/typescript/crosschain/query_pb.d.ts index 19877c30bd..5fcc90d640 100644 --- a/typescript/crosschain/query_pb.d.ts +++ b/typescript/crosschain/query_pb.d.ts @@ -871,6 +871,89 @@ export declare class QueryListPendingCctxResponse extends Message | undefined, b: QueryListPendingCctxResponse | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.crosschain.QueryRateLimiterInputRequest + */ +export declare class QueryRateLimiterInputRequest extends Message { + /** + * @generated from field: uint32 limit = 1; + */ + limit: number; + + /** + * @generated from field: int64 window = 2; + */ + window: bigint; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.QueryRateLimiterInputRequest"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryRateLimiterInputRequest; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryRateLimiterInputRequest; + + static fromJsonString(jsonString: string, options?: Partial): QueryRateLimiterInputRequest; + + static equals(a: QueryRateLimiterInputRequest | PlainMessage | undefined, b: QueryRateLimiterInputRequest | PlainMessage | undefined): boolean; +} + +/** + * @generated from message zetachain.zetacore.crosschain.QueryRateLimiterInputResponse + */ +export declare class QueryRateLimiterInputResponse extends Message { + /** + * @generated from field: int64 height = 1; + */ + height: bigint; + + /** + * @generated from field: repeated zetachain.zetacore.crosschain.CrossChainTx cctxs_missed = 2; + */ + cctxsMissed: CrossChainTx[]; + + /** + * @generated from field: repeated zetachain.zetacore.crosschain.CrossChainTx cctxs_pending = 3; + */ + cctxsPending: CrossChainTx[]; + + /** + * @generated from field: uint64 total_pending = 4; + */ + totalPending: bigint; + + /** + * @generated from field: string past_cctxs_value = 5; + */ + pastCctxsValue: string; + + /** + * @generated from field: string pending_cctxs_value = 6; + */ + pendingCctxsValue: string; + + /** + * @generated from field: int64 lowest_pending_cctx_height = 7; + */ + lowestPendingCctxHeight: bigint; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.QueryRateLimiterInputResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryRateLimiterInputResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryRateLimiterInputResponse; + + static fromJsonString(jsonString: string, options?: Partial): QueryRateLimiterInputResponse; + + static equals(a: QueryRateLimiterInputResponse | PlainMessage | undefined, b: QueryRateLimiterInputResponse | PlainMessage | undefined): boolean; +} + /** * @generated from message zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitRequest */ diff --git a/typescript/crosschain/rate_limiter_flags_pb.d.ts b/typescript/crosschain/rate_limiter_flags_pb.d.ts index 02997cd099..121c55255a 100644 --- a/typescript/crosschain/rate_limiter_flags_pb.d.ts +++ b/typescript/crosschain/rate_limiter_flags_pb.d.ts @@ -5,6 +5,7 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; +import type { CoinType } from "../pkg/coin/coin_pb.js"; /** * @generated from message zetachain.zetacore.crosschain.RateLimiterFlags @@ -80,3 +81,47 @@ export declare class Conversion extends Message { static equals(a: Conversion | PlainMessage | undefined, b: Conversion | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.crosschain.AssetRate + */ +export declare class AssetRate extends Message { + /** + * @generated from field: int64 chainId = 1; + */ + chainId: bigint; + + /** + * @generated from field: string asset = 2; + */ + asset: string; + + /** + * @generated from field: uint32 decimals = 3; + */ + decimals: number; + + /** + * @generated from field: coin.CoinType coin_type = 4; + */ + coinType: CoinType; + + /** + * @generated from field: string rate = 5; + */ + rate: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.AssetRate"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): AssetRate; + + static fromJson(jsonValue: JsonValue, options?: Partial): AssetRate; + + static fromJsonString(jsonString: string, options?: Partial): AssetRate; + + static equals(a: AssetRate | PlainMessage | undefined, b: AssetRate | PlainMessage | undefined): boolean; +} + diff --git a/x/crosschain/keeper/cctx_utils.go b/x/crosschain/keeper/cctx_utils.go index 6f9651967a..591f131c16 100644 --- a/x/crosschain/keeper/cctx_utils.go +++ b/x/crosschain/keeper/cctx_utils.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + "sort" cosmoserrors "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" @@ -105,3 +106,14 @@ func GetAbortedAmount(cctx types.CrossChainTx) sdkmath.Uint { return sdkmath.ZeroUint() } + +// SortCctxsByHeightAndChainID sorts the cctxs by height (first come first serve), the chain ID doesn't really matter +func SortCctxsByHeightAndChainID(cctxs []*types.CrossChainTx) []*types.CrossChainTx { + sort.Slice(cctxs, func(i, j int) bool { + if cctxs[i].InboundTxParams.InboundTxObservedExternalHeight == cctxs[j].InboundTxParams.InboundTxObservedExternalHeight { + return cctxs[i].GetCurrentOutTxParam().ReceiverChainId < cctxs[j].GetCurrentOutTxParam().ReceiverChainId + } + return cctxs[i].InboundTxParams.InboundTxObservedExternalHeight < cctxs[j].InboundTxParams.InboundTxObservedExternalHeight + }) + return cctxs +} diff --git a/x/crosschain/keeper/cctx_utils_test.go b/x/crosschain/keeper/cctx_utils_test.go index af6445744c..a268c20b78 100644 --- a/x/crosschain/keeper/cctx_utils_test.go +++ b/x/crosschain/keeper/cctx_utils_test.go @@ -12,6 +12,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/keeper" crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" @@ -374,3 +375,36 @@ func TestKeeper_UpdateNonce(t *testing.T) { require.NoError(t, err) }) } + +func TestKeeper_SortCctxsByHeightAndChainId(t *testing.T) { + // cctx1 + cctx1 := sample.CrossChainTx(t, "1-1") + cctx1.GetCurrentOutTxParam().ReceiverChainId = 1 + cctx1.InboundTxParams.InboundTxObservedExternalHeight = 10 + + // cctx2 + cctx2 := sample.CrossChainTx(t, "1-2") + cctx2.GetCurrentOutTxParam().ReceiverChainId = 1 + cctx2.InboundTxParams.InboundTxObservedExternalHeight = 13 + + // cctx3 + cctx3 := sample.CrossChainTx(t, "56-1") + cctx3.GetCurrentOutTxParam().ReceiverChainId = 56 + cctx3.InboundTxParams.InboundTxObservedExternalHeight = 13 + + // cctx4 + cctx4 := sample.CrossChainTx(t, "56-2") + cctx4.GetCurrentOutTxParam().ReceiverChainId = 56 + cctx4.InboundTxParams.InboundTxObservedExternalHeight = 16 + + // sort by height + cctxs := []*types.CrossChainTx{cctx1, cctx2, cctx3, cctx4} + keeper.SortCctxsByHeightAndChainID(cctxs) + + // check order + require.Len(t, cctxs, 4) + require.Equal(t, cctx1, cctxs[0]) + require.Equal(t, cctx2, cctxs[1]) + require.Equal(t, cctx3, cctxs[2]) + require.Equal(t, cctx4, cctxs[3]) +} diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index fb5779fa5d..0f313897ca 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -9,12 +9,165 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" - fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) +// RateLimiterInput returns the input data for the rate limiter +func (k Keeper) RateLimiterInput(c context.Context, req *types.QueryRateLimiterInputRequest) (res *types.QueryRateLimiterInputResponse, err error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + if req.Window <= 0 { + return nil, status.Error(codes.InvalidArgument, "window must be positive") + } + + // use default MaxPendingCctxs if not specified or too high + limit := req.Limit + if limit == 0 || limit > MaxPendingCctxs { + limit = MaxPendingCctxs + } + ctx := sdk.UnwrapSDKContext(c) + + // get current height and tss + height := ctx.BlockHeight() + if height <= 0 { + return nil, status.Error(codes.OutOfRange, "height out of range") + } + tss, found := k.zetaObserverKeeper.GetTSS(ctx) + if !found { + return nil, observertypes.ErrTssNotFound + } + + // calculate the rate limiter sliding window left boundary (inclusive) + leftWindowBoundary := height - req.Window + 1 + if leftWindowBoundary < 1 { + leftWindowBoundary = 1 + } + + // the `limit` of pending result is reached or not + maxCCTXsReached := func(cctxs []*types.CrossChainTx) bool { + // #nosec G701 len always positive + return uint32(len(cctxs)) > limit + } + + // if a cctx falls within the rate limiter window + isCctxInWindow := func(cctx *types.CrossChainTx) bool { + // #nosec G701 checked positive + return cctx.InboundTxParams.InboundTxObservedExternalHeight >= uint64(leftWindowBoundary) + } + + // it is a past cctx if its nonce < `nonceLow`, + isPastCctx := func(cctx *types.CrossChainTx, nonceLow int64) bool { + // #nosec G701 always positive + return cctx.GetCurrentOutTxParam().OutboundTxTssNonce < uint64(nonceLow) + } + + // get foreign chains and conversion rates of foreign coins + chains := k.zetaObserverKeeper.GetSupportedForeignChains(ctx) + assetRates := k.GetRateLimiterAssetRateList(ctx) + gasAssetRateMap, erc20AssetRateMap := BuildAssetRateMapFromList(assetRates) + + // query pending nonces of each foreign chain and get the lowest height of the pending cctxs + lowestPendingCctxHeight := int64(0) + pendingNoncesMap := make(map[int64]observertypes.PendingNonces) + for _, chain := range chains { + pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, chain.ChainId) + if !found { + return nil, status.Error(codes.Internal, "pending nonces not found") + } + pendingNoncesMap[chain.ChainId] = pendingNonces + + // update lowest pending cctx height + if pendingNonces.NonceLow < pendingNonces.NonceHigh { + cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, chain.ChainId, pendingNonces.NonceLow) + if err != nil { + return nil, err + } + // #nosec G701 len always in range + cctxHeight := int64(cctx.InboundTxParams.InboundTxObservedExternalHeight) + if lowestPendingCctxHeight == 0 || cctxHeight < lowestPendingCctxHeight { + lowestPendingCctxHeight = cctxHeight + } + } + } + + // define a few variables to be used in the query loops + totalPending := uint64(0) + pastCctxsValue := sdk.NewInt(0) + pendingCctxsValue := sdk.NewInt(0) + cctxsMissed := make([]*types.CrossChainTx, 0) + cctxsPending := make([]*types.CrossChainTx, 0) + + // query backwards for pending cctxs of each foreign chain + 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 + // therefore might mask some lower nonce cctx that is still pending. + pendingNonces := pendingNoncesMap[chain.ChainId] + startNonce := pendingNonces.NonceHigh - 1 + endNonce := pendingNonces.NonceLow - MaxLookbackNonce + if endNonce < 0 { + endNonce = 0 + } + + // go all the way back to the left window boundary or `NonceLow - 1000`, depending on which on arrives first + for nonce := startNonce; nonce >= 0; nonce-- { + cctx, err := getCctxByChainIDAndNonce(k, ctx, tss.TssPubkey, chain.ChainId, nonce) + if err != nil { + return nil, err + } + inWindow := isCctxInWindow(cctx) + isPast := isPastCctx(cctx, pendingNonces.NonceLow) + + // we should at least go backwards by 1000 nonces to pick up missed pending cctxs + // we might go even further back if the endNonce hasn't hit the left window boundary yet + if nonce < endNonce && !inWindow { + break + } + + // sum up past cctxs' value within window + if inWindow && isPast { + pastCctxsValue = pastCctxsValue.Add(ConvertCctxValue(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) + } + + // add cctx to corresponding list + if IsPending(cctx) { + totalPending++ + if isPast { + cctxsMissed = append(cctxsMissed, cctx) + } else { + cctxsPending = append(cctxsPending, cctx) + // sum up non-past pending cctxs' value + pendingCctxsValue = pendingCctxsValue.Add(ConvertCctxValue(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) + } + } + } + } + + // sort the missed cctxs order by height (can sort by other criteria, for unit testability) + SortCctxsByHeightAndChainID(cctxsMissed) + + // sort the pending cctxs order by height (first come first serve) + SortCctxsByHeightAndChainID(cctxsPending) + + // we take all the missed cctxs (won't be a lot) for simplicity of the query, but we only take a `limit` number of pending cctxs + if maxCCTXsReached(cctxsPending) { + cctxsPending = cctxsPending[:limit] + } + + return &types.QueryRateLimiterInputResponse{ + Height: height, + CctxsMissed: cctxsMissed, + CctxsPending: cctxsPending, + TotalPending: totalPending, + PastCctxsValue: pastCctxsValue.String(), + PendingCctxsValue: pendingCctxsValue.String(), + LowestPendingCctxHeight: lowestPendingCctxHeight, + }, nil +} + // ListPendingCctxWithinRateLimit returns a list of pending cctxs that do not exceed the outbound rate limit // a limit for the number of cctxs to return can be specified or the default is MaxPendingCctxs func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.QueryListPendingCctxWithinRateLimitRequest) (res *types.QueryListPendingCctxWithinRateLimitResponse, err error) { @@ -79,14 +232,13 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que } // get the conversion rates for all foreign coins - var gasCoinRates map[int64]sdk.Dec - var erc20CoinRates map[int64]map[string]sdk.Dec - var foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins + var gasAssetRateMap map[int64]*types.AssetRate + var erc20AssetRateMap map[int64]map[string]*types.AssetRate var blockLimitInAzeta sdkmath.Int var windowLimitInAzeta sdkmath.Int if applyLimit { - gasCoinRates, erc20CoinRates = k.GetRateLimiterRates(ctx) - foreignCoinMap = k.fungibleKeeper.GetAllForeignCoinMap(ctx) + assetRates := k.GetRateLimiterAssetRateList(ctx) + gasAssetRateMap, erc20AssetRateMap = BuildAssetRateMapFromList(assetRates) // initiate block limit and window limit in azeta blockLimitInAzeta = sdkmath.NewIntFromBigInt(rateLimitFlags.Rate.BigInt()) @@ -170,7 +322,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que break } // skip the cctx if rate limit is exceeded but still accumulate the total withdraw value - if inWindow && rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { + if inWindow && rateLimitExceeded(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { limitExceeded = true continue } @@ -203,7 +355,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que } // skip the cctx if rate limit is exceeded but still accumulate the total withdraw value - if rateLimitExceeded(chain.ChainId, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { + if rateLimitExceeded(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { limitExceeded = true continue } @@ -241,52 +393,46 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que func ConvertCctxValue( chainID int64, cctx *types.CrossChainTx, - gasCoinRates map[int64]sdk.Dec, - erc20CoinRates map[int64]map[string]sdk.Dec, - foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins, + gasAssetRateMap map[int64]*types.AssetRate, + erc20AssetRateMap map[int64]map[string]*types.AssetRate, ) sdkmath.Int { var rate sdk.Dec - var decimals uint64 + var decimals uint32 switch cctx.InboundTxParams.CoinType { case coin.CoinType_Zeta: // no conversion needed for ZETA - return sdkmath.NewIntFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) + return sdk.NewIntFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) case coin.CoinType_Gas: - rate = gasCoinRates[chainID] + assetRate, found := gasAssetRateMap[chainID] + if !found { + // skip if no rate found for gas coin on this chainID + return sdk.NewInt(0) + } + rate = assetRate.Rate + decimals = assetRate.Decimals case coin.CoinType_ERC20: // get the ERC20 coin rate - _, found := erc20CoinRates[chainID] + _, found := erc20AssetRateMap[chainID] if !found { // skip if no rate found for this chainID - return sdkmath.NewInt(0) + return sdk.NewInt(0) } - rate = erc20CoinRates[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] + assetRate := erc20AssetRateMap[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] + rate = assetRate.Rate + decimals = assetRate.Decimals default: // skip CoinType_Cmd - return sdkmath.NewInt(0) + return sdk.NewInt(0) } // should not happen, return 0 to skip if it happens if rate.IsNil() || rate.LTE(sdk.NewDec(0)) { return sdkmath.NewInt(0) } - // get foreign coin decimals - foreignCoinFromChainMap, found := foreignCoinMap[chainID] - if !found { - // skip if no coin found for this chainID - return sdkmath.NewInt(0) - } - foreignCoin, found := foreignCoinFromChainMap[strings.ToLower(cctx.InboundTxParams.Asset)] - if !found { - // skip if no coin found for this Asset - return sdkmath.NewInt(0) - } - decimals = uint64(foreignCoin.Decimals) - // the whole coin amounts of zeta and zrc20 // given decimals = 6, the amount will be 10^6 = 1000000 oneZeta := coin.AzetaPerZeta() - oneZrc20 := sdk.NewDec(10).Power(decimals) + oneZrc20 := sdk.NewDec(10).Power(uint64(decimals)) // convert cctx asset amount into azeta amount // given amountCctx = 2000000, rate = 0.8, decimals = 6 @@ -302,13 +448,12 @@ func ConvertCctxValue( func rateLimitExceeded( chainID int64, cctx *types.CrossChainTx, - gasCoinRates map[int64]sdk.Dec, - erc20CoinRates map[int64]map[string]sdk.Dec, - foreignCoinMap map[int64]map[string]fungibletypes.ForeignCoins, + gasAssetRateMap map[int64]*types.AssetRate, + erc20AssetRateMap map[int64]map[string]*types.AssetRate, currentCctxValue *sdkmath.Int, - withdrawLimitInZeta sdkmath.Int, + withdrawLimitInAzeta sdkmath.Int, ) bool { - cctxValueAzeta := ConvertCctxValue(chainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) - *currentCctxValue = currentCctxValue.Add(cctxValueAzeta) - return currentCctxValue.GT(withdrawLimitInZeta) + amountZeta := ConvertCctxValue(chainID, cctx, gasAssetRateMap, erc20AssetRateMap) + *currentCctxValue = currentCctxValue.Add(amountZeta) + return currentCctxValue.GT(withdrawLimitInAzeta) } 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 6418e57c03..90e9ed830e 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -6,6 +6,7 @@ import ( "testing" "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/coin" @@ -59,37 +60,6 @@ func createTestRateLimiterFlags( } } -// createCctxsWithCoinTypeAndHeightRange -// - create 1 cctx per block from lowBlock to highBlock (inclusive) -// -// return created cctxs -func createCctxsWithCoinTypeAndHeightRange( - t *testing.T, - lowBlock uint64, - highBlock uint64, - chainID int64, - coinType coin.CoinType, - asset string, - amount uint64, - status types.CctxStatus, -) (cctxs []*types.CrossChainTx) { - // create 1 pending cctxs per block - for i := lowBlock; i <= highBlock; i++ { - nonce := i - 1 - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", chainID, nonce)) - cctx.CctxStatus.Status = status - cctx.InboundTxParams.SenderChainId = chainID - cctx.InboundTxParams.CoinType = coinType - cctx.InboundTxParams.Asset = asset - cctx.InboundTxParams.InboundTxObservedExternalHeight = i - cctx.GetCurrentOutTxParam().ReceiverChainId = chainID - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(amount) - cctx.GetCurrentOutTxParam().OutboundTxTssNonce = nonce - cctxs = append(cctxs, cctx) - } - return cctxs -} - // setCctxsInKeeper sets the given cctxs to the keeper func setCctxsInKeeper( ctx sdk.Context, @@ -101,7 +71,7 @@ func setCctxsInKeeper( for _, cctx := range cctxs { k.SetCrossChainTx(ctx, *cctx) zk.ObserverKeeper.SetNonceToCctx(ctx, observertypes.NonceToCctx{ - ChainId: cctx.InboundTxParams.SenderChainId, + ChainId: cctx.GetCurrentOutTxParam().ReceiverChainId, // #nosec G701 always in range for tests Nonce: int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce), CctxIndex: cctx.Index, @@ -149,119 +119,365 @@ func Test_ConvertCctxValue(t *testing.T) { k.SetRateLimiterFlags(ctx, *rateLimiterFlags) // get rate limiter rates - gasCoinRates, erc20CoinRates := k.GetRateLimiterRates(ctx) - foreignCoinMap := zk.FungibleKeeper.GetAllForeignCoinMap(ctx) - - t.Run("should convert cctx ZETA value correctly", func(t *testing.T) { - // create cctx with 0.3 ZETA - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) - cctx.InboundTxParams.CoinType = coin.CoinType_Zeta - cctx.InboundTxParams.Asset = "" - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(3e17) // 0.3 ZETA - - // convert cctx value - value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewInt(3e17), value) - }) - t.Run("should convert cctx ETH value correctly", func(t *testing.T) { - // create cctx with 0.003 ETH - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) - cctx.InboundTxParams.CoinType = coin.CoinType_Gas - cctx.InboundTxParams.Asset = "" - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(3e15) // 0.003 ETH - - // convert cctx value: 0.003 ETH * 2500 = 7.5 ZETA - value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewInt(75e17), value) - }) - t.Run("should convert cctx BTC value correctly", func(t *testing.T) { - // create cctx with 0.0007 BTC - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", btcChainID, 1)) - cctx.InboundTxParams.CoinType = coin.CoinType_Gas - cctx.InboundTxParams.Asset = "" - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(70000) // 0.0007 BTC - - // convert cctx value: 0.0007 BTC * 50000 = 35.0 ZETA - value := keeper.ConvertCctxValue(btcChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewInt(35).Mul(sdk.NewInt(1e18)), value) - }) - t.Run("should convert cctx USDT value correctly", func(t *testing.T) { - // create cctx with 3 USDT - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) - cctx.InboundTxParams.CoinType = coin.CoinType_ERC20 - cctx.InboundTxParams.Asset = assetUSDT - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(3e6) // 3 USDT - - // convert cctx value: 3 USDT * 0.8 = 2.4 ZETA - value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewInt(24e17), value) - }) - t.Run("should return 0 if no rate found for chainID", func(t *testing.T) { - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) - cctx.InboundTxParams.CoinType = coin.CoinType_ERC20 - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) - - // use nil erc20CoinRates map to convert cctx value - value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, nil, foreignCoinMap) - require.Equal(t, sdk.NewInt(0), value) - }) - t.Run("should return 0 if coinType is CoinType_Cmd", func(t *testing.T) { - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) - cctx.InboundTxParams.CoinType = coin.CoinType_Cmd - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) - - // convert cctx value - value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewInt(0), value) - }) - t.Run("should return 0 on nil rate or rate <= 0", func(t *testing.T) { - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) - cctx.InboundTxParams.CoinType = coin.CoinType_Gas - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) - - // use nil gasCoinRates map to convert cctx value - value := keeper.ConvertCctxValue(ethChainID, cctx, nil, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewInt(0), value) - - // set rate to 0 - zeroCoinRates, _ := k.GetRateLimiterRates(ctx) - zeroCoinRates[ethChainID] = sdk.NewDec(0) - - // convert cctx value - value = keeper.ConvertCctxValue(ethChainID, cctx, zeroCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewInt(0), value) - - // set rate to -1 - negativeCoinRates, _ := k.GetRateLimiterRates(ctx) - negativeCoinRates[ethChainID] = sdk.NewDec(-1) - - // convert cctx value - value = keeper.ConvertCctxValue(ethChainID, cctx, negativeCoinRates, erc20CoinRates, foreignCoinMap) - require.Equal(t, sdk.NewInt(0), value) - }) - t.Run("should return 0 if no coin found for chainID", func(t *testing.T) { - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) - cctx.InboundTxParams.CoinType = coin.CoinType_Gas - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) - - // use empty foreignCoinMap to convert cctx value - value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, nil) - require.Equal(t, sdk.NewInt(0), value) - }) - t.Run("should return 0 if no coin found for asset", func(t *testing.T) { - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", ethChainID, 1)) - cctx.InboundTxParams.CoinType = coin.CoinType_ERC20 - cctx.InboundTxParams.Asset = assetUSDT - cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(100) - - // delete assetUSDT from foreignCoinMap for ethChainID - tempCoinMap := zk.FungibleKeeper.GetAllForeignCoinMap(ctx) - delete(tempCoinMap[ethChainID], strings.ToLower(assetUSDT)) - - // convert cctx value - value := keeper.ConvertCctxValue(ethChainID, cctx, gasCoinRates, erc20CoinRates, tempCoinMap) - require.Equal(t, sdk.NewInt(0), value) - }) + assetRates := k.GetRateLimiterAssetRateList(ctx) + gasAssetRateMap, erc20AssetRateMap := keeper.BuildAssetRateMapFromList(assetRates) + + // test cases + tests := []struct { + name string + + // input + chainID int64 + coinType coin.CoinType + asset string + amount math.Uint + gasAssetRates map[int64]*types.AssetRate + erc20AssetRates map[int64]map[string]*types.AssetRate + + // output + expectedValue sdkmath.Int + }{ + { + name: "should convert cctx ZETA value correctly", + chainID: ethChainID, + coinType: coin.CoinType_Zeta, + asset: "", + amount: sdk.NewUint(3e17), // 0.3 ZETA + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(3e17), + }, + { + name: "should convert cctx ETH value correctly", + chainID: ethChainID, + coinType: coin.CoinType_Gas, + asset: "", + amount: sdk.NewUint(3e15), // 0.003 ETH + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(75e17), // 0.003 ETH * 2500 = 7.5 ZETA + }, + { + name: "should convert cctx BTC value correctly", + chainID: btcChainID, + coinType: coin.CoinType_Gas, + asset: "", + amount: sdk.NewUint(70000), // 0.0007 BTC + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(35).Mul(sdk.NewInt(1e18)), // 0.0007 BTC * 50000 = 35.0 ZETA + }, + { + name: "should convert cctx USDT value correctly", + chainID: ethChainID, + coinType: coin.CoinType_ERC20, + asset: assetUSDT, + amount: sdk.NewUint(3e6), // 3 USDT + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(24e17), // 3 USDT * 0.8 = 2.4 ZETA + }, + { + name: "should return 0 if no gas asset rate found for chainID", + chainID: ethChainID, + coinType: coin.CoinType_Gas, + asset: "", + amount: sdk.NewUint(100), + gasAssetRates: nil, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(0), + }, + { + name: "should return 0 if no erc20 asset rate found for chainID", + chainID: ethChainID, + coinType: coin.CoinType_ERC20, + asset: assetUSDT, + amount: sdk.NewUint(100), + gasAssetRates: gasAssetRateMap, + erc20AssetRates: nil, + expectedValue: sdk.NewInt(0), + }, + { + name: "should return 0 if coinType is CoinType_Cmd", + chainID: ethChainID, + coinType: coin.CoinType_Cmd, + asset: "", + amount: sdk.NewUint(100), + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(0), + }, + { + name: "should return 0 on nil rate", + chainID: ethChainID, + coinType: coin.CoinType_Gas, + asset: "", + amount: sdk.NewUint(100), + gasAssetRates: func() map[int64]*types.AssetRate { + // set rate to nil + tempRates := k.GetRateLimiterAssetRateList(ctx) + nilAssetRateMap, _ := keeper.BuildAssetRateMapFromList(tempRates) + nilAssetRateMap[ethChainID].Rate = sdk.Dec{} + return nilAssetRateMap + }(), + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(0), + }, + { + name: "should return 0 on rate <= 0", + chainID: ethChainID, + coinType: coin.CoinType_ERC20, + asset: assetUSDT, + amount: sdk.NewUint(100), + gasAssetRates: gasAssetRateMap, + erc20AssetRates: func() map[int64]map[string]*types.AssetRate { + // set rate to 0 + tempRates := k.GetRateLimiterAssetRateList(ctx) + _, zeroAssetRateMap := keeper.BuildAssetRateMapFromList(tempRates) + zeroAssetRateMap[ethChainID][strings.ToLower(assetUSDT)].Rate = sdk.NewDec(0) + return zeroAssetRateMap + }(), + expectedValue: sdk.NewInt(0), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create cctx with given input + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", tt.chainID, 1)) + cctx.InboundTxParams.CoinType = tt.coinType + cctx.InboundTxParams.Asset = tt.asset + cctx.GetCurrentOutTxParam().Amount = tt.amount + + // convert cctx value + value := keeper.ConvertCctxValue(tt.chainID, cctx, tt.gasAssetRates, tt.erc20AssetRates) + require.Equal(t, tt.expectedValue, value) + }) + } +} + +func TestKeeper_RateLimiterInput(t *testing.T) { + // create sample TSS + tss := sample.Tss() + + // create sample zrc20 addresses for ETH, BTC, USDT + zrc20ETH := sample.EthAddress().Hex() + zrc20BTC := sample.EthAddress().Hex() + zrc20USDT := sample.EthAddress().Hex() + + // 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 := sample.CustomCctxsInBlockRange(t, 1, 999, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_OutboundMined) + ethPendingCctxs := sample.CustomCctxsInBlockRange(t, 1000, 1199, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_PendingOutbound) + + // 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 := sample.CustomCctxsInBlockRange(t, 1, 999, btcChainID, coin.CoinType_Gas, "", 1000, types.CctxStatus_OutboundMined) + btcPendingCctxs := sample.CustomCctxsInBlockRange(t, 1000, 1199, btcChainID, coin.CoinType_Gas, "", 1000, types.CctxStatus_PendingOutbound) + + // define test cases + tests := []struct { + name string + rateLimitFlags *types.RateLimiterFlags + + // Eth chain cctxs setup + ethMinedCctxs []*types.CrossChainTx + ethPendingCctxs []*types.CrossChainTx + ethPendingNonces observertypes.PendingNonces + + // Btc chain cctxs setup + btcMinedCctxs []*types.CrossChainTx + btcPendingCctxs []*types.CrossChainTx + btcPendingNonces observertypes.PendingNonces + + // block height and limit of cctxs to retrieve + currentHeight int64 + queryLimit uint32 + + // expected results + expectedHeight int64 + expectedCctxsMissed []*types.CrossChainTx + expectedCctxsPending []*types.CrossChainTx + expectedTotalPending uint64 + expectedPastCctxsValue string + expectedPendingCctxsValue string + expectedLowestPendingCctxHeight int64 + }{ + { + name: "can retrieve all pending cctxs", + 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: 0, // use default MaxPendingCctxs + + // expected results + expectedHeight: 1199, + expectedCctxsMissed: keeper.SortCctxsByHeightAndChainID(append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...)), + expectedCctxsPending: keeper.SortCctxsByHeightAndChainID(append(append([]*types.CrossChainTx{}, ethPendingCctxs[100:200]...), btcPendingCctxs[100:200]...)), + expectedTotalPending: 400, + expectedPastCctxsValue: sdk.NewInt(1200).Mul(sdk.NewInt(1e18)).String(), // 400 * (2.5 + 0.5) ZETA + expectedPendingCctxsValue: sdk.NewInt(300).Mul(sdk.NewInt(1e18)).String(), // 100 * 1e15 ZETA + expectedLowestPendingCctxHeight: 1100, + }, + { + name: "should use left window boundary 1 if window > currentHeight", + rateLimitFlags: createTestRateLimiterFlags(1200, 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, // window 1200 > 1199 + queryLimit: keeper.MaxPendingCctxs + 1, // should use default MaxPendingCctxs + + // expected results + expectedHeight: 1199, + expectedCctxsMissed: keeper.SortCctxsByHeightAndChainID(append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...)), + expectedCctxsPending: keeper.SortCctxsByHeightAndChainID(append(append([]*types.CrossChainTx{}, ethPendingCctxs[100:200]...), btcPendingCctxs[100:200]...)), + expectedTotalPending: 400, + expectedPastCctxsValue: sdk.NewInt(3297).Mul(sdk.NewInt(1e18)).String(), // 1099 * (2.5 + 0.5) ZETA + expectedPendingCctxsValue: sdk.NewInt(300).Mul(sdk.NewInt(1e18)).String(), // 100 * 1e15 ZETA + expectedLowestPendingCctxHeight: 1100, + }, + { + name: "should loop from nonce 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, // startNonce will be set to 0 (NonceLow - 1000 < 0) + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + btcMinedCctxs: btcMinedCctxs, + btcPendingCctxs: btcPendingCctxs, + btcPendingNonces: observertypes.PendingNonces{ + ChainId: btcChainID, + NonceLow: 999, // startNonce will be set to 0 (NonceLow - 1000 < 0) + NonceHigh: 1199, + Tss: tss.TssPubkey, + }, + currentHeight: 1199, + queryLimit: keeper.MaxPendingCctxs, + + // expected results + expectedHeight: 1199, + expectedCctxsMissed: []*types.CrossChainTx{}, // no missed cctxs + expectedCctxsPending: keeper.SortCctxsByHeightAndChainID(append(append([]*types.CrossChainTx{}, ethPendingCctxs...), btcPendingCctxs...)), + expectedTotalPending: 400, + expectedPastCctxsValue: sdk.NewInt(900).Mul(sdk.NewInt(1e18)).String(), // 300 * (2.5 + 0.5) ZETA + expectedPendingCctxsValue: sdk.NewInt(600).Mul(sdk.NewInt(1e18)).String(), // 200 * (2.5 + 0.5) ZETA + expectedLowestPendingCctxHeight: 1000, + }, + { + name: "set a lower gRPC request limit < len(pending_cctxs)", + 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: 100, // 100 < keeper.MaxPendingCctxs + + // expected results + expectedHeight: 1199, + // should include all missed and 50 pending cctxs for each chain + expectedCctxsMissed: keeper.SortCctxsByHeightAndChainID(append(append([]*types.CrossChainTx{}, ethPendingCctxs[0:100]...), btcPendingCctxs[0:100]...)), + expectedCctxsPending: keeper.SortCctxsByHeightAndChainID(append(append([]*types.CrossChainTx{}, ethPendingCctxs[100:150]...), btcPendingCctxs[100:150]...)), + expectedTotalPending: 400, + expectedPastCctxsValue: sdk.NewInt(1200).Mul(sdk.NewInt(1e18)).String(), // 400 * (2.5 + 0.5) ZETA + expectedPendingCctxsValue: sdk.NewInt(300).Mul(sdk.NewInt(1e18)).String(), // 100 * (2.5 + 0.5) ZETA + expectedLowestPendingCctxHeight: 1100, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create test keepers + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // Set TSS + zk.ObserverKeeper.SetTSS(ctx, tss) + + // Set foreign coins + assetUSDT := sample.EthAddress().Hex() + setupForeignCoins(t, ctx, zk, zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT) + + // Set rate limiter flags + if tt.rateLimitFlags != nil { + k.SetRateLimiterFlags(ctx, *tt.rateLimitFlags) + } + + // Set Eth chain mined cctxs, pending ccxts and pending nonces + setCctxsInKeeper(ctx, *k, zk, tss, tt.ethMinedCctxs) + setCctxsInKeeper(ctx, *k, zk, tss, tt.ethPendingCctxs) + zk.ObserverKeeper.SetPendingNonces(ctx, tt.ethPendingNonces) + + // Set Btc chain mined cctxs, pending ccxts and pending nonces + setCctxsInKeeper(ctx, *k, zk, tss, tt.btcMinedCctxs) + setCctxsInKeeper(ctx, *k, zk, tss, tt.btcPendingCctxs) + zk.ObserverKeeper.SetPendingNonces(ctx, tt.btcPendingNonces) + + // Set current block height + ctx = ctx.WithBlockHeight(tt.currentHeight) + + // Query input data for the rate limiter + request := &types.QueryRateLimiterInputRequest{ + Limit: tt.queryLimit, + Window: tt.rateLimitFlags.Window, + } + res, err := k.RateLimiterInput(ctx, request) + + // check results + require.NoError(t, err) + require.Equal(t, tt.expectedHeight, res.Height) + require.Equal(t, tt.expectedCctxsMissed, res.CctxsMissed) + require.Equal(t, tt.expectedCctxsPending, res.CctxsPending) + require.Equal(t, tt.expectedTotalPending, res.TotalPending) + require.Equal(t, tt.expectedPastCctxsValue, res.PastCctxsValue) + require.Equal(t, tt.expectedPendingCctxsValue, res.PendingCctxsValue) + require.Equal(t, tt.expectedLowestPendingCctxHeight, res.LowestPendingCctxHeight) + }) + } } func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { @@ -275,13 +491,13 @@ func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { // 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) + ethMinedCctxs := sample.CustomCctxsInBlockRange(t, 1, 999, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_OutboundMined) + ethPendingCctxs := sample.CustomCctxsInBlockRange(t, 1000, 1199, ethChainID, coin.CoinType_Gas, "", uint64(1e15), types.CctxStatus_PendingOutbound) // 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) + btcMinedCctxs := sample.CustomCctxsInBlockRange(t, 1, 999, btcChainID, coin.CoinType_Gas, "", 1000, types.CctxStatus_OutboundMined) + btcPendingCctxs := sample.CustomCctxsInBlockRange(t, 1000, 1199, btcChainID, coin.CoinType_Gas, "", 1000, types.CctxStatus_PendingOutbound) // define test cases tests := []struct { diff --git a/x/crosschain/keeper/rate_limiter_flags.go b/x/crosschain/keeper/rate_limiter_flags.go index 324c636f93..e8287e17f0 100644 --- a/x/crosschain/keeper/rate_limiter_flags.go +++ b/x/crosschain/keeper/rate_limiter_flags.go @@ -29,15 +29,12 @@ func (k Keeper) GetRateLimiterFlags(ctx sdk.Context) (val types.RateLimiterFlags return val, true } -// GetRateLimiterRates returns two maps of foreign coins and their rates -// The 1st map: foreign chain id -> gas coin rate -// The 2nd map: foreign chain id -> erc20 asset -> erc20 coin rate -func (k Keeper) GetRateLimiterRates(ctx sdk.Context) (map[int64]sdk.Dec, map[int64]map[string]sdk.Dec) { +// GetRateLimiterAssetRateList returns a list of all foreign asset rate +func (k Keeper) GetRateLimiterAssetRateList(ctx sdk.Context) []*types.AssetRate { rateLimiterFlags, _ := k.GetRateLimiterFlags(ctx) - // the result maps - gasCoinRates := make(map[int64]sdk.Dec) - erc20CoinRates := make(map[int64]map[string]sdk.Dec) + // the result list + assetRateList := make([]*types.AssetRate, 0) // loop through the rate limiter flags to get the rate for _, conversion := range rateLimiterFlags.Conversions { @@ -46,16 +43,37 @@ func (k Keeper) GetRateLimiterRates(ctx sdk.Context) (map[int64]sdk.Dec, map[int continue } - chainID := fCoin.ForeignChainId - switch fCoin.CoinType { - case coin.CoinType_Gas: - gasCoinRates[chainID] = conversion.Rate - case coin.CoinType_ERC20: - if _, found := erc20CoinRates[chainID]; !found { - erc20CoinRates[chainID] = make(map[string]sdk.Dec) + // add asset rate to list + assetRateList = append(assetRateList, &types.AssetRate{ + ChainId: fCoin.ForeignChainId, + Asset: strings.ToLower(fCoin.Asset), + Decimals: fCoin.Decimals, + CoinType: fCoin.CoinType, + Rate: conversion.Rate, + }) + } + return assetRateList +} + +// BuildAssetRateMapFromList builds maps (foreign chain id -> asset -> rate) from a list of gas and erc20 asset rates +// The 1st map: foreign chain id -> gas coin asset rate +// The 2nd map: foreign chain id -> erc20 asset -> erc20 coin asset rate +func BuildAssetRateMapFromList(assetRates []*types.AssetRate) (map[int64]*types.AssetRate, map[int64]map[string]*types.AssetRate) { + // the result maps + gasAssetRateMap := make(map[int64]*types.AssetRate) + erc20AssetRateMap := make(map[int64]map[string]*types.AssetRate) + + // loop through the asset rates to build the maps + for _, assetRate := range assetRates { + chainID := assetRate.ChainId + if assetRate.CoinType == coin.CoinType_Gas { + gasAssetRateMap[chainID] = assetRate + } else { + if _, found := erc20AssetRateMap[chainID]; !found { + erc20AssetRateMap[chainID] = make(map[string]*types.AssetRate) } - erc20CoinRates[chainID][strings.ToLower(fCoin.Asset)] = conversion.Rate + erc20AssetRateMap[chainID][strings.ToLower(assetRate.Asset)] = assetRate } } - return gasCoinRates, erc20CoinRates + return gasAssetRateMap, erc20AssetRateMap } diff --git a/x/crosschain/keeper/rate_limiter_flags_test.go b/x/crosschain/keeper/rate_limiter_flags_test.go index 0b8010031d..7d12ef5443 100644 --- a/x/crosschain/keeper/rate_limiter_flags_test.go +++ b/x/crosschain/keeper/rate_limiter_flags_test.go @@ -10,9 +10,40 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" ) +// createForeignCoinAndAssetRate creates foreign coin and corresponding asset rate +func createForeignCoinAndAssetRate( + t *testing.T, + zrc20Addr string, + asset string, + chainID int64, + decimals uint32, + coinType coin.CoinType, + rate sdk.Dec, +) (fungibletypes.ForeignCoins, *types.AssetRate) { + // create foreign coin + foreignCoin := sample.ForeignCoins(t, zrc20Addr) + foreignCoin.Asset = asset + foreignCoin.ForeignChainId = chainID + foreignCoin.Decimals = decimals + foreignCoin.CoinType = coinType + + // create corresponding asset rate + assetRate := &types.AssetRate{ + ChainId: foreignCoin.ForeignChainId, + Asset: strings.ToLower(foreignCoin.Asset), + Decimals: foreignCoin.Decimals, + CoinType: foreignCoin.CoinType, + Rate: rate, + } + + return foreignCoin, assetRate +} + func TestKeeper_GetRateLimiterFlags(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) @@ -28,10 +59,11 @@ func TestKeeper_GetRateLimiterFlags(t *testing.T) { require.Equal(t, flags, r) } -func TestKeeper_GetRateLimiterRates(t *testing.T) { +func TestKeeper_GetRateLimiterRateList(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) // create test flags + chainID := chains.GoerliLocalnetChain().ChainId zrc20GasAddr := sample.EthAddress().Hex() zrc20ERC20Addr1 := sample.EthAddress().Hex() zrc20ERC20Addr2 := sample.EthAddress().Hex() @@ -53,39 +85,64 @@ func TestKeeper_GetRateLimiterRates(t *testing.T) { }, } - chainID := chains.GoerliLocalnetChain().ChainId + // set flags + k.SetRateLimiterFlags(ctx, flags) // add gas coin - fcGas := sample.ForeignCoins(t, zrc20GasAddr) - fcGas.CoinType = coin.CoinType_Gas - fcGas.ForeignChainId = chainID - zk.FungibleKeeper.SetForeignCoins(ctx, fcGas) - - // add two erc20 coins - asset1 := sample.EthAddress().Hex() - fcERC20 := sample.ForeignCoins(t, zrc20ERC20Addr1) - fcERC20.Asset = asset1 - fcERC20.ForeignChainId = chainID - zk.FungibleKeeper.SetForeignCoins(ctx, fcERC20) - - asset2 := sample.EthAddress().Hex() - fcERC20 = sample.ForeignCoins(t, zrc20ERC20Addr2) - fcERC20.Asset = asset2 - fcERC20.ForeignChainId = chainID - zk.FungibleKeeper.SetForeignCoins(ctx, fcERC20) + gasCoin, gasAssetRate := createForeignCoinAndAssetRate(t, zrc20GasAddr, "", chainID, 18, coin.CoinType_Gas, sdk.NewDec(1)) + zk.FungibleKeeper.SetForeignCoins(ctx, gasCoin) - // set flags - k.SetRateLimiterFlags(ctx, flags) - r, found := k.GetRateLimiterFlags(ctx) - require.True(t, found) - require.Equal(t, flags, r) + // add 1st erc20 coin + erc20Coin1, erc20AssetRate1 := createForeignCoinAndAssetRate(t, zrc20ERC20Addr1, sample.EthAddress().Hex(), chainID, 8, coin.CoinType_ERC20, sdk.NewDec(2)) + zk.FungibleKeeper.SetForeignCoins(ctx, erc20Coin1) + + // add 2nd erc20 coin + erc20Coin2, erc20AssetRate2 := createForeignCoinAndAssetRate(t, zrc20ERC20Addr2, sample.EthAddress().Hex(), chainID, 6, coin.CoinType_ERC20, sdk.NewDec(3)) + zk.FungibleKeeper.SetForeignCoins(ctx, erc20Coin2) // get rates - gasRates, erc20Rates := k.GetRateLimiterRates(ctx) - require.Equal(t, 1, len(gasRates)) - require.Equal(t, 1, len(erc20Rates)) - require.Equal(t, sdk.NewDec(1), gasRates[chainID]) - require.Equal(t, 2, len(erc20Rates[chainID])) - require.Equal(t, sdk.NewDec(2), erc20Rates[chainID][strings.ToLower(asset1)]) - require.Equal(t, sdk.NewDec(3), erc20Rates[chainID][strings.ToLower(asset2)]) + assetRates := k.GetRateLimiterAssetRateList(ctx) + require.EqualValues(t, []*types.AssetRate{gasAssetRate, erc20AssetRate1, erc20AssetRate2}, assetRates) +} + +func TestBuildAssetRateMapFromList(t *testing.T) { + // define asset rate list + assetRates := []*types.AssetRate{ + { + ChainId: 1, + Asset: "eth", + Decimals: 18, + CoinType: coin.CoinType_Gas, + Rate: sdk.NewDec(1), + }, + { + ChainId: 1, + Asset: "usdt", + Decimals: 6, + CoinType: coin.CoinType_ERC20, + Rate: sdk.NewDec(2), + }, + { + ChainId: 2, + Asset: "btc", + Decimals: 8, + CoinType: coin.CoinType_Gas, + Rate: sdk.NewDec(3), + }, + } + + // build asset rate map + gasAssetRateMap, erc20AssetRateMap := keeper.BuildAssetRateMapFromList(assetRates) + + // check length + require.Equal(t, 2, len(gasAssetRateMap)) + require.Equal(t, 1, len(erc20AssetRateMap)) + require.Equal(t, 1, len(erc20AssetRateMap[1])) + + // check gas asset rate map + require.EqualValues(t, assetRates[0], gasAssetRateMap[1]) + require.EqualValues(t, assetRates[2], gasAssetRateMap[2]) + + // check erc20 asset rate map + require.EqualValues(t, assetRates[1], erc20AssetRateMap[1]["usdt"]) } diff --git a/x/crosschain/types/query.pb.go b/x/crosschain/types/query.pb.go index 8fce000ace..f6a0f4123d 100644 --- a/x/crosschain/types/query.pb.go +++ b/x/crosschain/types/query.pb.go @@ -1587,6 +1587,150 @@ func (m *QueryListPendingCctxResponse) GetTotalPending() uint64 { return 0 } +type QueryRateLimiterInputRequest struct { + Limit uint32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` + Window int64 `protobuf:"varint,2,opt,name=window,proto3" json:"window,omitempty"` +} + +func (m *QueryRateLimiterInputRequest) Reset() { *m = QueryRateLimiterInputRequest{} } +func (m *QueryRateLimiterInputRequest) String() string { return proto.CompactTextString(m) } +func (*QueryRateLimiterInputRequest) ProtoMessage() {} +func (*QueryRateLimiterInputRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_65a992045e92a606, []int{33} +} +func (m *QueryRateLimiterInputRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryRateLimiterInputRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryRateLimiterInputRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryRateLimiterInputRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryRateLimiterInputRequest.Merge(m, src) +} +func (m *QueryRateLimiterInputRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryRateLimiterInputRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryRateLimiterInputRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryRateLimiterInputRequest proto.InternalMessageInfo + +func (m *QueryRateLimiterInputRequest) GetLimit() uint32 { + if m != nil { + return m.Limit + } + return 0 +} + +func (m *QueryRateLimiterInputRequest) GetWindow() int64 { + if m != nil { + return m.Window + } + return 0 +} + +type QueryRateLimiterInputResponse struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + CctxsMissed []*CrossChainTx `protobuf:"bytes,2,rep,name=cctxs_missed,json=cctxsMissed,proto3" json:"cctxs_missed,omitempty"` + CctxsPending []*CrossChainTx `protobuf:"bytes,3,rep,name=cctxs_pending,json=cctxsPending,proto3" json:"cctxs_pending,omitempty"` + TotalPending uint64 `protobuf:"varint,4,opt,name=total_pending,json=totalPending,proto3" json:"total_pending,omitempty"` + PastCctxsValue string `protobuf:"bytes,5,opt,name=past_cctxs_value,json=pastCctxsValue,proto3" json:"past_cctxs_value,omitempty"` + PendingCctxsValue string `protobuf:"bytes,6,opt,name=pending_cctxs_value,json=pendingCctxsValue,proto3" json:"pending_cctxs_value,omitempty"` + LowestPendingCctxHeight int64 `protobuf:"varint,7,opt,name=lowest_pending_cctx_height,json=lowestPendingCctxHeight,proto3" json:"lowest_pending_cctx_height,omitempty"` +} + +func (m *QueryRateLimiterInputResponse) Reset() { *m = QueryRateLimiterInputResponse{} } +func (m *QueryRateLimiterInputResponse) String() string { return proto.CompactTextString(m) } +func (*QueryRateLimiterInputResponse) ProtoMessage() {} +func (*QueryRateLimiterInputResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_65a992045e92a606, []int{34} +} +func (m *QueryRateLimiterInputResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryRateLimiterInputResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryRateLimiterInputResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryRateLimiterInputResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryRateLimiterInputResponse.Merge(m, src) +} +func (m *QueryRateLimiterInputResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryRateLimiterInputResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryRateLimiterInputResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryRateLimiterInputResponse proto.InternalMessageInfo + +func (m *QueryRateLimiterInputResponse) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *QueryRateLimiterInputResponse) GetCctxsMissed() []*CrossChainTx { + if m != nil { + return m.CctxsMissed + } + return nil +} + +func (m *QueryRateLimiterInputResponse) GetCctxsPending() []*CrossChainTx { + if m != nil { + return m.CctxsPending + } + return nil +} + +func (m *QueryRateLimiterInputResponse) GetTotalPending() uint64 { + if m != nil { + return m.TotalPending + } + return 0 +} + +func (m *QueryRateLimiterInputResponse) GetPastCctxsValue() string { + if m != nil { + return m.PastCctxsValue + } + return "" +} + +func (m *QueryRateLimiterInputResponse) GetPendingCctxsValue() string { + if m != nil { + return m.PendingCctxsValue + } + return "" +} + +func (m *QueryRateLimiterInputResponse) GetLowestPendingCctxHeight() int64 { + if m != nil { + return m.LowestPendingCctxHeight + } + return 0 +} + type QueryListPendingCctxWithinRateLimitRequest struct { Limit uint32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` } @@ -1599,7 +1743,7 @@ func (m *QueryListPendingCctxWithinRateLimitRequest) String() string { } func (*QueryListPendingCctxWithinRateLimitRequest) ProtoMessage() {} func (*QueryListPendingCctxWithinRateLimitRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{33} + return fileDescriptor_65a992045e92a606, []int{35} } func (m *QueryListPendingCctxWithinRateLimitRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1651,7 +1795,7 @@ func (m *QueryListPendingCctxWithinRateLimitResponse) String() string { } func (*QueryListPendingCctxWithinRateLimitResponse) ProtoMessage() {} func (*QueryListPendingCctxWithinRateLimitResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{34} + return fileDescriptor_65a992045e92a606, []int{36} } func (m *QueryListPendingCctxWithinRateLimitResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1722,7 +1866,7 @@ func (m *QueryLastZetaHeightRequest) Reset() { *m = QueryLastZetaHeightR func (m *QueryLastZetaHeightRequest) String() string { return proto.CompactTextString(m) } func (*QueryLastZetaHeightRequest) ProtoMessage() {} func (*QueryLastZetaHeightRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{35} + return fileDescriptor_65a992045e92a606, []int{37} } func (m *QueryLastZetaHeightRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1759,7 +1903,7 @@ func (m *QueryLastZetaHeightResponse) Reset() { *m = QueryLastZetaHeight func (m *QueryLastZetaHeightResponse) String() string { return proto.CompactTextString(m) } func (*QueryLastZetaHeightResponse) ProtoMessage() {} func (*QueryLastZetaHeightResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{36} + return fileDescriptor_65a992045e92a606, []int{38} } func (m *QueryLastZetaHeightResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1804,7 +1948,7 @@ func (m *QueryConvertGasToZetaRequest) Reset() { *m = QueryConvertGasToZ func (m *QueryConvertGasToZetaRequest) String() string { return proto.CompactTextString(m) } func (*QueryConvertGasToZetaRequest) ProtoMessage() {} func (*QueryConvertGasToZetaRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{37} + return fileDescriptor_65a992045e92a606, []int{39} } func (m *QueryConvertGasToZetaRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1857,7 +2001,7 @@ func (m *QueryConvertGasToZetaResponse) Reset() { *m = QueryConvertGasTo func (m *QueryConvertGasToZetaResponse) String() string { return proto.CompactTextString(m) } func (*QueryConvertGasToZetaResponse) ProtoMessage() {} func (*QueryConvertGasToZetaResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{38} + return fileDescriptor_65a992045e92a606, []int{40} } func (m *QueryConvertGasToZetaResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1914,7 +2058,7 @@ func (m *QueryMessagePassingProtocolFeeRequest) Reset() { *m = QueryMess func (m *QueryMessagePassingProtocolFeeRequest) String() string { return proto.CompactTextString(m) } func (*QueryMessagePassingProtocolFeeRequest) ProtoMessage() {} func (*QueryMessagePassingProtocolFeeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{39} + return fileDescriptor_65a992045e92a606, []int{41} } func (m *QueryMessagePassingProtocolFeeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1953,7 +2097,7 @@ func (m *QueryMessagePassingProtocolFeeResponse) Reset() { func (m *QueryMessagePassingProtocolFeeResponse) String() string { return proto.CompactTextString(m) } func (*QueryMessagePassingProtocolFeeResponse) ProtoMessage() {} func (*QueryMessagePassingProtocolFeeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{40} + return fileDescriptor_65a992045e92a606, []int{42} } func (m *QueryMessagePassingProtocolFeeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1996,7 +2140,7 @@ func (m *QueryRateLimiterFlagsRequest) Reset() { *m = QueryRateLimiterFl func (m *QueryRateLimiterFlagsRequest) String() string { return proto.CompactTextString(m) } func (*QueryRateLimiterFlagsRequest) ProtoMessage() {} func (*QueryRateLimiterFlagsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{41} + return fileDescriptor_65a992045e92a606, []int{43} } func (m *QueryRateLimiterFlagsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2033,7 +2177,7 @@ func (m *QueryRateLimiterFlagsResponse) Reset() { *m = QueryRateLimiterF func (m *QueryRateLimiterFlagsResponse) String() string { return proto.CompactTextString(m) } func (*QueryRateLimiterFlagsResponse) ProtoMessage() {} func (*QueryRateLimiterFlagsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{42} + return fileDescriptor_65a992045e92a606, []int{44} } func (m *QueryRateLimiterFlagsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2103,6 +2247,8 @@ func init() { proto.RegisterType((*QueryAllCctxResponse)(nil), "zetachain.zetacore.crosschain.QueryAllCctxResponse") proto.RegisterType((*QueryListPendingCctxRequest)(nil), "zetachain.zetacore.crosschain.QueryListPendingCctxRequest") proto.RegisterType((*QueryListPendingCctxResponse)(nil), "zetachain.zetacore.crosschain.QueryListPendingCctxResponse") + proto.RegisterType((*QueryRateLimiterInputRequest)(nil), "zetachain.zetacore.crosschain.QueryRateLimiterInputRequest") + proto.RegisterType((*QueryRateLimiterInputResponse)(nil), "zetachain.zetacore.crosschain.QueryRateLimiterInputResponse") proto.RegisterType((*QueryListPendingCctxWithinRateLimitRequest)(nil), "zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitRequest") proto.RegisterType((*QueryListPendingCctxWithinRateLimitResponse)(nil), "zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitResponse") proto.RegisterType((*QueryLastZetaHeightRequest)(nil), "zetachain.zetacore.crosschain.QueryLastZetaHeightRequest") @@ -2118,130 +2264,140 @@ func init() { func init() { proto.RegisterFile("crosschain/query.proto", fileDescriptor_65a992045e92a606) } var fileDescriptor_65a992045e92a606 = []byte{ - // 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, + // 2118 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xcf, 0x6f, 0x1c, 0x49, + 0x15, 0x4e, 0x79, 0xe2, 0xc4, 0x29, 0x3b, 0xbf, 0x2a, 0x4e, 0x62, 0x7a, 0x93, 0xc9, 0x6e, 0x9b, + 0xc4, 0x26, 0x21, 0x33, 0x1b, 0x67, 0xe3, 0x25, 0x89, 0x17, 0x61, 0x3b, 0x9b, 0xc4, 0xe0, 0x64, + 0xbd, 0x23, 0x43, 0x50, 0x10, 0x6a, 0x95, 0x7b, 0x6a, 0x7b, 0x5a, 0xdb, 0xee, 0x9e, 0xed, 0xae, + 0x89, 0x27, 0x89, 0x7c, 0xc9, 0x81, 0x33, 0xd2, 0x0a, 0x71, 0xe1, 0x8a, 0xe0, 0xc0, 0x81, 0x03, + 0x82, 0x03, 0xd2, 0xa2, 0x15, 0x10, 0x72, 0x5c, 0x09, 0x09, 0x21, 0x90, 0x10, 0x4a, 0xb8, 0x70, + 0xe5, 0x2f, 0x40, 0x5d, 0xfd, 0x7a, 0xa6, 0xfa, 0xd7, 0x4c, 0xcd, 0x78, 0xf6, 0x90, 0x53, 0xa6, + 0xbb, 0xea, 0xbd, 0xfa, 0xbe, 0xf7, 0x5e, 0xbf, 0x7e, 0xfd, 0xc5, 0xf8, 0x94, 0xe9, 0x7b, 0x41, + 0x60, 0x36, 0xa8, 0xed, 0x56, 0x3f, 0x69, 0x31, 0xff, 0x71, 0xa5, 0xe9, 0x7b, 0xdc, 0x23, 0x67, + 0x9f, 0x30, 0x4e, 0xc5, 0xed, 0x8a, 0xf8, 0xe5, 0xf9, 0xac, 0xd2, 0xdd, 0xaa, 0x5d, 0x34, 0xbd, + 0x60, 0xdb, 0x0b, 0xaa, 0x5b, 0x34, 0x60, 0x91, 0x5d, 0xf5, 0xd1, 0x95, 0x2d, 0xc6, 0xe9, 0x95, + 0x6a, 0x93, 0x5a, 0xb6, 0x4b, 0xb9, 0xed, 0xb9, 0x91, 0x2b, 0xed, 0x9c, 0x74, 0x84, 0xf8, 0x69, + 0x88, 0xdf, 0x06, 0x6f, 0xc3, 0x06, 0x4d, 0xda, 0x60, 0xd1, 0xc0, 0x68, 0xfa, 0xb6, 0xc9, 0x60, + 0x6d, 0x56, 0x5a, 0x13, 0x36, 0x46, 0x83, 0x06, 0x0d, 0x83, 0x7b, 0x86, 0x69, 0x76, 0x1c, 0x94, + 0x33, 0x9b, 0xb8, 0x4f, 0xcd, 0x8f, 0x99, 0x0f, 0xeb, 0xba, 0xb4, 0xee, 0xd0, 0x80, 0x1b, 0x5b, + 0x8e, 0x67, 0x7e, 0x6c, 0x34, 0x98, 0x6d, 0x35, 0x78, 0x0e, 0x4a, 0xaf, 0xc5, 0xb3, 0x4e, 0x64, + 0x24, 0x3e, 0xe5, 0xcc, 0x70, 0xec, 0x6d, 0x9b, 0x33, 0xdf, 0xf8, 0xc8, 0xa1, 0x56, 0x00, 0x9b, + 0xa6, 0x2d, 0xcf, 0xf2, 0xc4, 0xcf, 0x6a, 0xf8, 0x0b, 0xee, 0x9e, 0xb1, 0x3c, 0xcf, 0x72, 0x58, + 0x95, 0x36, 0xed, 0x2a, 0x75, 0x5d, 0x8f, 0x8b, 0xf0, 0x80, 0x8d, 0x7e, 0x06, 0x6b, 0x1f, 0x86, + 0x11, 0x7c, 0xc8, 0x38, 0x5d, 0x36, 0x4d, 0xaf, 0xe5, 0x72, 0xdb, 0xb5, 0x6a, 0xec, 0x93, 0x16, + 0x0b, 0xb8, 0x7e, 0x0f, 0xbf, 0x91, 0xbb, 0x1a, 0x34, 0x3d, 0x37, 0x60, 0xa4, 0x82, 0x4f, 0xd0, + 0x2d, 0xcf, 0xe7, 0xac, 0x6e, 0x84, 0x79, 0x32, 0xe8, 0x76, 0xb8, 0x63, 0x06, 0xbd, 0x89, 0xe6, + 0x0f, 0xd5, 0x8e, 0xc3, 0x92, 0xb0, 0x15, 0x0b, 0x1d, 0x77, 0x77, 0x18, 0xff, 0xa0, 0xc5, 0x37, + 0xdb, 0x9b, 0x11, 0x47, 0x38, 0x8d, 0xcc, 0xe0, 0x83, 0x82, 0xe1, 0xda, 0x2d, 0xe1, 0xa2, 0x54, + 0x8b, 0x2f, 0xc9, 0x34, 0x1e, 0x77, 0x3d, 0xd7, 0x64, 0x33, 0x63, 0x6f, 0xa2, 0xf9, 0xfd, 0xb5, + 0xe8, 0x42, 0x6f, 0xe1, 0x33, 0xf9, 0xee, 0x00, 0xde, 0x77, 0xf1, 0x94, 0x27, 0xdd, 0x17, 0x4e, + 0x27, 0x17, 0x2e, 0x55, 0x7a, 0x56, 0x57, 0x45, 0x76, 0xb5, 0xb2, 0xff, 0xc5, 0xbf, 0xce, 0xed, + 0xab, 0x25, 0xdc, 0xe8, 0x0c, 0x58, 0x2c, 0x3b, 0x4e, 0x1e, 0x8b, 0xdb, 0x18, 0x77, 0xab, 0x10, + 0xce, 0xbc, 0x50, 0x89, 0x4a, 0xb6, 0x12, 0x96, 0x6c, 0x25, 0x2a, 0x75, 0x28, 0xd9, 0xca, 0x06, + 0xb5, 0x18, 0xd8, 0xd6, 0x24, 0x4b, 0xfd, 0x33, 0x04, 0xf4, 0x32, 0xe7, 0x14, 0xd2, 0x2b, 0x8d, + 0x80, 0x1e, 0xb9, 0x93, 0xc0, 0x3f, 0x26, 0xf0, 0xcf, 0xf5, 0xc5, 0x1f, 0x61, 0x4a, 0x10, 0x78, + 0x86, 0xb0, 0x9e, 0x47, 0x60, 0xe5, 0xf1, 0x6a, 0x88, 0x24, 0x8e, 0xd7, 0x34, 0x1e, 0x17, 0xc8, + 0x20, 0xe7, 0xd1, 0x45, 0x2a, 0x8a, 0x63, 0x43, 0x47, 0xf1, 0xcf, 0x08, 0xcf, 0xf6, 0x04, 0xf1, + 0x9a, 0x04, 0xf3, 0x47, 0x08, 0xbf, 0x15, 0xf3, 0x58, 0x73, 0x8b, 0x62, 0xf9, 0x15, 0x3c, 0x11, + 0xb5, 0x37, 0xbb, 0x9e, 0x7c, 0x84, 0xea, 0x23, 0x0b, 0xe8, 0xe7, 0x52, 0x56, 0xf3, 0x80, 0x40, + 0x3c, 0x6b, 0x78, 0xd2, 0x76, 0xd3, 0xe1, 0xbc, 0xd8, 0x27, 0x9c, 0xb2, 0xbf, 0x28, 0x9a, 0xb2, + 0x93, 0xd1, 0x05, 0x53, 0x7a, 0x82, 0xa5, 0x23, 0x83, 0x51, 0x3f, 0xc1, 0xbf, 0x97, 0x9e, 0xe0, + 0xe4, 0x39, 0xaf, 0x43, 0x90, 0x6e, 0xe2, 0xb3, 0x71, 0x77, 0x0d, 0x8f, 0xbc, 0x4b, 0x83, 0xc6, + 0xa6, 0xb7, 0x6a, 0xf2, 0x76, 0x1c, 0x26, 0x0d, 0x4f, 0xd8, 0xb0, 0x00, 0x2d, 0xbf, 0x73, 0xad, + 0xef, 0xe2, 0x72, 0x91, 0x31, 0x70, 0xff, 0x01, 0x3e, 0x62, 0x27, 0x56, 0x20, 0xd0, 0x97, 0x15, + 0xe8, 0x77, 0x8d, 0x20, 0x02, 0x29, 0x57, 0xfa, 0x12, 0x1c, 0x9f, 0xdc, 0x7c, 0x8b, 0x72, 0xaa, + 0x02, 0xfe, 0x09, 0x3e, 0x57, 0x68, 0x0d, 0xe8, 0x1f, 0xe0, 0xc3, 0xab, 0x21, 0x26, 0x51, 0xf4, + 0x9b, 0xed, 0x40, 0xb1, 0x5f, 0xc8, 0x36, 0x00, 0x3d, 0xe9, 0x47, 0xb7, 0x20, 0xea, 0x50, 0x32, + 0xd9, 0xa8, 0x8f, 0xaa, 0x38, 0x9f, 0x23, 0x88, 0x51, 0xce, 0x49, 0x3d, 0x52, 0x54, 0x1a, 0x51, + 0x8a, 0x46, 0x57, 0xa7, 0x55, 0x7c, 0x3a, 0x2e, 0xb5, 0x3b, 0x34, 0xd8, 0x08, 0xc7, 0x37, 0xe9, + 0xd5, 0x62, 0xbb, 0x75, 0xd6, 0x86, 0x0c, 0x47, 0x17, 0xba, 0x81, 0x67, 0xb2, 0x06, 0x40, 0x79, + 0x15, 0x4f, 0xc4, 0xf7, 0x20, 0xb6, 0x73, 0x7d, 0xc8, 0x76, 0x5c, 0x74, 0x0c, 0x75, 0x0a, 0x88, + 0x96, 0x1d, 0x27, 0x8d, 0x68, 0x54, 0xd9, 0xfb, 0x25, 0x02, 0x12, 0x89, 0x33, 0x72, 0x49, 0x94, + 0x86, 0x22, 0x31, 0xba, 0xfc, 0x2c, 0x76, 0x5b, 0xc1, 0x3a, 0x0d, 0xf8, 0x4a, 0x38, 0xfd, 0xde, + 0x15, 0xc3, 0x6f, 0xef, 0x34, 0x3d, 0x85, 0xa7, 0x30, 0xcf, 0x0e, 0x88, 0x7e, 0x1f, 0x1f, 0x4d, + 0x2d, 0x41, 0x48, 0x2b, 0x7d, 0xf8, 0xa6, 0x1d, 0xa6, 0xdd, 0xe8, 0x8d, 0xee, 0xc3, 0x51, 0x00, + 0x7a, 0x54, 0x99, 0xfc, 0x13, 0x02, 0x9e, 0x79, 0x47, 0xf5, 0xe2, 0x59, 0x1a, 0x01, 0xcf, 0xd1, + 0x65, 0xf9, 0x12, 0x3e, 0x11, 0x67, 0x4b, 0xee, 0x56, 0xf9, 0xa9, 0x5d, 0x87, 0x8f, 0x0e, 0xd8, + 0xbc, 0xf2, 0xf8, 0x7e, 0x38, 0xcf, 0x0f, 0xfb, 0x19, 0x60, 0xe1, 0xe9, 0xe4, 0xd1, 0x10, 0xb5, + 0x0f, 0xf0, 0x94, 0xdc, 0x5b, 0x15, 0xc7, 0x7f, 0xd9, 0xa4, 0x96, 0x70, 0xa0, 0xff, 0x10, 0x38, + 0x2e, 0x3b, 0xce, 0x97, 0xd1, 0x91, 0x7f, 0x8d, 0x80, 0x48, 0xc7, 0x7f, 0x21, 0x91, 0xd2, 0x9e, + 0x88, 0x8c, 0x2e, 0xeb, 0xf7, 0x61, 0x90, 0x5a, 0xb7, 0x03, 0xbe, 0xc1, 0xdc, 0xba, 0xed, 0x5a, + 0x72, 0x64, 0x7a, 0x8c, 0xa3, 0xd3, 0x78, 0x5c, 0x7c, 0xc2, 0x8a, 0xd3, 0x0f, 0xd7, 0xa2, 0x0b, + 0xfd, 0xd3, 0x78, 0x62, 0xca, 0x38, 0xfc, 0xb2, 0x42, 0xa1, 0xe3, 0x29, 0xee, 0x71, 0xea, 0xc0, + 0x61, 0x50, 0x59, 0x89, 0x7b, 0xfa, 0x3a, 0x80, 0xaa, 0x51, 0xce, 0xd6, 0xa3, 0xef, 0xee, 0x35, + 0xb7, 0xd9, 0x92, 0xfb, 0x57, 0xc4, 0x05, 0x49, 0x5c, 0xc8, 0x29, 0x7c, 0x60, 0xc7, 0x76, 0xeb, + 0xde, 0x8e, 0xf0, 0x59, 0xaa, 0xc1, 0x95, 0xfe, 0x93, 0x12, 0xbc, 0xe2, 0xb3, 0xee, 0x80, 0xe4, + 0x29, 0x7c, 0xa0, 0xd1, 0xed, 0x66, 0xa5, 0x1a, 0x5c, 0x91, 0xfb, 0x78, 0xca, 0x34, 0x79, 0x3b, + 0x30, 0xb6, 0xed, 0x20, 0x60, 0xf5, 0x99, 0xb1, 0xc1, 0xc9, 0x4f, 0x0a, 0x07, 0xf7, 0x84, 0x3d, + 0xd9, 0xc0, 0x87, 0x23, 0x7f, 0x4d, 0x20, 0x5f, 0x1a, 0x22, 0x9a, 0xc2, 0x03, 0x44, 0x8a, 0xcc, + 0xe2, 0xc3, 0x22, 0x72, 0x1d, 0x8f, 0xfb, 0xb3, 0xe1, 0x24, 0xf3, 0xf8, 0x58, 0x93, 0x06, 0xdc, + 0x88, 0xce, 0x7e, 0x44, 0x9d, 0x16, 0x9b, 0x19, 0x17, 0xed, 0xe1, 0x48, 0x78, 0x3f, 0xcc, 0x77, + 0xf0, 0xbd, 0xf0, 0x2e, 0xa9, 0xe0, 0x13, 0xe0, 0x28, 0xb1, 0xf9, 0x40, 0xa4, 0x2f, 0x34, 0xbb, + 0xf5, 0x01, 0xfb, 0x6f, 0x62, 0xcd, 0xf1, 0x76, 0x58, 0xc0, 0x0d, 0xd9, 0x0c, 0xa4, 0x96, 0x99, + 0x83, 0x22, 0x98, 0xa7, 0xa3, 0x1d, 0x52, 0x71, 0x41, 0xcb, 0x5f, 0xc1, 0x17, 0xf3, 0x4a, 0xef, + 0x81, 0xcd, 0x1b, 0xb6, 0xdb, 0xc9, 0x55, 0xcf, 0x9c, 0xeb, 0x9f, 0x8f, 0xe1, 0x4b, 0x4a, 0x4e, + 0x20, 0xd3, 0x1f, 0xe2, 0x23, 0x49, 0x51, 0x6a, 0xa8, 0x82, 0x36, 0xe5, 0x82, 0xce, 0xa4, 0x20, + 0xa7, 0xa2, 0xc9, 0x22, 0x3e, 0x6d, 0xb6, 0x7c, 0x9f, 0xb9, 0xdc, 0xd8, 0xb1, 0x79, 0xa3, 0xee, + 0xd3, 0x1d, 0x03, 0x8a, 0xb5, 0x24, 0xa2, 0x74, 0x12, 0x96, 0x1f, 0xc0, 0xea, 0x03, 0xb1, 0x48, + 0x16, 0xf0, 0xc9, 0x8c, 0x9d, 0x4f, 0x39, 0x13, 0x79, 0x3e, 0x54, 0x3b, 0x91, 0xb2, 0x0a, 0x09, + 0x87, 0x49, 0xec, 0x2a, 0x56, 0x06, 0x6b, 0x9b, 0x8c, 0xd5, 0x59, 0x5d, 0x64, 0x7c, 0xa2, 0x76, + 0xdc, 0x8f, 0x63, 0xf2, 0x3e, 0x2c, 0x74, 0x14, 0xa9, 0xf0, 0x55, 0xf5, 0x90, 0x71, 0x9a, 0x78, + 0xed, 0xea, 0xd7, 0xe2, 0x8e, 0x93, 0x5a, 0xed, 0x3e, 0x3a, 0x77, 0x13, 0x8f, 0x0e, 0x24, 0x77, + 0x13, 0x1e, 0xe1, 0x55, 0xcf, 0x7d, 0xc4, 0xfc, 0x70, 0xee, 0xdb, 0xf4, 0x42, 0xf3, 0xcc, 0x3b, + 0x27, 0xd3, 0xa8, 0x34, 0x3c, 0x61, 0xd1, 0x60, 0xbd, 0xd3, 0xab, 0x0e, 0xd5, 0x3a, 0xd7, 0xfa, + 0xcf, 0x11, 0x3c, 0xca, 0x59, 0xb7, 0x80, 0xe7, 0xeb, 0xf8, 0xb8, 0xd7, 0xe2, 0x5b, 0x5e, 0xcb, + 0xad, 0xdf, 0xa1, 0xc1, 0x9a, 0x1b, 0x2e, 0xc6, 0xfa, 0x58, 0x66, 0x21, 0xdc, 0x2d, 0x54, 0x39, + 0xd3, 0x73, 0x6e, 0x33, 0x06, 0xbb, 0xc7, 0xa0, 0xda, 0xd3, 0x0b, 0x64, 0x1e, 0x1f, 0x0d, 0xff, + 0x95, 0xa7, 0x82, 0x92, 0xc8, 0x75, 0xfa, 0xb6, 0x3e, 0x87, 0xcf, 0x0b, 0x98, 0xf7, 0x58, 0x10, + 0x50, 0x8b, 0x6d, 0xd0, 0x20, 0xb0, 0x5d, 0x6b, 0xa3, 0xeb, 0x31, 0x8e, 0xee, 0x6d, 0x7c, 0xa1, + 0xdf, 0x46, 0x20, 0x76, 0x06, 0x1f, 0xfa, 0xa8, 0x03, 0x31, 0x22, 0xd4, 0xbd, 0xa1, 0x97, 0xb3, + 0x1d, 0xf3, 0xb6, 0x43, 0xad, 0xf8, 0x0b, 0x5b, 0x7f, 0x86, 0xb2, 0x3d, 0x10, 0x36, 0x80, 0x7f, + 0x8a, 0x8f, 0xf9, 0xa9, 0x35, 0x78, 0xb5, 0x56, 0xfb, 0x3c, 0x1b, 0x69, 0x97, 0xf0, 0xfd, 0x91, + 0x71, 0xb7, 0xf0, 0x5f, 0x1d, 0x8f, 0x0b, 0x10, 0xe4, 0x39, 0xc2, 0x53, 0xb2, 0x94, 0x43, 0x6e, + 0xf4, 0x39, 0xa3, 0x87, 0x8a, 0xa9, 0xdd, 0x1c, 0xca, 0x36, 0xa2, 0xad, 0xbf, 0xf7, 0xec, 0xaf, + 0xff, 0xf9, 0x74, 0xec, 0x5d, 0x72, 0xad, 0x1a, 0x9a, 0x5e, 0x96, 0x74, 0xeb, 0x8e, 0x38, 0xdc, + 0x31, 0xaa, 0x3e, 0x85, 0xb9, 0x68, 0xb7, 0xfa, 0x54, 0x4c, 0x42, 0xbb, 0xe4, 0x77, 0x08, 0x1f, + 0x95, 0xfd, 0x2e, 0x3b, 0x8e, 0x1a, 0x97, 0x7c, 0x2d, 0x53, 0x8d, 0x4b, 0x81, 0x3e, 0xa9, 0x5f, + 0x12, 0x5c, 0xce, 0x93, 0x59, 0x05, 0x2e, 0xe4, 0x9f, 0x08, 0x9f, 0x4a, 0x21, 0x07, 0x49, 0x89, + 0x2c, 0x0f, 0x01, 0x22, 0xa9, 0x8b, 0x69, 0x2b, 0x7b, 0x71, 0x01, 0x74, 0x6e, 0x08, 0x3a, 0xef, + 0x90, 0x05, 0x05, 0x3a, 0x60, 0x0b, 0x19, 0xda, 0x25, 0xff, 0x40, 0xf8, 0xa4, 0xa4, 0xdb, 0x48, + 0xe4, 0xbe, 0xa5, 0x88, 0xac, 0x50, 0xf3, 0xd3, 0x96, 0xf7, 0xe0, 0x01, 0xa8, 0x2d, 0x09, 0x6a, + 0x8b, 0xe4, 0x9d, 0x02, 0x6a, 0xb6, 0x5b, 0xc0, 0xcc, 0xb0, 0xeb, 0xbb, 0xe4, 0xb7, 0x08, 0x1f, + 0x49, 0x92, 0x53, 0xae, 0xb9, 0x1c, 0xf5, 0x4d, 0xb9, 0xe6, 0xf2, 0x14, 0xb5, 0xbe, 0x35, 0x27, + 0x31, 0x09, 0xc8, 0x5f, 0x00, 0xb8, 0xa4, 0x4a, 0x2c, 0x29, 0x3e, 0xbc, 0xb9, 0xda, 0x8c, 0xf6, + 0xde, 0x90, 0xd6, 0x00, 0xfe, 0x1b, 0x02, 0xfc, 0x02, 0x79, 0xbb, 0x07, 0xf8, 0xae, 0x59, 0xf5, + 0x69, 0x7c, 0xbd, 0x4b, 0xfe, 0x86, 0x30, 0xc9, 0xaa, 0x55, 0x44, 0x09, 0x4f, 0xa1, 0x46, 0xa6, + 0x7d, 0x73, 0x58, 0x73, 0xe0, 0xb3, 0x2c, 0xf8, 0xdc, 0x24, 0xd7, 0x0b, 0xf9, 0xa4, 0xff, 0x4b, + 0xcd, 0xa8, 0x53, 0x4e, 0x65, 0x62, 0x7f, 0x40, 0xf8, 0x78, 0xf2, 0x84, 0xb0, 0xbc, 0x96, 0x06, + 0x28, 0x91, 0x21, 0xb3, 0x54, 0xa8, 0x8a, 0xe9, 0x97, 0x05, 0xab, 0x39, 0x72, 0x5e, 0x29, 0x4b, + 0xe4, 0x57, 0xa8, 0xab, 0xc6, 0x90, 0x45, 0xc5, 0x02, 0x49, 0xc9, 0x46, 0xda, 0xbb, 0x03, 0xdb, + 0x01, 0xd8, 0xaa, 0x00, 0xfb, 0x35, 0x32, 0x57, 0x00, 0xd6, 0x02, 0x83, 0x30, 0xe6, 0x75, 0xd6, + 0xde, 0x25, 0xbf, 0x40, 0x78, 0x32, 0xf6, 0x12, 0x86, 0x7a, 0x51, 0x31, 0x58, 0x43, 0x21, 0xce, + 0x11, 0xaf, 0xf4, 0x39, 0x81, 0xf8, 0x2d, 0x72, 0xae, 0x0f, 0x62, 0xf2, 0x19, 0xc2, 0xc7, 0xd2, + 0x73, 0x17, 0x51, 0x6a, 0x1e, 0x05, 0x43, 0xa0, 0xb6, 0x34, 0x9c, 0xb1, 0x62, 0xa8, 0xcd, 0x34, + 0xd6, 0xe7, 0x08, 0x4f, 0x4a, 0xa3, 0x15, 0xb9, 0xa5, 0x72, 0x7c, 0xbf, 0x11, 0x4e, 0x7b, 0x7f, + 0x8f, 0x5e, 0x80, 0xcd, 0x45, 0xc1, 0xe6, 0xab, 0x44, 0x2f, 0x60, 0x23, 0x8d, 0xa3, 0xe4, 0x05, + 0xca, 0xe8, 0x53, 0x44, 0xb5, 0x15, 0xe6, 0xab, 0x6b, 0x6a, 0xad, 0xa7, 0x58, 0x19, 0xd4, 0x17, + 0x05, 0xfc, 0xb7, 0x49, 0xa5, 0x00, 0xbe, 0x93, 0xb4, 0xeb, 0x94, 0xff, 0x1f, 0x11, 0x26, 0x29, + 0x9f, 0xe1, 0x53, 0xa0, 0xda, 0x32, 0xf6, 0xc2, 0xa6, 0x58, 0xff, 0xd3, 0x2b, 0x82, 0xcd, 0x3c, + 0xb9, 0xa0, 0xc6, 0x86, 0xfc, 0x0c, 0xe1, 0xfd, 0xa2, 0xf9, 0x2c, 0x28, 0x86, 0x51, 0x6e, 0x8f, + 0x57, 0x07, 0xb2, 0x51, 0x7c, 0xef, 0x9a, 0xf0, 0xc2, 0x12, 0x41, 0xfe, 0x0d, 0xc2, 0x93, 0x92, + 0xee, 0x47, 0xae, 0x0f, 0x70, 0x62, 0x52, 0x2b, 0x1c, 0x0e, 0xec, 0x35, 0x01, 0xb6, 0x4a, 0x2e, + 0xf7, 0x04, 0x9b, 0x19, 0xae, 0x7f, 0x8a, 0xf0, 0xc1, 0xf8, 0x0d, 0xb4, 0xa0, 0x98, 0xd1, 0x81, + 0x03, 0x9b, 0xd2, 0xfe, 0xf4, 0x59, 0x81, 0xf5, 0x2c, 0x79, 0xa3, 0x07, 0xd6, 0x70, 0x02, 0x3b, + 0x9a, 0x52, 0x1c, 0xd4, 0x46, 0xb0, 0x7c, 0xdd, 0x4e, 0x6d, 0x04, 0x2b, 0x90, 0xe8, 0xfa, 0x77, + 0x0e, 0x09, 0xe4, 0xff, 0x10, 0x2e, 0xf7, 0x96, 0x4a, 0xc8, 0xda, 0x10, 0x58, 0xf2, 0x35, 0x1b, + 0xed, 0xdb, 0xa3, 0x70, 0x05, 0x2c, 0xaf, 0x0b, 0x96, 0x57, 0xc9, 0x95, 0xfe, 0x2c, 0xd3, 0x8c, + 0xc2, 0x79, 0x39, 0xf9, 0x07, 0x35, 0x6a, 0x4f, 0x40, 0xee, 0x9f, 0xe8, 0x68, 0x37, 0x86, 0x31, + 0x55, 0x1c, 0x65, 0x9e, 0x24, 0x51, 0x86, 0xc0, 0x93, 0xba, 0x8b, 0x1a, 0xf0, 0x5c, 0x25, 0x47, + 0x0d, 0x78, 0xbe, 0xcc, 0xd3, 0x17, 0xb8, 0x93, 0x44, 0x19, 0x8e, 0x0a, 0x69, 0x59, 0x40, 0x6d, + 0x54, 0x28, 0x10, 0x30, 0xd4, 0x46, 0x85, 0x22, 0x71, 0xa3, 0xef, 0xa8, 0x90, 0x96, 0x2a, 0xd2, + 0x04, 0x84, 0x5c, 0x3c, 0x30, 0x01, 0x59, 0xb3, 0x1e, 0x98, 0x40, 0x42, 0xa1, 0x1e, 0x84, 0x80, + 0x30, 0x5c, 0xf9, 0xce, 0x8b, 0x97, 0x65, 0xf4, 0xc5, 0xcb, 0x32, 0xfa, 0xf7, 0xcb, 0x32, 0xfa, + 0xf1, 0xab, 0xf2, 0xbe, 0x2f, 0x5e, 0x95, 0xf7, 0xfd, 0xfd, 0x55, 0x79, 0xdf, 0xc3, 0x2b, 0x96, + 0xcd, 0x1b, 0xad, 0xad, 0x8a, 0xe9, 0x6d, 0xcb, 0xce, 0x62, 0x4c, 0xd5, 0xb6, 0xec, 0x97, 0x3f, + 0x6e, 0xb2, 0x60, 0xeb, 0x80, 0x18, 0x3e, 0xae, 0xfe, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xff, 0x69, + 0x74, 0xa9, 0x3f, 0x28, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2294,6 +2450,8 @@ type QueryClient interface { LastZetaHeight(ctx context.Context, in *QueryLastZetaHeightRequest, opts ...grpc.CallOption) (*QueryLastZetaHeightResponse, error) // Queries the rate limiter flags RateLimiterFlags(ctx context.Context, in *QueryRateLimiterFlagsRequest, opts ...grpc.CallOption) (*QueryRateLimiterFlagsResponse, error) + // Queries the input data of rate limiter. + RateLimiterInput(ctx context.Context, in *QueryRateLimiterInputRequest, opts ...grpc.CallOption) (*QueryRateLimiterInputResponse, error) } type queryClient struct { @@ -2502,6 +2660,15 @@ func (c *queryClient) RateLimiterFlags(ctx context.Context, in *QueryRateLimiter return out, nil } +func (c *queryClient) RateLimiterInput(ctx context.Context, in *QueryRateLimiterInputRequest, opts ...grpc.CallOption) (*QueryRateLimiterInputResponse, error) { + out := new(QueryRateLimiterInputResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/RateLimiterInput", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Queries a OutTxTracker by index. @@ -2542,6 +2709,8 @@ type QueryServer interface { LastZetaHeight(context.Context, *QueryLastZetaHeightRequest) (*QueryLastZetaHeightResponse, error) // Queries the rate limiter flags RateLimiterFlags(context.Context, *QueryRateLimiterFlagsRequest) (*QueryRateLimiterFlagsResponse, error) + // Queries the input data of rate limiter. + RateLimiterInput(context.Context, *QueryRateLimiterInputRequest) (*QueryRateLimiterInputResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -2614,6 +2783,9 @@ func (*UnimplementedQueryServer) LastZetaHeight(ctx context.Context, req *QueryL func (*UnimplementedQueryServer) RateLimiterFlags(ctx context.Context, req *QueryRateLimiterFlagsRequest) (*QueryRateLimiterFlagsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RateLimiterFlags not implemented") } +func (*UnimplementedQueryServer) RateLimiterInput(ctx context.Context, req *QueryRateLimiterInputRequest) (*QueryRateLimiterInputResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RateLimiterInput not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -3015,6 +3187,24 @@ func _Query_RateLimiterFlags_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } +func _Query_RateLimiterInput_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryRateLimiterInputRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).RateLimiterInput(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.crosschain.Query/RateLimiterInput", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).RateLimiterInput(ctx, req.(*QueryRateLimiterInputRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "zetachain.zetacore.crosschain.Query", HandlerType: (*QueryServer)(nil), @@ -3107,6 +3297,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "RateLimiterFlags", Handler: _Query_RateLimiterFlags_Handler, }, + { + MethodName: "RateLimiterInput", + Handler: _Query_RateLimiterInput_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "crosschain/query.proto", @@ -4346,6 +4540,119 @@ func (m *QueryListPendingCctxResponse) MarshalToSizedBuffer(dAtA []byte) (int, e return len(dAtA) - i, nil } +func (m *QueryRateLimiterInputRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryRateLimiterInputRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryRateLimiterInputRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Window != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Window)) + i-- + dAtA[i] = 0x10 + } + if m.Limit != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryRateLimiterInputResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryRateLimiterInputResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryRateLimiterInputResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.LowestPendingCctxHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.LowestPendingCctxHeight)) + i-- + dAtA[i] = 0x38 + } + if len(m.PendingCctxsValue) > 0 { + i -= len(m.PendingCctxsValue) + copy(dAtA[i:], m.PendingCctxsValue) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PendingCctxsValue))) + i-- + dAtA[i] = 0x32 + } + if len(m.PastCctxsValue) > 0 { + i -= len(m.PastCctxsValue) + copy(dAtA[i:], m.PastCctxsValue) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PastCctxsValue))) + i-- + dAtA[i] = 0x2a + } + if m.TotalPending != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.TotalPending)) + i-- + dAtA[i] = 0x20 + } + if len(m.CctxsPending) > 0 { + for iNdEx := len(m.CctxsPending) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CctxsPending[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.CctxsMissed) > 0 { + for iNdEx := len(m.CctxsMissed) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CctxsMissed[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *QueryListPendingCctxWithinRateLimitRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5174,6 +5481,59 @@ func (m *QueryListPendingCctxResponse) Size() (n int) { return n } +func (m *QueryRateLimiterInputRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Limit != 0 { + n += 1 + sovQuery(uint64(m.Limit)) + } + if m.Window != 0 { + n += 1 + sovQuery(uint64(m.Window)) + } + return n +} + +func (m *QueryRateLimiterInputResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + if len(m.CctxsMissed) > 0 { + for _, e := range m.CctxsMissed { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if len(m.CctxsPending) > 0 { + for _, e := range m.CctxsPending { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.TotalPending != 0 { + n += 1 + sovQuery(uint64(m.TotalPending)) + } + l = len(m.PastCctxsValue) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.PendingCctxsValue) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.LowestPendingCctxHeight != 0 { + n += 1 + sovQuery(uint64(m.LowestPendingCctxHeight)) + } + return n +} + func (m *QueryListPendingCctxWithinRateLimitRequest) Size() (n int) { if m == nil { return 0 @@ -8422,6 +8782,333 @@ func (m *QueryListPendingCctxResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryRateLimiterInputRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryRateLimiterInputRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryRateLimiterInputRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Window", wireType) + } + m.Window = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Window |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryRateLimiterInputResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryRateLimiterInputResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryRateLimiterInputResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CctxsMissed", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CctxsMissed = append(m.CctxsMissed, &CrossChainTx{}) + if err := m.CctxsMissed[len(m.CctxsMissed)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CctxsPending", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CctxsPending = append(m.CctxsPending, &CrossChainTx{}) + if err := m.CctxsPending[len(m.CctxsPending)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPending", wireType) + } + m.TotalPending = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPending |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PastCctxsValue", 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.PastCctxsValue = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PendingCctxsValue", 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.PendingCctxsValue = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LowestPendingCctxHeight", wireType) + } + m.LowestPendingCctxHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LowestPendingCctxHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryListPendingCctxWithinRateLimitRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/crosschain/types/query.pb.gw.go b/x/crosschain/types/query.pb.gw.go index 6a4aa48cdd..7bedbd13bd 100644 --- a/x/crosschain/types/query.pb.gw.go +++ b/x/crosschain/types/query.pb.gw.go @@ -995,6 +995,42 @@ func local_request_Query_RateLimiterFlags_0(ctx context.Context, marshaler runti } +var ( + filter_Query_RateLimiterInput_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_RateLimiterInput_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryRateLimiterInputRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_RateLimiterInput_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.RateLimiterInput(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_RateLimiterInput_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryRateLimiterInputRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_RateLimiterInput_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.RateLimiterInput(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -1507,6 +1543,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_RateLimiterInput_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_RateLimiterInput_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_RateLimiterInput_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1988,6 +2047,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_RateLimiterInput_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_RateLimiterInput_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_RateLimiterInput_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -2035,6 +2114,8 @@ var ( pattern_Query_LastZetaHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "lastZetaHeight"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_RateLimiterFlags_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "rateLimiterFlags"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_RateLimiterInput_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "rateLimiterInput"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -2081,4 +2162,6 @@ var ( forward_Query_LastZetaHeight_0 = runtime.ForwardResponseMessage forward_Query_RateLimiterFlags_0 = runtime.ForwardResponseMessage + + forward_Query_RateLimiterInput_0 = runtime.ForwardResponseMessage ) diff --git a/x/crosschain/types/rate_limiter_flags.pb.go b/x/crosschain/types/rate_limiter_flags.pb.go index 899a1d7099..a422c55433 100644 --- a/x/crosschain/types/rate_limiter_flags.pb.go +++ b/x/crosschain/types/rate_limiter_flags.pb.go @@ -12,6 +12,7 @@ import ( github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/gogo/protobuf/proto" + coin "github.com/zeta-chain/zetacore/pkg/coin" ) // Reference imports to suppress errors if they are not otherwise used. @@ -134,9 +135,79 @@ func (m *Conversion) GetZrc20() string { return "" } +type AssetRate struct { + ChainId int64 `protobuf:"varint,1,opt,name=chainId,proto3" json:"chainId,omitempty"` + Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` + Decimals uint32 `protobuf:"varint,3,opt,name=decimals,proto3" json:"decimals,omitempty"` + CoinType coin.CoinType `protobuf:"varint,4,opt,name=coin_type,json=coinType,proto3,enum=coin.CoinType" json:"coin_type,omitempty"` + Rate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,5,opt,name=rate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"rate"` +} + +func (m *AssetRate) Reset() { *m = AssetRate{} } +func (m *AssetRate) String() string { return proto.CompactTextString(m) } +func (*AssetRate) ProtoMessage() {} +func (*AssetRate) Descriptor() ([]byte, []int) { + return fileDescriptor_b17ae80d5af4e97e, []int{2} +} +func (m *AssetRate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AssetRate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AssetRate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AssetRate) XXX_Merge(src proto.Message) { + xxx_messageInfo_AssetRate.Merge(m, src) +} +func (m *AssetRate) XXX_Size() int { + return m.Size() +} +func (m *AssetRate) XXX_DiscardUnknown() { + xxx_messageInfo_AssetRate.DiscardUnknown(m) +} + +var xxx_messageInfo_AssetRate proto.InternalMessageInfo + +func (m *AssetRate) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *AssetRate) GetAsset() string { + if m != nil { + return m.Asset + } + return "" +} + +func (m *AssetRate) GetDecimals() uint32 { + if m != nil { + return m.Decimals + } + return 0 +} + +func (m *AssetRate) GetCoinType() coin.CoinType { + if m != nil { + return m.CoinType + } + return coin.CoinType_Zeta +} + func init() { proto.RegisterType((*RateLimiterFlags)(nil), "zetachain.zetacore.crosschain.RateLimiterFlags") proto.RegisterType((*Conversion)(nil), "zetachain.zetacore.crosschain.Conversion") + proto.RegisterType((*AssetRate)(nil), "zetachain.zetacore.crosschain.AssetRate") } func init() { @@ -144,28 +215,34 @@ func init() { } var fileDescriptor_b17ae80d5af4e97e = []byte{ - // 331 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xc1, 0x4e, 0x32, 0x31, - 0x14, 0x85, 0xa7, 0xc0, 0xcf, 0x2f, 0x65, 0x63, 0x1a, 0x62, 0x26, 0x26, 0x0e, 0x13, 0x4c, 0x74, - 0x5c, 0xd0, 0x2a, 0xbe, 0xc1, 0x60, 0xdc, 0xe8, 0xc6, 0x49, 0xdc, 0xb8, 0x21, 0x43, 0x29, 0x43, - 0x23, 0xb4, 0xa4, 0xad, 0xa2, 0x3c, 0x85, 0x8f, 0xc5, 0x92, 0xa5, 0x31, 0x86, 0x18, 0x78, 0x11, - 0x33, 0x9d, 0x41, 0x66, 0x65, 0x5c, 0xf5, 0xde, 0xe4, 0x9e, 0xd3, 0xef, 0xe4, 0xc0, 0x63, 0xaa, - 0xa4, 0xd6, 0x74, 0x14, 0x73, 0x41, 0x54, 0x6c, 0x58, 0x6f, 0xcc, 0x27, 0xdc, 0x30, 0xd5, 0x1b, - 0x8e, 0xe3, 0x44, 0xe3, 0xa9, 0x92, 0x46, 0xa2, 0xa3, 0x39, 0x33, 0xb1, 0xbd, 0xc1, 0x76, 0x92, - 0x8a, 0xe1, 0x9d, 0xee, 0xb0, 0x91, 0xc8, 0x44, 0xda, 0x4b, 0x92, 0x4e, 0x99, 0xa8, 0xf5, 0x09, - 0xe0, 0x7e, 0x14, 0x1b, 0x76, 0x9b, 0x19, 0x5e, 0xa7, 0x7e, 0xc8, 0x85, 0xff, 0x99, 0x88, 0xfb, - 0x63, 0x36, 0x70, 0x81, 0x0f, 0x82, 0xbd, 0x68, 0xbb, 0xa2, 0x03, 0x58, 0x9d, 0x71, 0x31, 0x90, - 0x33, 0xb7, 0xe4, 0x83, 0xa0, 0x1c, 0xe5, 0x1b, 0xea, 0xc2, 0x4a, 0xca, 0xe5, 0x96, 0x7d, 0x10, - 0xd4, 0x42, 0xb2, 0x58, 0x35, 0x9d, 0x8f, 0x55, 0xf3, 0x34, 0xe1, 0x66, 0xf4, 0xd4, 0xc7, 0x54, - 0x4e, 0x08, 0x95, 0x7a, 0x22, 0x75, 0xfe, 0xb4, 0xf5, 0xe0, 0x91, 0x98, 0xd7, 0x29, 0xd3, 0xf8, - 0x9e, 0x0b, 0x13, 0x59, 0x31, 0xba, 0x83, 0x75, 0x2a, 0xc5, 0x33, 0x53, 0x9a, 0x4b, 0xa1, 0xdd, - 0x8a, 0x5f, 0x0e, 0xea, 0x9d, 0x33, 0xfc, 0x6b, 0x2c, 0xdc, 0xfd, 0x51, 0x84, 0x95, 0xf4, 0xdb, - 0xa8, 0xe8, 0xd1, 0x1a, 0x42, 0xb8, 0x3b, 0x40, 0x0d, 0xf8, 0x6f, 0xae, 0x68, 0xe7, 0xdc, 0xa6, - 0xaa, 0x45, 0xd9, 0x82, 0xc2, 0x9c, 0xbd, 0x64, 0xd9, 0x71, 0xce, 0x7e, 0xf2, 0x07, 0xf6, 0x2b, - 0x46, 0x33, 0xf4, 0xf0, 0x66, 0xb1, 0xf6, 0xc0, 0x72, 0xed, 0x81, 0xaf, 0xb5, 0x07, 0xde, 0x36, - 0x9e, 0xb3, 0xdc, 0x78, 0xce, 0xfb, 0xc6, 0x73, 0x1e, 0x2e, 0x0a, 0x3e, 0x29, 0x7f, 0x3b, 0x6b, - 0x71, 0x1b, 0x85, 0xbc, 0x90, 0x42, 0xb7, 0xd6, 0xb6, 0x5f, 0xb5, 0xd5, 0x5c, 0x7e, 0x07, 0x00, - 0x00, 0xff, 0xff, 0xd2, 0x2c, 0x21, 0x90, 0xf6, 0x01, 0x00, 0x00, + // 426 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0xdd, 0x6a, 0x14, 0x31, + 0x14, 0xde, 0x74, 0xb7, 0x75, 0x27, 0xc5, 0x22, 0xb1, 0xc8, 0xb0, 0xe0, 0x74, 0x58, 0x41, 0x47, + 0xa4, 0x89, 0xae, 0x4f, 0xe0, 0xac, 0x08, 0xa2, 0x37, 0x06, 0xbd, 0xf1, 0x66, 0xc9, 0x66, 0xd2, + 0x69, 0xe8, 0x4c, 0x32, 0x4c, 0xa2, 0xb5, 0x7d, 0x0a, 0x1f, 0x6b, 0x2f, 0x7b, 0x29, 0x22, 0x45, + 0x76, 0x5f, 0x44, 0x92, 0xcc, 0xfe, 0x5c, 0x89, 0x78, 0x33, 0x73, 0xbe, 0x70, 0xce, 0xf9, 0xbe, + 0x73, 0xbe, 0x03, 0x1f, 0xf1, 0x56, 0x1b, 0xc3, 0xcf, 0x99, 0x54, 0xa4, 0x65, 0x56, 0xcc, 0x2a, + 0x59, 0x4b, 0x2b, 0xda, 0xd9, 0x59, 0xc5, 0x4a, 0x83, 0x9b, 0x56, 0x5b, 0x8d, 0x1e, 0x5e, 0x0b, + 0xcb, 0x7c, 0x0e, 0xf6, 0x91, 0x6e, 0x05, 0xde, 0xd6, 0x8d, 0x8e, 0x4b, 0x5d, 0x6a, 0x9f, 0x49, + 0x5c, 0x14, 0x8a, 0x46, 0xf7, 0x9b, 0x8b, 0x92, 0x70, 0x2d, 0x95, 0xff, 0x84, 0xc7, 0xf1, 0x2f, + 0x00, 0xef, 0x51, 0x66, 0xc5, 0xfb, 0xc0, 0xf2, 0xc6, 0x91, 0xa0, 0x18, 0xde, 0x11, 0x8a, 0xcd, + 0x2b, 0x51, 0xc4, 0x20, 0x05, 0xd9, 0x90, 0xae, 0x21, 0x7a, 0x00, 0x0f, 0x2e, 0xa5, 0x2a, 0xf4, + 0x65, 0xbc, 0x97, 0x82, 0xac, 0x4f, 0x3b, 0x84, 0xa6, 0x70, 0xe0, 0xc4, 0xc6, 0xfd, 0x14, 0x64, + 0x51, 0x4e, 0x16, 0xb7, 0x27, 0xbd, 0x9f, 0xb7, 0x27, 0x4f, 0x4a, 0x69, 0xcf, 0xbf, 0xcc, 0x31, + 0xd7, 0x35, 0xe1, 0xda, 0xd4, 0xda, 0x74, 0xbf, 0x53, 0x53, 0x5c, 0x10, 0x7b, 0xd5, 0x08, 0x83, + 0x3f, 0x49, 0x65, 0xa9, 0x2f, 0x46, 0x1f, 0xe0, 0x21, 0xd7, 0xea, 0xab, 0x68, 0x8d, 0xd4, 0xca, + 0xc4, 0x83, 0xb4, 0x9f, 0x1d, 0x4e, 0x9e, 0xe2, 0xbf, 0xce, 0x8a, 0xa7, 0x9b, 0x8a, 0x7c, 0xe0, + 0x68, 0xe9, 0x6e, 0x8f, 0xf1, 0x19, 0x84, 0xdb, 0x04, 0x74, 0x0c, 0xf7, 0xaf, 0x5b, 0x3e, 0x79, + 0xee, 0xa7, 0x8a, 0x68, 0x00, 0x28, 0xef, 0xb4, 0xef, 0x79, 0xed, 0xb8, 0xd3, 0xfe, 0xf8, 0x1f, + 0xb4, 0xbf, 0x16, 0x3c, 0x48, 0x1f, 0x2f, 0x00, 0x8c, 0x5e, 0x19, 0x23, 0xac, 0xdb, 0xa5, 0xdb, + 0x9f, 0x17, 0xf7, 0x36, 0xec, 0xaf, 0x4f, 0xd7, 0xd0, 0x29, 0x60, 0x2e, 0x2d, 0x90, 0xd1, 0x00, + 0xd0, 0x08, 0x0e, 0x0b, 0xc1, 0x65, 0xcd, 0x2a, 0xe3, 0x37, 0x78, 0x97, 0x6e, 0x30, 0x7a, 0x06, + 0x23, 0x67, 0xd7, 0xcc, 0x31, 0xc6, 0x83, 0x14, 0x64, 0x47, 0x93, 0x23, 0xec, 0x0d, 0x9c, 0x6a, + 0xa9, 0x3e, 0x5e, 0x35, 0x82, 0x0e, 0x79, 0x17, 0x6d, 0x46, 0xd9, 0xff, 0xff, 0x51, 0xf2, 0x77, + 0x8b, 0x65, 0x02, 0x6e, 0x96, 0x09, 0xf8, 0xbd, 0x4c, 0xc0, 0xf7, 0x55, 0xd2, 0xbb, 0x59, 0x25, + 0xbd, 0x1f, 0xab, 0xa4, 0xf7, 0xf9, 0xc5, 0x4e, 0x1f, 0x67, 0xc5, 0x69, 0xb8, 0xd2, 0xb5, 0x2b, + 0xe4, 0x1b, 0xd9, 0xb9, 0x5d, 0xdf, 0x76, 0x7e, 0xe0, 0xaf, 0xec, 0xe5, 0x9f, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xe5, 0xed, 0xc8, 0x24, 0xd6, 0x02, 0x00, 0x00, } func (m *RateLimiterFlags) Marshal() (dAtA []byte, err error) { @@ -270,6 +347,61 @@ func (m *Conversion) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *AssetRate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AssetRate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AssetRate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Rate.Size() + i -= size + if _, err := m.Rate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if m.CoinType != 0 { + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.CoinType)) + i-- + dAtA[i] = 0x20 + } + if m.Decimals != 0 { + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.Decimals)) + i-- + dAtA[i] = 0x18 + } + if len(m.Asset) > 0 { + i -= len(m.Asset) + copy(dAtA[i:], m.Asset) + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(len(m.Asset))) + i-- + dAtA[i] = 0x12 + } + if m.ChainId != 0 { + i = encodeVarintRateLimiterFlags(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintRateLimiterFlags(dAtA []byte, offset int, v uint64) int { offset -= sovRateLimiterFlags(v) base := offset @@ -319,6 +451,30 @@ func (m *Conversion) Size() (n int) { return n } +func (m *AssetRate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ChainId != 0 { + n += 1 + sovRateLimiterFlags(uint64(m.ChainId)) + } + l = len(m.Asset) + if l > 0 { + n += 1 + l + sovRateLimiterFlags(uint64(l)) + } + if m.Decimals != 0 { + n += 1 + sovRateLimiterFlags(uint64(m.Decimals)) + } + if m.CoinType != 0 { + n += 1 + sovRateLimiterFlags(uint64(m.CoinType)) + } + l = m.Rate.Size() + n += 1 + l + sovRateLimiterFlags(uint64(l)) + return n +} + func sovRateLimiterFlags(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -598,6 +754,179 @@ func (m *Conversion) Unmarshal(dAtA []byte) error { } return nil } +func (m *AssetRate) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AssetRate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AssetRate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Asset", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + 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 ErrInvalidLengthRateLimiterFlags + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRateLimiterFlags + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Asset = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Decimals", wireType) + } + m.Decimals = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Decimals |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CoinType", wireType) + } + m.CoinType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CoinType |= coin.CoinType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRateLimiterFlags + } + 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 ErrInvalidLengthRateLimiterFlags + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRateLimiterFlags + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Rate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRateLimiterFlags(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRateLimiterFlags + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipRateLimiterFlags(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/zetaclient/core_context/zeta_core_context.go b/zetaclient/core_context/zeta_core_context.go index 5bb8d23089..7d2c3f6e37 100644 --- a/zetaclient/core_context/zeta_core_context.go +++ b/zetaclient/core_context/zeta_core_context.go @@ -80,6 +80,18 @@ func (c *ZetaCoreContext) GetEnabledChains() []chains.Chain { return copiedChains } +func (c *ZetaCoreContext) GetEnabledForeignChains() []chains.Chain { + c.coreContextLock.RLock() + defer c.coreContextLock.RUnlock() + foreignChains := make([]chains.Chain, 0) + for _, chain := range c.chainsEnabled { + if !chain.IsZetaChain() { + foreignChains = append(foreignChains, chain) + } + } + return foreignChains +} + func (c *ZetaCoreContext) GetEVMChainParams(chainID int64) (*observertypes.ChainParams, bool) { c.coreContextLock.RLock() defer c.coreContextLock.RUnlock() diff --git a/zetaclient/core_context/zeta_core_context_test.go b/zetaclient/core_context/zeta_core_context_test.go index 93b0e1c57f..a9d43058c0 100644 --- a/zetaclient/core_context/zeta_core_context_test.go +++ b/zetaclient/core_context/zeta_core_context_test.go @@ -69,6 +69,9 @@ func TestNewZetaCoreContext(t *testing.T) { // assert enabled chains require.Empty(t, len(zetaContext.GetEnabledChains())) + // assert foreign chains + require.Empty(t, len(zetaContext.GetEnabledForeignChains())) + // assert current tss pubkey require.Equal(t, "", zetaContext.GetCurrentTssPubkey()) @@ -155,6 +158,10 @@ func TestUpdateZetaCoreContext(t *testing.T) { ChainName: 2, ChainId: 2, }, + { + ChainName: 3, + ChainId: chains.ZetaTestnetChain().ChainId, + }, } evmChainParamsToUpdate := map[int64]*observertypes.ChainParams{ 1: { @@ -192,6 +199,9 @@ func TestUpdateZetaCoreContext(t *testing.T) { // assert enabled chains updated require.Equal(t, enabledChainsToUpdate, zetaContext.GetEnabledChains()) + // assert enabled foreign chains + require.Equal(t, enabledChainsToUpdate[0:2], zetaContext.GetEnabledForeignChains()) + // assert current tss pubkey updated require.Equal(t, tssPubKeyToUpdate, zetaContext.GetCurrentTssPubkey()) diff --git a/zetaclient/interfaces/interfaces.go b/zetaclient/interfaces/interfaces.go index d40ac4689b..6179918b29 100644 --- a/zetaclient/interfaces/interfaces.go +++ b/zetaclient/interfaces/interfaces.go @@ -99,11 +99,13 @@ type ZetaCoreBridger interface { GetLastBlockHeightByChain(chain chains.Chain) (*crosschaintypes.LastBlockHeight, error) ListPendingCctx(chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, int64, string, bool, error) + GetRateLimiterInput(window int64) (crosschaintypes.QueryRateLimiterInputResponse, error) GetPendingNoncesByChain(chainID int64) (observertypes.PendingNonces, error) GetCctxByNonce(chainID int64, nonce uint64) (*crosschaintypes.CrossChainTx, error) GetOutTxTracker(chain chains.Chain, nonce uint64) (*crosschaintypes.OutTxTracker, error) GetAllOutTxTrackerByChain(chainID int64, order Order) ([]crosschaintypes.OutTxTracker, error) GetCrosschainFlags() (observertypes.CrosschainFlags, error) + GetRateLimiterFlags() (crosschaintypes.RateLimiterFlags, error) GetObserverList() ([]string, error) GetKeyGen() (*observertypes.Keygen, error) GetBtcTssAddress(chainID int64) (string, error) diff --git a/zetaclient/metrics/metrics.go b/zetaclient/metrics/metrics.go index d6525a70ba..d466524970 100644 --- a/zetaclient/metrics/metrics.go +++ b/zetaclient/metrics/metrics.go @@ -84,6 +84,12 @@ var ( Name: "num_active_message_signs", Help: "Number of concurrent key signs", }) + + PercentageOfRateReached = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: ZetaClientNamespace, + Name: "percentage_of_rate_reached", + Help: "Percentage of the rate limiter rate reached", + }) ) func NewMetrics() (*Metrics, error) { diff --git a/zetaclient/ratelimiter/rate_limiter.go b/zetaclient/ratelimiter/rate_limiter.go new file mode 100644 index 0000000000..ed54831849 --- /dev/null +++ b/zetaclient/ratelimiter/rate_limiter.go @@ -0,0 +1,139 @@ +package ratelimiter + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// Input is the input data for the rate limiter +type Input struct { + // zeta chain height + Height int64 + + // the missed cctxs in range [?, NonceLow) across all chains + CctxsMissed []*crosschaintypes.CrossChainTx + + // the pending cctxs in range [NonceLow, NonceHigh) across all chains + CctxsPending []*crosschaintypes.CrossChainTx + + // the total value of the past cctxs within window across all chains + PastCctxsValue sdkmath.Int + + // the total value of the pending cctxs across all chains + PendingCctxsValue sdkmath.Int + + // the lowest height of the pending (not missed) cctxs across all chains + LowestPendingCctxHeight int64 +} + +// Output is the output data for the rate limiter +type Output struct { + // the cctxs to be scheduled after rate limit check + CctxsMap map[int64][]*crosschaintypes.CrossChainTx + + // the current sliding window within which the withdrawals are considered by the rate limiter + CurrentWithdrawWindow int64 + + // the current withdraw rate (azeta/block) within the current sliding window + CurrentWithdrawRate sdkmath.Int + + // wehther the current withdraw rate exceeds the given rate limit or not + RateLimitExceeded bool +} + +// NewInput creates a rate limiter input from gRPC response +func NewInput(resp crosschaintypes.QueryRateLimiterInputResponse) (*Input, bool) { + // parse the past cctxs value from string + pastCctxsValue, ok := sdk.NewIntFromString(resp.PastCctxsValue) + if !ok { + return nil, false + } + + // parse the pending cctxs value from string + pendingCctxsValue, ok := sdk.NewIntFromString(resp.PendingCctxsValue) + if !ok { + return nil, false + } + + return &Input{ + Height: resp.Height, + CctxsMissed: resp.CctxsMissed, + CctxsPending: resp.CctxsPending, + PastCctxsValue: pastCctxsValue, + PendingCctxsValue: pendingCctxsValue, + LowestPendingCctxHeight: resp.LowestPendingCctxHeight, + }, true +} + +// IsRateLimiterUsable checks if the rate limiter is usable or not +func IsRateLimiterUsable(rateLimiterFlags crosschaintypes.RateLimiterFlags) bool { + if !rateLimiterFlags.Enabled { + return false + } + if rateLimiterFlags.Window <= 0 { + return false + } + if rateLimiterFlags.Rate.IsNil() { + return false + } + if rateLimiterFlags.Rate.IsZero() { + return false + } + return true +} + +// ApplyRateLimiter applies the rate limiter to the input and produces output +func ApplyRateLimiter(input *Input, window int64, rate sdkmath.Uint) *Output { + // block limit and the window limit in azeta + blockLimitInAzeta := sdkmath.NewIntFromBigInt(rate.BigInt()) + windowLimitInAzeta := blockLimitInAzeta.Mul(sdkmath.NewInt(window)) + + // invariant: for period of time >= `window`, the zetaclient-side average withdraw rate should be <= `blockLimitInZeta` + // otherwise, zetaclient should wait for the average rate to drop below `blockLimitInZeta` + withdrawWindow := window + withdrawLimitInAzeta := windowLimitInAzeta + if input.LowestPendingCctxHeight != 0 { + // If [input.LowestPendingCctxHeight, input.Height] is wider than the given `window`, we should: + // 1. use the wider window to calculate the average withdraw rate + // 2. adjust the limit proportionally to fit the wider window + pendingCctxWindow := input.Height - input.LowestPendingCctxHeight + 1 + if pendingCctxWindow > window { + withdrawWindow = pendingCctxWindow + withdrawLimitInAzeta = blockLimitInAzeta.Mul(sdk.NewInt(pendingCctxWindow)) + } + } + + // limit exceeded or not + totalWithdrawInAzeta := input.PastCctxsValue.Add(input.PendingCctxsValue) + limitExceeded := totalWithdrawInAzeta.GT(withdrawLimitInAzeta) + + // define the result cctx map to be scheduled + cctxMap := make(map[int64][]*crosschaintypes.CrossChainTx) + + // addCctxsToMap adds the given cctxs to the cctx map + addCctxsToMap := func(cctxs []*crosschaintypes.CrossChainTx) { + for _, cctx := range cctxs { + chainID := cctx.GetCurrentOutTxParam().ReceiverChainId + if _, found := cctxMap[chainID]; !found { + cctxMap[chainID] = make([]*crosschaintypes.CrossChainTx, 0) + } + cctxMap[chainID] = append(cctxMap[chainID], cctx) + } + } + + // schedule missed cctxs regardless of the `limitExceeded` flag + addCctxsToMap(input.CctxsMissed) + + // schedule pending cctxs only if `limitExceeded == false` + if !limitExceeded { + addCctxsToMap(input.CctxsPending) + } + + return &Output{ + CctxsMap: cctxMap, + CurrentWithdrawWindow: withdrawWindow, + CurrentWithdrawRate: totalWithdrawInAzeta.Quo(sdk.NewInt(withdrawWindow)), + RateLimitExceeded: limitExceeded, + } +} diff --git a/zetaclient/ratelimiter/rate_limiter_test.go b/zetaclient/ratelimiter/rate_limiter_test.go new file mode 100644 index 0000000000..6b04a5e728 --- /dev/null +++ b/zetaclient/ratelimiter/rate_limiter_test.go @@ -0,0 +1,265 @@ +package ratelimiter_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/testutil/sample" + crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/ratelimiter" +) + +func Test_NewFilterInput(t *testing.T) { + // sample response + response := crosschaintypes.QueryRateLimiterInputResponse{ + Height: 10, + CctxsMissed: []*crosschaintypes.CrossChainTx{sample.CrossChainTx(t, "1-1")}, + CctxsPending: []*crosschaintypes.CrossChainTx{sample.CrossChainTx(t, "1-2")}, + TotalPending: 7, + PastCctxsValue: sdk.NewInt(12345678).Mul(sdk.NewInt(1e18)).String(), + PendingCctxsValue: sdk.NewInt(4321).Mul(sdk.NewInt(1e18)).String(), + LowestPendingCctxHeight: 2, + } + + t.Run("should create a filter input from gRPC response", func(t *testing.T) { + filterInput, ok := ratelimiter.NewInput(response) + require.True(t, ok) + require.Equal(t, response.Height, filterInput.Height) + require.Equal(t, response.CctxsMissed, filterInput.CctxsMissed) + require.Equal(t, response.CctxsPending, filterInput.CctxsPending) + require.Equal(t, response.PastCctxsValue, filterInput.PastCctxsValue.String()) + require.Equal(t, response.PendingCctxsValue, filterInput.PendingCctxsValue.String()) + require.Equal(t, response.LowestPendingCctxHeight, filterInput.LowestPendingCctxHeight) + }) + t.Run("should return false if past cctxs value is invalid", func(t *testing.T) { + invalidResp := response + invalidResp.PastCctxsValue = "invalid" + filterInput, ok := ratelimiter.NewInput(invalidResp) + require.False(t, ok) + require.Nil(t, filterInput) + }) + t.Run("should return false if pending cctxs value is invalid", func(t *testing.T) { + invalidResp := response + invalidResp.PendingCctxsValue = "invalid" + filterInput, ok := ratelimiter.NewInput(invalidResp) + require.False(t, ok) + require.Nil(t, filterInput) + }) +} + +func Test_IsRateLimiterUsable(t *testing.T) { + tests := []struct { + name string + flags crosschaintypes.RateLimiterFlags + expected bool + }{ + { + name: "rate limiter is enabled", + flags: crosschaintypes.RateLimiterFlags{ + Enabled: true, + Window: 100, + Rate: sdkmath.NewUint(1e18), // 1 ZETA/block + }, + expected: true, + }, + { + name: "rate limiter is disabled", + flags: crosschaintypes.RateLimiterFlags{ + Enabled: false, + }, + expected: false, + }, + { + name: "rate limiter is enabled with 0 window", + flags: crosschaintypes.RateLimiterFlags{ + Enabled: true, + Window: 0, + }, + expected: false, + }, + { + name: "rate limiter is enabled with nil rate", + flags: crosschaintypes.RateLimiterFlags{ + Enabled: true, + Window: 100, + Rate: sdkmath.Uint{}, + }, + expected: false, + }, + { + name: "rate limiter is enabled with zero rate", + flags: crosschaintypes.RateLimiterFlags{ + Enabled: true, + Window: 100, + Rate: sdkmath.NewUint(0), + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + usable := ratelimiter.IsRateLimiterUsable(tt.flags) + require.Equal(t, tt.expected, usable) + }) + } +} + +func Test_ApplyRateLimiter(t *testing.T) { + // define test chain ids + ethChainID := chains.EthChain().ChainId + btcChainID := chains.BtcMainnetChain().ChainId + + // create 10 missed and 90 pending cctxs for eth chain, the coinType/amount does not matter for this test + // but we still use a proper cctx value (0.5 ZETA) to make the test more realistic + ethCctxsMissed := sample.CustomCctxsInBlockRange(t, 1, 10, ethChainID, coin.CoinType_Gas, "", uint64(2e14), crosschaintypes.CctxStatus_PendingOutbound) + ethCctxsPending := sample.CustomCctxsInBlockRange(t, 11, 100, ethChainID, coin.CoinType_Gas, "", uint64(2e14), crosschaintypes.CctxStatus_PendingOutbound) + ethCctxsAll := append(append([]*crosschaintypes.CrossChainTx{}, ethCctxsMissed...), ethCctxsPending...) + + // create 10 missed and 90 pending cctxs for btc chain, the coinType/amount does not matter for this test + // but we still use a proper cctx value (0.5 ZETA) to make the test more realistic + btcCctxsMissed := sample.CustomCctxsInBlockRange(t, 1, 10, btcChainID, coin.CoinType_Gas, "", 2000, crosschaintypes.CctxStatus_PendingOutbound) + btcCctxsPending := sample.CustomCctxsInBlockRange(t, 11, 100, btcChainID, coin.CoinType_Gas, "", 2000, crosschaintypes.CctxStatus_PendingOutbound) + btcCctxsAll := append(append([]*crosschaintypes.CrossChainTx{}, btcCctxsMissed...), btcCctxsPending...) + + // all missed cctxs and all pending cctxs across all chains + allCctxsMissed := crosschainkeeper.SortCctxsByHeightAndChainID( + append(append([]*crosschaintypes.CrossChainTx{}, ethCctxsMissed...), btcCctxsMissed...)) + allCctxsPending := crosschainkeeper.SortCctxsByHeightAndChainID( + append(append([]*crosschaintypes.CrossChainTx{}, ethCctxsPending...), btcCctxsPending...)) + + // define test cases + tests := []struct { + name string + window int64 + rate sdkmath.Uint + input ratelimiter.Input + output ratelimiter.Output + }{ + { + name: "should return all missed and pending cctxs", + window: 100, + rate: sdk.NewUint(1e18), // 1 ZETA/block + input: ratelimiter.Input{ + Height: 100, + CctxsMissed: allCctxsMissed, + CctxsPending: allCctxsPending, + PastCctxsValue: sdk.NewInt(10).Mul(sdk.NewInt(1e18)), // 10 * 1 ZETA + PendingCctxsValue: sdk.NewInt(90).Mul(sdk.NewInt(1e18)), // 90 * 1 ZETA + LowestPendingCctxHeight: 11, + }, + output: ratelimiter.Output{ + CctxsMap: map[int64][]*crosschaintypes.CrossChainTx{ + ethChainID: ethCctxsAll, + btcChainID: btcCctxsAll, + }, + CurrentWithdrawWindow: 100, // height [1, 100] + CurrentWithdrawRate: sdk.NewInt(1e18), // (10 + 90) / 100 + RateLimitExceeded: false, + }, + }, + { + name: "should monitor a wider window and adjust the total limit", + window: 50, + rate: sdk.NewUint(1e18), // 1 ZETA/block + input: ratelimiter.Input{ + Height: 100, + CctxsMissed: allCctxsMissed, + CctxsPending: allCctxsPending, + PastCctxsValue: sdk.NewInt(0), // no past cctx in height range [51, 100] + PendingCctxsValue: sdk.NewInt(90).Mul(sdk.NewInt(1e18)), // 90 * 1 ZETA + LowestPendingCctxHeight: 11, + }, + output: ratelimiter.Output{ + CctxsMap: map[int64][]*crosschaintypes.CrossChainTx{ + ethChainID: ethCctxsAll, + btcChainID: btcCctxsAll, + }, + CurrentWithdrawWindow: 90, // [LowestPendingCctxHeight, Height] = [11, 100] + CurrentWithdrawRate: sdk.NewInt(1e18), // 90 / 90 = 1 ZETA/block + RateLimitExceeded: false, + }, + }, + { + name: "rate limit is exceeded in given sliding window 100", + window: 100, + rate: sdk.NewUint(1e18), // 1 ZETA/block + input: ratelimiter.Input{ + Height: 100, + CctxsMissed: allCctxsMissed, + CctxsPending: allCctxsPending, + PastCctxsValue: sdk.NewInt(11).Mul(sdk.NewInt(1e18)), // 11 ZETA, increased value by 1 ZETA + PendingCctxsValue: sdk.NewInt(90).Mul(sdk.NewInt(1e18)), // 90 * 1 ZETA + LowestPendingCctxHeight: 11, + }, + output: ratelimiter.Output{ // should return missed cctxs only + CctxsMap: map[int64][]*crosschaintypes.CrossChainTx{ + ethChainID: ethCctxsMissed, + btcChainID: btcCctxsMissed, + }, + CurrentWithdrawWindow: 100, // height [1, 100] + CurrentWithdrawRate: sdk.NewInt(101e16), // (11 + 90) / 100 = 1.01 ZETA/block (exceeds 0.99 ZETA/block) + RateLimitExceeded: true, + }, + }, + { + name: "rate limit is exceeded in wider window then the given sliding window 50", + window: 50, + rate: sdk.NewUint(1e18), // 1 ZETA/block + input: ratelimiter.Input{ + Height: 100, + CctxsMissed: allCctxsMissed, + CctxsPending: allCctxsPending, + PastCctxsValue: sdk.NewInt(0), // no past cctx in height range [51, 100] + PendingCctxsValue: sdk.NewInt(91).Mul(sdk.NewInt(1e18)), // 91 ZETA, increased value by 1 ZETA + LowestPendingCctxHeight: 11, + }, + output: ratelimiter.Output{ + CctxsMap: map[int64][]*crosschaintypes.CrossChainTx{ + ethChainID: ethCctxsMissed, + btcChainID: btcCctxsMissed, + }, + CurrentWithdrawWindow: 90, // [LowestPendingCctxHeight, Height] = [11, 100] + CurrentWithdrawRate: sdk.NewInt(91).Mul(sdk.NewInt(1e18)).Quo(sdk.NewInt(90)), // 91 / 90 = 1.011111111111111111 ZETA/block + RateLimitExceeded: true, + }, + }, + { + name: "should not exceed rate limit if we wait for 1 more block", + window: 50, + rate: sdk.NewUint(1e18), // 1 ZETA/block + input: ratelimiter.Input{ + Height: 101, + CctxsMissed: allCctxsMissed, + CctxsPending: allCctxsPending, + PastCctxsValue: sdk.NewInt(0), // no past cctx in height range [52, 101] + PendingCctxsValue: sdk.NewInt(91).Mul(sdk.NewInt(1e18)), // 91 ZETA, increased value by 1 ZETA + LowestPendingCctxHeight: 11, + }, + output: ratelimiter.Output{ + CctxsMap: map[int64][]*crosschaintypes.CrossChainTx{ + ethChainID: ethCctxsAll, + btcChainID: btcCctxsAll, + }, + CurrentWithdrawWindow: 91, // [LowestPendingCctxHeight, Height] = [11, 101] + CurrentWithdrawRate: sdk.NewInt(91).Mul(sdk.NewInt(1e18)).Quo(sdk.NewInt(91)), // 91 / 91 = 1.011 ZETA/block + RateLimitExceeded: false, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + output := ratelimiter.ApplyRateLimiter(&tt.input, tt.window, tt.rate) + require.Equal(t, tt.output.CctxsMap, output.CctxsMap) + require.Equal(t, tt.output.CurrentWithdrawWindow, output.CurrentWithdrawWindow) + require.Equal(t, tt.output.CurrentWithdrawRate, output.CurrentWithdrawRate) + require.Equal(t, tt.output.RateLimitExceeded, output.RateLimitExceeded) + }) + } +} diff --git a/zetaclient/testutils/stub/core_bridge.go b/zetaclient/testutils/stub/core_bridge.go index ec72eae7d8..becc5eb98c 100644 --- a/zetaclient/testutils/stub/core_bridge.go +++ b/zetaclient/testutils/stub/core_bridge.go @@ -13,7 +13,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/pkg/proofs" "github.com/zeta-chain/zetacore/testutil/sample" - cctxtypes "github.com/zeta-chain/zetacore/x/crosschain/types" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" observerTypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/interfaces" "github.com/zeta-chain/zetacore/zetaclient/keys" @@ -21,12 +21,23 @@ import ( ) const ErrMsgPaused = "zeta core bridge is paused" +const ErrMsgRPCFailed = "rpc failed" var _ interfaces.ZetaCoreBridger = &MockZetaCoreBridge{} type MockZetaCoreBridge struct { paused bool zetaChain chains.Chain + + // the mock data for testing + // pending cctxs + pendingCctxs map[int64][]*crosschaintypes.CrossChainTx + + // rate limiter flags + rateLimiterFlags *crosschaintypes.RateLimiterFlags + + // rate limiter input + input *crosschaintypes.QueryRateLimiterInputResponse } func NewMockZetaCoreBridge() *MockZetaCoreBridge { @@ -35,12 +46,13 @@ func NewMockZetaCoreBridge() *MockZetaCoreBridge { panic(err) } return &MockZetaCoreBridge{ - paused: false, - zetaChain: zetaChain, + paused: false, + zetaChain: zetaChain, + pendingCctxs: map[int64][]*crosschaintypes.CrossChainTx{}, } } -func (z *MockZetaCoreBridge) PostVoteInbound(_, _ uint64, _ *cctxtypes.MsgVoteOnObservedInboundTx) (string, string, error) { +func (z *MockZetaCoreBridge) PostVoteInbound(_, _ uint64, _ *crosschaintypes.MsgVoteOnObservedInboundTx) (string, string, error) { if z.paused { return "", "", errors.New(ErrMsgPaused) } @@ -107,25 +119,35 @@ func (z *MockZetaCoreBridge) GetZetaBlockHeight() (int64, error) { return 0, nil } -func (z *MockZetaCoreBridge) GetLastBlockHeightByChain(_ chains.Chain) (*cctxtypes.LastBlockHeight, error) { +func (z *MockZetaCoreBridge) GetLastBlockHeightByChain(_ chains.Chain) (*crosschaintypes.LastBlockHeight, error) { if z.paused { return nil, errors.New(ErrMsgPaused) } - return &cctxtypes.LastBlockHeight{}, nil + return &crosschaintypes.LastBlockHeight{}, nil +} + +func (z *MockZetaCoreBridge) GetRateLimiterInput(_ int64) (crosschaintypes.QueryRateLimiterInputResponse, error) { + if z.paused { + return crosschaintypes.QueryRateLimiterInputResponse{}, errors.New(ErrMsgPaused) + } + if z.input == nil { + return crosschaintypes.QueryRateLimiterInputResponse{}, errors.New(ErrMsgRPCFailed) + } + return *z.input, nil } -func (z *MockZetaCoreBridge) ListPendingCctx(_ int64) ([]*cctxtypes.CrossChainTx, uint64, error) { +func (z *MockZetaCoreBridge) ListPendingCctx(chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) { if z.paused { return nil, 0, errors.New(ErrMsgPaused) } - return []*cctxtypes.CrossChainTx{}, 0, nil + return z.pendingCctxs[chainID], 0, nil } -func (z *MockZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*cctxtypes.CrossChainTx, uint64, int64, string, bool, error) { +func (z *MockZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, int64, string, bool, error) { if z.paused { return nil, 0, 0, "", false, errors.New(ErrMsgPaused) } - return []*cctxtypes.CrossChainTx{}, 0, 0, "", false, nil + return []*crosschaintypes.CrossChainTx{}, 0, 0, "", false, nil } func (z *MockZetaCoreBridge) GetPendingNoncesByChain(_ int64) (observerTypes.PendingNonces, error) { @@ -135,25 +157,25 @@ func (z *MockZetaCoreBridge) GetPendingNoncesByChain(_ int64) (observerTypes.Pen return observerTypes.PendingNonces{}, nil } -func (z *MockZetaCoreBridge) GetCctxByNonce(_ int64, _ uint64) (*cctxtypes.CrossChainTx, error) { +func (z *MockZetaCoreBridge) GetCctxByNonce(_ int64, _ uint64) (*crosschaintypes.CrossChainTx, error) { if z.paused { return nil, errors.New(ErrMsgPaused) } - return &cctxtypes.CrossChainTx{}, nil + return &crosschaintypes.CrossChainTx{}, nil } -func (z *MockZetaCoreBridge) GetOutTxTracker(_ chains.Chain, _ uint64) (*cctxtypes.OutTxTracker, error) { +func (z *MockZetaCoreBridge) GetOutTxTracker(_ chains.Chain, _ uint64) (*crosschaintypes.OutTxTracker, error) { if z.paused { return nil, errors.New(ErrMsgPaused) } - return &cctxtypes.OutTxTracker{}, nil + return &crosschaintypes.OutTxTracker{}, nil } -func (z *MockZetaCoreBridge) GetAllOutTxTrackerByChain(_ int64, _ interfaces.Order) ([]cctxtypes.OutTxTracker, error) { +func (z *MockZetaCoreBridge) GetAllOutTxTrackerByChain(_ int64, _ interfaces.Order) ([]crosschaintypes.OutTxTracker, error) { if z.paused { return nil, errors.New(ErrMsgPaused) } - return []cctxtypes.OutTxTracker{}, nil + return []crosschaintypes.OutTxTracker{}, nil } func (z *MockZetaCoreBridge) GetCrosschainFlags() (observerTypes.CrosschainFlags, error) { @@ -163,6 +185,16 @@ func (z *MockZetaCoreBridge) GetCrosschainFlags() (observerTypes.CrosschainFlags return observerTypes.CrosschainFlags{}, nil } +func (z *MockZetaCoreBridge) GetRateLimiterFlags() (crosschaintypes.RateLimiterFlags, error) { + if z.paused { + return crosschaintypes.RateLimiterFlags{}, errors.New(ErrMsgPaused) + } + if z.rateLimiterFlags == nil { + return crosschaintypes.RateLimiterFlags{}, errors.New(ErrMsgRPCFailed) + } + return *z.rateLimiterFlags, nil +} + func (z *MockZetaCoreBridge) GetObserverList() ([]string, error) { if z.paused { return nil, errors.New(ErrMsgPaused) @@ -184,11 +216,11 @@ func (z *MockZetaCoreBridge) GetBtcTssAddress(_ int64) (string, error) { return testutils.TSSAddressBTCMainnet, nil } -func (z *MockZetaCoreBridge) GetInboundTrackersForChain(_ int64) ([]cctxtypes.InTxTracker, error) { +func (z *MockZetaCoreBridge) GetInboundTrackersForChain(_ int64) ([]crosschaintypes.InTxTracker, error) { if z.paused { return nil, errors.New(ErrMsgPaused) } - return []cctxtypes.InTxTracker{}, nil + return []crosschaintypes.InTxTracker{}, nil } func (z *MockZetaCoreBridge) GetLogger() *zerolog.Logger { @@ -213,3 +245,22 @@ func (z *MockZetaCoreBridge) GetZetaHotKeyBalance() (math.Int, error) { } return math.NewInt(0), nil } + +// ---------------------------------------------------------------------------- +// Feed data to the mock zeta bridge for testing +// ---------------------------------------------------------------------------- + +func (z *MockZetaCoreBridge) WithPendingCctx(chainID int64, cctxs []*crosschaintypes.CrossChainTx) *MockZetaCoreBridge { + z.pendingCctxs[chainID] = cctxs + return z +} + +func (z *MockZetaCoreBridge) WithRateLimiterFlags(flags *crosschaintypes.RateLimiterFlags) *MockZetaCoreBridge { + z.rateLimiterFlags = flags + return z +} + +func (z *MockZetaCoreBridge) WithRateLimiterInput(input *crosschaintypes.QueryRateLimiterInputResponse) *MockZetaCoreBridge { + z.input = input + return z +} diff --git a/zetaclient/zetabridge/query.go b/zetaclient/zetabridge/query.go index 9c313f364a..cd2c532be5 100644 --- a/zetaclient/zetabridge/query.go +++ b/zetaclient/zetabridge/query.go @@ -17,7 +17,7 @@ import ( "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/proofs" - "github.com/zeta-chain/zetacore/x/crosschain/types" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/interfaces" @@ -33,6 +33,15 @@ func (b *ZetaCoreBridge) GetCrosschainFlags() (observertypes.CrosschainFlags, er return resp.CrosschainFlags, nil } +func (b *ZetaCoreBridge) GetRateLimiterFlags() (crosschaintypes.RateLimiterFlags, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.RateLimiterFlags(context.Background(), &crosschaintypes.QueryRateLimiterFlagsRequest{}) + if err != nil { + return crosschaintypes.RateLimiterFlags{}, err + } + return resp.RateLimiterFlags, nil +} + func (b *ZetaCoreBridge) GetVerificationFlags() (lightclienttypes.VerificationFlags, error) { client := lightclienttypes.NewQueryClient(b.grpcConn) resp, err := client.VerificationFlags(context.Background(), &lightclienttypes.QueryVerificationFlagsRequest{}) @@ -76,27 +85,27 @@ func (b *ZetaCoreBridge) GetUpgradePlan() (*upgradetypes.Plan, error) { return resp.Plan, nil } -func (b *ZetaCoreBridge) GetAllCctx() ([]*types.CrossChainTx, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.CctxAll(context.Background(), &types.QueryAllCctxRequest{}) +func (b *ZetaCoreBridge) GetAllCctx() ([]*crosschaintypes.CrossChainTx, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.CctxAll(context.Background(), &crosschaintypes.QueryAllCctxRequest{}) if err != nil { return nil, err } return resp.CrossChainTx, nil } -func (b *ZetaCoreBridge) GetCctxByHash(sendHash string) (*types.CrossChainTx, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.Cctx(context.Background(), &types.QueryGetCctxRequest{Index: sendHash}) +func (b *ZetaCoreBridge) GetCctxByHash(sendHash string) (*crosschaintypes.CrossChainTx, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.Cctx(context.Background(), &crosschaintypes.QueryGetCctxRequest{Index: sendHash}) if err != nil { return nil, err } return resp.CrossChainTx, nil } -func (b *ZetaCoreBridge) GetCctxByNonce(chainID int64, nonce uint64) (*types.CrossChainTx, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.CctxByNonce(context.Background(), &types.QueryGetCctxByNonceRequest{ +func (b *ZetaCoreBridge) GetCctxByNonce(chainID int64, nonce uint64) (*crosschaintypes.CrossChainTx, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.CctxByNonce(context.Background(), &crosschaintypes.QueryGetCctxByNonceRequest{ ChainID: chainID, Nonce: nonce, }) @@ -120,14 +129,33 @@ func (b *ZetaCoreBridge) GetObserverList() ([]string, error) { return nil, err } +// GetRateLimiterInput returns input data for the rate limit checker +func (b *ZetaCoreBridge) GetRateLimiterInput(window int64) (crosschaintypes.QueryRateLimiterInputResponse, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) + resp, err := client.RateLimiterInput( + context.Background(), + &crosschaintypes.QueryRateLimiterInputRequest{ + Window: window, + }, + maxSizeOption, + ) + if err != nil { + return crosschaintypes.QueryRateLimiterInputResponse{}, err + } + return *resp, nil +} + // ListPendingCctx returns a list of pending cctxs for a given chainID // - The max size of the list is crosschainkeeper.MaxPendingCctxs -func (b *ZetaCoreBridge) ListPendingCctx(chainID int64) ([]*types.CrossChainTx, uint64, error) { - client := types.NewQueryClient(b.grpcConn) +func (b *ZetaCoreBridge) ListPendingCctx(chainID int64) ([]*crosschaintypes.CrossChainTx, uint64, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) resp, err := client.ListPendingCctx( context.Background(), - &types.QueryListPendingCctxRequest{ChainId: chainID}, + &crosschaintypes.QueryListPendingCctxRequest{ + ChainId: chainID, + }, maxSizeOption, ) if err != nil { @@ -139,12 +167,12 @@ 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, int64, string, bool, error) { - client := types.NewQueryClient(b.grpcConn) +func (b *ZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*crosschaintypes.CrossChainTx, uint64, int64, string, bool, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) resp, err := client.ListPendingCctxWithinRateLimit( context.Background(), - &types.QueryListPendingCctxWithinRateLimitRequest{}, + &crosschaintypes.QueryListPendingCctxWithinRateLimitRequest{}, maxSizeOption, ) if err != nil { @@ -154,8 +182,8 @@ func (b *ZetaCoreBridge) ListPendingCctxWithinRatelimit() ([]*types.CrossChainTx } func (b *ZetaCoreBridge) GetAbortedZetaAmount() (string, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.ZetaAccounting(context.Background(), &types.QueryZetaAccountingRequest{}) + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.ZetaAccounting(context.Background(), &crosschaintypes.QueryZetaAccountingRequest{}) if err != nil { return "", err } @@ -189,9 +217,9 @@ func (b *ZetaCoreBridge) GetZetaTokenSupplyOnNode() (sdkmath.Int, error) { return resp.GetAmount().Amount, nil } -func (b *ZetaCoreBridge) GetLastBlockHeight() ([]*types.LastBlockHeight, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.LastBlockHeightAll(context.Background(), &types.QueryAllLastBlockHeightRequest{}) +func (b *ZetaCoreBridge) GetLastBlockHeight() ([]*crosschaintypes.LastBlockHeight, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.LastBlockHeightAll(context.Background(), &crosschaintypes.QueryAllLastBlockHeightRequest{}) if err != nil { b.logger.Error().Err(err).Msg("query GetBlockHeight error") return nil, err @@ -222,9 +250,9 @@ func (b *ZetaCoreBridge) GetNodeInfo() (*tmservice.GetNodeInfoResponse, error) { return nil, err } -func (b *ZetaCoreBridge) GetLastBlockHeightByChain(chain chains.Chain) (*types.LastBlockHeight, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.LastBlockHeight(context.Background(), &types.QueryGetLastBlockHeightRequest{Index: chain.ChainName.String()}) +func (b *ZetaCoreBridge) GetLastBlockHeightByChain(chain chains.Chain) (*crosschaintypes.LastBlockHeight, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.LastBlockHeight(context.Background(), &crosschaintypes.QueryGetLastBlockHeightRequest{Index: chain.ChainName.String()}) if err != nil { return nil, err } @@ -232,8 +260,8 @@ func (b *ZetaCoreBridge) GetLastBlockHeightByChain(chain chains.Chain) (*types.L } func (b *ZetaCoreBridge) GetZetaBlockHeight() (int64, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.LastZetaHeight(context.Background(), &types.QueryLastZetaHeightRequest{}) + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.LastZetaHeight(context.Background(), &crosschaintypes.QueryLastZetaHeightRequest{}) if err != nil { return 0, err } @@ -304,9 +332,9 @@ func (b *ZetaCoreBridge) GetBallot(ballotIdentifier string) (*observertypes.Quer return resp, nil } -func (b *ZetaCoreBridge) GetInboundTrackersForChain(chainID int64) ([]types.InTxTracker, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.InTxTrackerAllByChain(context.Background(), &types.QueryAllInTxTrackerByChainRequest{ChainId: chainID}) +func (b *ZetaCoreBridge) GetInboundTrackersForChain(chainID int64) ([]crosschaintypes.InTxTracker, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.InTxTrackerAllByChain(context.Background(), &crosschaintypes.QueryAllInTxTrackerByChainRequest{ChainId: chainID}) if err != nil { return nil, err } @@ -351,9 +379,9 @@ func (b *ZetaCoreBridge) GetTssHistory() ([]observertypes.TSS, error) { return resp.TssList, nil } -func (b *ZetaCoreBridge) GetOutTxTracker(chain chains.Chain, nonce uint64) (*types.OutTxTracker, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.OutTxTracker(context.Background(), &types.QueryGetOutTxTrackerRequest{ +func (b *ZetaCoreBridge) GetOutTxTracker(chain chains.Chain, nonce uint64) (*crosschaintypes.OutTxTracker, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.OutTxTracker(context.Background(), &crosschaintypes.QueryGetOutTxTrackerRequest{ ChainID: chain.ChainId, Nonce: nonce, }) @@ -363,9 +391,9 @@ func (b *ZetaCoreBridge) GetOutTxTracker(chain chains.Chain, nonce uint64) (*typ return &resp.OutTxTracker, nil } -func (b *ZetaCoreBridge) GetAllOutTxTrackerByChain(chainID int64, order interfaces.Order) ([]types.OutTxTracker, error) { - client := types.NewQueryClient(b.grpcConn) - resp, err := client.OutTxTrackerAllByChain(context.Background(), &types.QueryAllOutTxTrackerByChainRequest{ +func (b *ZetaCoreBridge) GetAllOutTxTrackerByChain(chainID int64, order interfaces.Order) ([]crosschaintypes.OutTxTracker, error) { + client := crosschaintypes.NewQueryClient(b.grpcConn) + resp, err := client.OutTxTrackerAllByChain(context.Background(), &crosschaintypes.QueryAllOutTxTrackerByChainRequest{ Chain: chainID, Pagination: &query.PageRequest{ Key: nil, diff --git a/zetaclient/zetabridge/query_test.go b/zetaclient/zetabridge/query_test.go index ea76fa78fd..1d232837a5 100644 --- a/zetaclient/zetabridge/query_test.go +++ b/zetaclient/zetabridge/query_test.go @@ -15,6 +15,7 @@ import ( "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/testutil/sample" crosschainTypes "github.com/zeta-chain/zetacore/x/crosschain/types" lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" @@ -102,6 +103,29 @@ func TestZetaCoreBridge_GetCrosschainFlags(t *testing.T) { require.Equal(t, expectedOutput.CrosschainFlags, resp) } +func TestZetaCoreBridge_GetRateLimiterFlags(t *testing.T) { + // create sample flags + rateLimiterFlags := sample.RateLimiterFlags() + expectedOutput := crosschainTypes.QueryRateLimiterFlagsResponse{ + RateLimiterFlags: rateLimiterFlags, + } + + // setup mock server + input := crosschainTypes.QueryRateLimiterFlagsRequest{} + method := "/zetachain.zetacore.crosschain.Query/RateLimiterFlags" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + // query + resp, err := zetabridge.GetRateLimiterFlags() + require.NoError(t, err) + require.Equal(t, expectedOutput.RateLimiterFlags, resp) +} + func TestZetaCoreBridge_GetVerificationFlags(t *testing.T) { expectedOutput := lightclienttypes.QueryVerificationFlagsResponse{VerificationFlags: lightclienttypes.VerificationFlags{ EthTypeChainEnabled: true, @@ -266,6 +290,30 @@ func TestZetaCoreBridge_GetObserverList(t *testing.T) { require.Equal(t, expectedOutput.Observers, resp) } +func TestZetaCoreBridge_GetRateLimiterInput(t *testing.T) { + expectedOutput := crosschainTypes.QueryRateLimiterInputResponse{ + Height: 10, + CctxsMissed: []*crosschainTypes.CrossChainTx{sample.CrossChainTx(t, "1-1")}, + CctxsPending: []*crosschainTypes.CrossChainTx{sample.CrossChainTx(t, "1-2")}, + TotalPending: 1, + PastCctxsValue: "123456", + PendingCctxsValue: "1234", + LowestPendingCctxHeight: 2, + } + input := crosschainTypes.QueryRateLimiterInputRequest{Window: 10} + method := "/zetachain.zetacore.crosschain.Query/RateLimiterInput" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetRateLimiterInput(10) + require.NoError(t, err) + require.Equal(t, expectedOutput, resp) +} + func TestZetaCoreBridge_ListPendingCctx(t *testing.T) { expectedOutput := crosschainTypes.QueryListPendingCctxResponse{ CrossChainTx: []*crosschainTypes.CrossChainTx{ diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 535f741db1..b0688c2856 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -9,6 +9,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" "github.com/zeta-chain/zetacore/pkg/chains" + zetamath "github.com/zeta-chain/zetacore/pkg/math" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" @@ -17,6 +18,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/interfaces" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" + "github.com/zeta-chain/zetacore/zetaclient/ratelimiter" ) const ( @@ -93,6 +95,7 @@ func (co *CoreObserver) MonitorCore(appContext *appcontext.AppContext) { func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { outTxMan := outtxprocessor.NewOutTxProcessorManager(co.logger.ChainLogger) observeTicker := time.NewTicker(3 * time.Second) + sampledLogger := co.logger.ZetaChainWatcher.Sample(&zerolog.BasicSampler{N: 10}) var lastBlockNum int64 for { select { @@ -133,21 +136,18 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { // set current hot key burn rate metrics.HotKeyBurnRate.Set(float64(co.ts.HotKeyBurnRate.GetBurnRate().Int64())) + // get supported foreign chains + coreContext := appContext.ZetaCoreContext() + supportedForeignChains := coreContext.GetEnabledForeignChains() + // query pending cctxs across all foreign chains with rate limit - cctxMap, withdrawWindow, withdrawRate, err := co.getAllPendingCctxWithRatelimit() + cctxMap, err := co.GetPendingCctxsWithinRatelimit(supportedForeignChains, sampledLogger) 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: withdraw window is %d, withdraw rate is %s", withdrawWindow, withdrawRate) - } // schedule keysign for pending cctxs on each chain - coreContext := appContext.ZetaCoreContext() - supportedChains := coreContext.GetEnabledChains() - for _, c := range supportedChains { + for _, c := range supportedForeignChains { if c.IsZetaChain() { continue } @@ -194,29 +194,6 @@ 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, int64, string, error) { - cctxList, totalPending, withdrawWindow, withdrawRate, rateLimitExceeded, err := co.bridge.ListPendingCctxWithinRatelimit() - if err != nil { - return nil, 0, "", err - } - if rateLimitExceeded { - co.logger.ZetaChainWatcher.Warn().Msgf("rate limit exceeded, fetched %d cctxs out of %d", len(cctxList), totalPending) - } - - // classify pending cctxs by chain id - cctxMap := make(map[int64][]*types.CrossChainTx) - for _, cctx := range cctxList { - chainID := cctx.GetCurrentOutTxParam().ReceiverChainId - if _, found := cctxMap[chainID]; !found { - cctxMap[chainID] = make([]*types.CrossChainTx, 0) - } - cctxMap[chainID] = append(cctxMap[chainID], cctx) - } - - return cctxMap, withdrawWindow, withdrawRate, nil -} - // scheduleCctxEVM schedules evm outtx keysign on each ZetaChain block (the ticker) func (co *CoreObserver) scheduleCctxEVM( outTxMan *outtxprocessor.Processor, @@ -413,3 +390,51 @@ func (co *CoreObserver) GetUpdatedChainClient(coreContext *corecontext.ZetaCoreC } return chainOb, nil } + +// GetPendingCctxsWithinRatelimit get pending cctxs across foreign chains within rate limit +func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Chain, logger zerolog.Logger) (map[int64][]*types.CrossChainTx, error) { + // get rate limiter flags + rateLimitFlags, err := co.bridge.GetRateLimiterFlags() + if err != nil { + return nil, err + } + + // apply rate limiter or not according to the flags + applyLimit := ratelimiter.IsRateLimiterUsable(rateLimitFlags) + + // fallback to non-rate-limited query if rate limiter is not usable + cctxsMap := make(map[int64][]*types.CrossChainTx) + if !applyLimit { + for _, chain := range foreignChains { + resp, _, err := co.bridge.ListPendingCctx(chain.ChainId) + if err == nil && resp != nil { + cctxsMap[chain.ChainId] = resp + } + } + return cctxsMap, nil + } + + // query rate limiter input + resp, err := co.bridge.GetRateLimiterInput(rateLimitFlags.Window) + if err != nil { + return nil, err + } + input, ok := ratelimiter.NewInput(resp) + if !ok { + return nil, fmt.Errorf("failed to create rate limiter input") + } + + // apply rate limiter + output := ratelimiter.ApplyRateLimiter(input, rateLimitFlags.Window, rateLimitFlags.Rate) + logger.Info().Msgf( + "current window: %d, current rate: %s", output.CurrentWithdrawWindow, output.CurrentWithdrawRate.String()) + + // set metrics + percentage := zetamath.Percentage(output.CurrentWithdrawRate.BigInt(), rateLimitFlags.Rate.BigInt()) + if percentage != nil { + percentageFloat, _ := percentage.Float64() + metrics.PercentageOfRateReached.Set(percentageFloat) + } + + return output.CctxsMap, nil +} diff --git a/zetaclient/zetacore_observer_test.go b/zetaclient/zetacore_observer_test.go index d47c062757..1f53da66f8 100644 --- a/zetaclient/zetacore_observer_test.go +++ b/zetaclient/zetacore_observer_test.go @@ -8,7 +8,10 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/testutil/sample" + crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/config" corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" @@ -17,7 +20,13 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/testutils/stub" ) -func MockCoreObserver(t *testing.T, evmChain, btcChain chains.Chain, evmChainParams, btcChainParams *observertypes.ChainParams) *CoreObserver { +// MockCoreObserver creates a mock core observer for testing +func MockCoreObserver( + t *testing.T, + bridge interfaces.ZetaCoreBridger, + evmChain, btcChain chains.Chain, + evmChainParams, btcChainParams *observertypes.ChainParams, +) *CoreObserver { // create mock signers and clients evmSigner := stub.NewEVMSigner( evmChain, @@ -30,6 +39,7 @@ func MockCoreObserver(t *testing.T, evmChain, btcChain chains.Chain, evmChainPar // create core observer observer := &CoreObserver{ + bridge: bridge, signerMap: map[int64]interfaces.ChainSigner{ evmChain.ChainId: evmSigner, btcChain.ChainId: btcSigner, @@ -92,14 +102,14 @@ func Test_GetUpdatedSigner(t *testing.T) { } t.Run("signer should not be found", func(t *testing.T) { - observer := MockCoreObserver(t, evmChain, btcChain, evmChainParams, btcChainParams) + observer := MockCoreObserver(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) coreContext := CreateCoreContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) // BSC signer should not be found _, err := observer.GetUpdatedSigner(coreContext, chains.BscMainnetChain().ChainId) require.ErrorContains(t, err, "signer not found") }) t.Run("should be able to update connector and erc20 custody address", func(t *testing.T) { - observer := MockCoreObserver(t, evmChain, btcChain, evmChainParams, btcChainParams) + observer := MockCoreObserver(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) coreContext := CreateCoreContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) // update signer with new connector and erc20 custody address signer, err := observer.GetUpdatedSigner(coreContext, evmChain.ChainId) @@ -157,14 +167,14 @@ func Test_GetUpdatedChainClient(t *testing.T) { } t.Run("evm chain client should not be found", func(t *testing.T) { - observer := MockCoreObserver(t, evmChain, btcChain, evmChainParams, btcChainParams) + observer := MockCoreObserver(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) coreContext := CreateCoreContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) // BSC chain client should not be found _, err := observer.GetUpdatedChainClient(coreContext, chains.BscMainnetChain().ChainId) require.ErrorContains(t, err, "chain client not found") }) t.Run("chain params in evm chain client should be updated successfully", func(t *testing.T) { - observer := MockCoreObserver(t, evmChain, btcChain, evmChainParams, btcChainParams) + observer := MockCoreObserver(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) coreContext := CreateCoreContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) // update evm chain client with new chain params chainOb, err := observer.GetUpdatedChainClient(coreContext, evmChain.ChainId) @@ -173,14 +183,14 @@ func Test_GetUpdatedChainClient(t *testing.T) { require.True(t, observertypes.ChainParamsEqual(*evmChainParamsNew, chainOb.GetChainParams())) }) t.Run("btc chain client should not be found", func(t *testing.T) { - observer := MockCoreObserver(t, evmChain, btcChain, evmChainParams, btcChainParams) + observer := MockCoreObserver(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) coreContext := CreateCoreContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) // BTC testnet chain client should not be found _, err := observer.GetUpdatedChainClient(coreContext, chains.BtcTestNetChain().ChainId) require.ErrorContains(t, err, "chain client not found") }) t.Run("chain params in btc chain client should be updated successfully", func(t *testing.T) { - observer := MockCoreObserver(t, evmChain, btcChain, evmChainParams, btcChainParams) + observer := MockCoreObserver(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) coreContext := CreateCoreContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) // update btc chain client with new chain params chainOb, err := observer.GetUpdatedChainClient(coreContext, btcChain.ChainId) @@ -189,3 +199,135 @@ func Test_GetUpdatedChainClient(t *testing.T) { require.True(t, observertypes.ChainParamsEqual(*btcChainParamsNew, chainOb.GetChainParams())) }) } + +func Test_GetPendingCctxsWithinRatelimit(t *testing.T) { + // define test foreign chains + ethChain := chains.EthChain() + btcChain := chains.BtcMainnetChain() + foreignChains := []chains.Chain{ + ethChain, + btcChain, + } + + // chain params + ethChainParams := &observertypes.ChainParams{ChainId: ethChain.ChainId} + btcChainParams := &observertypes.ChainParams{ChainId: btcChain.ChainId} + + // create 10 missed and 90 pending cctxs for eth chain, the coinType/amount does not matter for this test + ethCctxsMissed := sample.CustomCctxsInBlockRange(t, 1, 10, ethChain.ChainId, coin.CoinType_Gas, "", uint64(2e14), crosschaintypes.CctxStatus_PendingOutbound) + ethCctxsPending := sample.CustomCctxsInBlockRange(t, 11, 100, ethChain.ChainId, coin.CoinType_Gas, "", uint64(2e14), crosschaintypes.CctxStatus_PendingOutbound) + ethCctxsAll := append(append([]*crosschaintypes.CrossChainTx{}, ethCctxsMissed...), ethCctxsPending...) + + // create 10 missed and 90 pending cctxs for btc chain, the coinType/amount does not matter for this test + btcCctxsMissed := sample.CustomCctxsInBlockRange(t, 1, 10, btcChain.ChainId, coin.CoinType_Gas, "", 2000, crosschaintypes.CctxStatus_PendingOutbound) + btcCctxsPending := sample.CustomCctxsInBlockRange(t, 11, 100, btcChain.ChainId, coin.CoinType_Gas, "", 2000, crosschaintypes.CctxStatus_PendingOutbound) + btcCctxsAll := append(append([]*crosschaintypes.CrossChainTx{}, btcCctxsMissed...), btcCctxsPending...) + + // all missed cctxs and all pending cctxs across all foreign chains + allCctxsMissed := crosschainkeeper.SortCctxsByHeightAndChainID( + append(append([]*crosschaintypes.CrossChainTx{}, ethCctxsMissed...), btcCctxsMissed...)) + allCctxsPending := crosschainkeeper.SortCctxsByHeightAndChainID( + append(append([]*crosschaintypes.CrossChainTx{}, ethCctxsPending...), btcCctxsPending...)) + + // define test cases + tests := []struct { + name string + rateLimiterFlags *crosschaintypes.RateLimiterFlags + response *crosschaintypes.QueryRateLimiterInputResponse + ethCctxsFallback []*crosschaintypes.CrossChainTx + btcCctxsFallback []*crosschaintypes.CrossChainTx + + // expected result map + fail bool + expectedCctxsMap map[int64][]*crosschaintypes.CrossChainTx + }{ + { + name: "should return all missed and pending cctxs using fallback", + rateLimiterFlags: &crosschaintypes.RateLimiterFlags{Enabled: false}, + response: &crosschaintypes.QueryRateLimiterInputResponse{}, + ethCctxsFallback: ethCctxsAll, + btcCctxsFallback: btcCctxsAll, + expectedCctxsMap: map[int64][]*crosschaintypes.CrossChainTx{ + ethChain.ChainId: ethCctxsAll, + btcChain.ChainId: btcCctxsAll, + }, + }, + { + name: "should return all missed and pending cctxs without fallback", + rateLimiterFlags: &crosschaintypes.RateLimiterFlags{ + Enabled: true, + Window: 100, + Rate: sdk.NewUint(1e18), // 1 ZETA/block + }, + response: &crosschaintypes.QueryRateLimiterInputResponse{ + Height: 100, + CctxsMissed: allCctxsMissed, + CctxsPending: allCctxsPending, + // #nosec G701 len always positive + TotalPending: uint64(len(allCctxsPending) + len(allCctxsMissed)), + PastCctxsValue: sdk.NewInt(10).Mul(sdk.NewInt(1e18)).String(), // 10 ZETA + PendingCctxsValue: sdk.NewInt(90).Mul(sdk.NewInt(1e18)).String(), // 90 ZETA + LowestPendingCctxHeight: 11, + }, + ethCctxsFallback: nil, + btcCctxsFallback: nil, + expectedCctxsMap: map[int64][]*crosschaintypes.CrossChainTx{ + ethChain.ChainId: ethCctxsAll, + btcChain.ChainId: btcCctxsAll, + }, + }, + { + name: "should fail if cannot query rate limiter flags", + rateLimiterFlags: nil, + fail: true, + }, + { + name: "should fail if cannot query rate limiter input", + rateLimiterFlags: &crosschaintypes.RateLimiterFlags{ + Enabled: true, + Window: 100, + Rate: sdk.NewUint(1e18), // 1 ZETA/block + }, + response: nil, + fail: true, + }, + { + name: "should fail on invalid rate limiter input", + rateLimiterFlags: &crosschaintypes.RateLimiterFlags{ + Enabled: true, + Window: 100, + Rate: sdk.NewUint(1e18), // 1 ZETA/block + }, + response: &crosschaintypes.QueryRateLimiterInputResponse{ + PastCctxsValue: "invalid", + }, + fail: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create mock bridge + bridge := stub.NewMockZetaCoreBridge() + + // load mock data + bridge.WithRateLimiterFlags(tt.rateLimiterFlags) + bridge.WithPendingCctx(ethChain.ChainId, tt.ethCctxsFallback) + bridge.WithPendingCctx(btcChain.ChainId, tt.btcCctxsFallback) + bridge.WithRateLimiterInput(tt.response) + + // create core observer + observer := MockCoreObserver(t, bridge, ethChain, btcChain, ethChainParams, btcChainParams) + + // run the test + cctxsMap, err := observer.GetPendingCctxsWithinRatelimit(foreignChains, zerolog.Logger{}) + if tt.fail { + require.Error(t, err) + require.Nil(t, cctxsMap) + } else { + require.NoError(t, err) + require.Equal(t, tt.expectedCctxsMap, cctxsMap) + } + }) + } +} From c3c885ba7948e7e941f71e4baf181d80398279b9 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 2 May 2024 16:41:24 -0500 Subject: [PATCH 32/40] added more unit tests and updated changelog --- changelog.md | 1 + .../keeper/grpc_query_cctx_rate_limit.go | 2 +- .../keeper/grpc_query_cctx_rate_limit_test.go | 46 +++++++++++++++++++ zetaclient/zetacore_observer.go | 33 ++++++------- 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/changelog.md b/changelog.md index 25753494aa..7f7652e577 100644 --- a/changelog.md +++ b/changelog.md @@ -45,6 +45,7 @@ * [2013](https://github.com/zeta-chain/node/pull/2013) - rename `GasPriceVoter` message to `VoteGasPrice` * [2059](https://github.com/zeta-chain/node/pull/2059) - Remove unused params from all functions in zetanode * [2076](https://github.com/zeta-chain/node/pull/2076) - automatically deposit native zeta to an address if it doesn't exist on ZEVM. +* [2110](https://github.com/zeta-chain/node/pull/2110) - move rate limiter logic to zetaclient side and code refactor. ### Features diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 0f313897ca..75ef53df58 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -14,7 +14,7 @@ import ( "google.golang.org/grpc/status" ) -// RateLimiterInput returns the input data for the rate limiter +// RateLimiterInput collects the input data for the rate limiter func (k Keeper) RateLimiterInput(c context.Context, req *types.QueryRateLimiterInputRequest) (res *types.QueryRateLimiterInputResponse, err error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") 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 90e9ed830e..2a8273467e 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -480,6 +480,52 @@ func TestKeeper_RateLimiterInput(t *testing.T) { } } +func TestKeeper_RateLimiterInput_Errors(t *testing.T) { + t.Run("should fail for empty req", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + _, err := k.RateLimiterInput(ctx, nil) + require.ErrorContains(t, err, "invalid request") + }) + t.Run("window must be positive", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + _, err := k.RateLimiterInput(ctx, &types.QueryRateLimiterInputRequest{ + Window: 0, // 0 window + }) + require.ErrorContains(t, err, "window must be positive") + }) + t.Run("height out of range", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + + // set current height to 0 + ctx = ctx.WithBlockHeight(0) + _, err := k.RateLimiterInput(ctx, &types.QueryRateLimiterInputRequest{ + Window: 100, + }) + require.ErrorContains(t, err, "height out of range") + }) + t.Run("tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + + // no TSS set + _, err := k.RateLimiterInput(ctx, &types.QueryRateLimiterInputRequest{ + Window: 100, + }) + require.ErrorContains(t, err, "tss not found") + }) + t.Run("pending nonces not found", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // Set TSS + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + + _, err := k.RateLimiterInput(ctx, &types.QueryRateLimiterInputRequest{ + Window: 100, + }) + require.ErrorContains(t, err, "pending nonces not found") + }) +} + func TestKeeper_ListPendingCctxWithinRateLimit(t *testing.T) { // create sample TSS tss := sample.Tss() diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index b0688c2856..006ed3daff 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -78,7 +78,7 @@ func NewCoreObserver( func (co *CoreObserver) MonitorCore(appContext *appcontext.AppContext) { myid := co.bridge.GetKeys().GetAddress() co.logger.ZetaChainWatcher.Info().Msgf("Starting Send Scheduler for %s", myid) - go co.startCctxScheduler(appContext) + go co.StartCctxScheduler(appContext) go func() { // bridge queries UpgradePlan from zetabridge and send to its pause channel if upgrade height is reached @@ -91,8 +91,8 @@ func (co *CoreObserver) MonitorCore(appContext *appcontext.AppContext) { }() } -// startCctxScheduler schedules keysigns for cctxs on each ZetaChain block (the ticker) -func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { +// StartCctxScheduler schedules keysigns for cctxs on each ZetaChain block (the ticker) +func (co *CoreObserver) StartCctxScheduler(appContext *appcontext.AppContext) { outTxMan := outtxprocessor.NewOutTxProcessorManager(co.logger.ChainLogger) observeTicker := time.NewTicker(3 * time.Second) sampledLogger := co.logger.ZetaChainWatcher.Sample(&zerolog.BasicSampler{N: 10}) @@ -138,19 +138,16 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { // get supported foreign chains coreContext := appContext.ZetaCoreContext() - supportedForeignChains := coreContext.GetEnabledForeignChains() + foreignChains := coreContext.GetEnabledForeignChains() - // query pending cctxs across all foreign chains with rate limit - cctxMap, err := co.GetPendingCctxsWithinRatelimit(supportedForeignChains, sampledLogger) + // query pending cctxs across all foreign chains within rate limit + cctxMap, err := co.GetPendingCctxsWithinRatelimit(foreignChains, sampledLogger) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("startCctxScheduler: queryPendingCctxWithRatelimit failed") + co.logger.ZetaChainWatcher.Error().Err(err).Msgf("startCctxScheduler: GetPendingCctxsWithinRatelimit failed") } // schedule keysign for pending cctxs on each chain - for _, c := range supportedForeignChains { - if c.IsZetaChain() { - continue - } + for _, c := range foreignChains { // get cctxs from map and set pending transactions prometheus gauge cctxList := cctxMap[c.ChainId] metrics.PendingTxsPerChain.WithLabelValues(c.ChainName.String()).Set(float64(len(cctxList))) @@ -176,9 +173,9 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { // #nosec G701 range is verified zetaHeight := uint64(bn) if chains.IsEVMChain(c.ChainId) { - co.scheduleCctxEVM(outTxMan, zetaHeight, c.ChainId, cctxList, ob, signer) + co.ScheduleCctxEVM(outTxMan, zetaHeight, c.ChainId, cctxList, ob, signer) } else if chains.IsBitcoinChain(c.ChainId) { - co.scheduleCctxBTC(outTxMan, zetaHeight, c.ChainId, cctxList, ob, signer) + co.ScheduleCctxBTC(outTxMan, zetaHeight, c.ChainId, cctxList, ob, signer) } else { co.logger.ZetaChainWatcher.Error().Msgf("startCctxScheduler: unsupported chain %d", c.ChainId) continue @@ -194,8 +191,8 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { } } -// scheduleCctxEVM schedules evm outtx keysign on each ZetaChain block (the ticker) -func (co *CoreObserver) scheduleCctxEVM( +// ScheduleCctxEVM schedules evm outtx keysign on each ZetaChain block (the ticker) +func (co *CoreObserver) ScheduleCctxEVM( outTxMan *outtxprocessor.Processor, zetaHeight uint64, chainID int64, @@ -277,11 +274,11 @@ func (co *CoreObserver) scheduleCctxEVM( } } -// scheduleCctxBTC schedules bitcoin outtx keysign on each ZetaChain block (the ticker) +// ScheduleCctxBTC schedules bitcoin outtx keysign on each ZetaChain block (the ticker) // 1. schedule at most one keysign per ticker // 2. schedule keysign only when nonce-mark UTXO is available // 3. stop keysign when lookahead is reached -func (co *CoreObserver) scheduleCctxBTC( +func (co *CoreObserver) ScheduleCctxBTC( outTxMan *outtxprocessor.Processor, zetaHeight uint64, chainID int64, @@ -427,7 +424,7 @@ func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Ch // apply rate limiter output := ratelimiter.ApplyRateLimiter(input, rateLimitFlags.Window, rateLimitFlags.Rate) logger.Info().Msgf( - "current window: %d, current rate: %s", output.CurrentWithdrawWindow, output.CurrentWithdrawRate.String()) + "current rate limiter window: %d rate: %s", output.CurrentWithdrawWindow, output.CurrentWithdrawRate.String()) // set metrics percentage := zetamath.Percentage(output.CurrentWithdrawRate.BigInt(), rateLimitFlags.Rate.BigInt()) From 21a2076ff3c51dd9ffee54356049d289cae477eb Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 2 May 2024 17:43:01 -0500 Subject: [PATCH 33/40] print more details from rate limiter output --- zetaclient/zetacore_observer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 006ed3daff..86708aa30f 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -423,14 +423,14 @@ func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Ch // apply rate limiter output := ratelimiter.ApplyRateLimiter(input, rateLimitFlags.Window, rateLimitFlags.Rate) - logger.Info().Msgf( - "current rate limiter window: %d rate: %s", output.CurrentWithdrawWindow, output.CurrentWithdrawRate.String()) // set metrics percentage := zetamath.Percentage(output.CurrentWithdrawRate.BigInt(), rateLimitFlags.Rate.BigInt()) if percentage != nil { percentageFloat, _ := percentage.Float64() metrics.PercentageOfRateReached.Set(percentageFloat) + logger.Info().Msgf("current rate limiter window: %d rate: %s, percentage: %f", + output.CurrentWithdrawWindow, output.CurrentWithdrawRate.String(), percentageFloat) } return output.CctxsMap, nil From cd3e0034ee5c059765a6753c20527dff18431094 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 2 May 2024 18:45:11 -0500 Subject: [PATCH 34/40] reorder observer methods declaration --- zetaclient/zetacore_observer.go | 206 ++++++++++++++++---------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 802fd5923b..da45c4fb31 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -95,6 +95,109 @@ func (co *CoreObserver) MonitorCore(appContext *appcontext.AppContext) { }() } +// GetUpdatedSigner returns signer with updated chain parameters +func (co *CoreObserver) GetUpdatedSigner(coreContext *corecontext.ZetaCoreContext, chainID int64) (interfaces.ChainSigner, error) { + signer, found := co.signerMap[chainID] + if !found { + return nil, fmt.Errorf("signer not found for chainID %d", chainID) + } + // update EVM signer parameters only. BTC signer doesn't use chain parameters for now. + if chains.IsEVMChain(chainID) { + evmParams, found := coreContext.GetEVMChainParams(chainID) + if found { + // update zeta connector and ERC20 custody addresses + zetaConnectorAddress := ethcommon.HexToAddress(evmParams.GetConnectorContractAddress()) + erc20CustodyAddress := ethcommon.HexToAddress(evmParams.GetErc20CustodyContractAddress()) + if zetaConnectorAddress != signer.GetZetaConnectorAddress() { + signer.SetZetaConnectorAddress(zetaConnectorAddress) + co.logger.ZetaChainWatcher.Info().Msgf( + "updated zeta connector address for chainID %d, new address: %s", chainID, zetaConnectorAddress) + } + if erc20CustodyAddress != signer.GetERC20CustodyAddress() { + signer.SetERC20CustodyAddress(erc20CustodyAddress) + co.logger.ZetaChainWatcher.Info().Msgf( + "updated ERC20 custody address for chainID %d, new address: %s", chainID, erc20CustodyAddress) + } + } + } + return signer, nil +} + +// GetUpdatedChainClient returns chain client object with updated chain parameters +func (co *CoreObserver) GetUpdatedChainClient(coreContext *corecontext.ZetaCoreContext, chainID int64) (interfaces.ChainClient, error) { + chainOb, found := co.clientMap[chainID] + if !found { + return nil, fmt.Errorf("chain client not found for chainID %d", chainID) + } + // update chain client chain parameters + curParams := chainOb.GetChainParams() + if chains.IsEVMChain(chainID) { + evmParams, found := coreContext.GetEVMChainParams(chainID) + if found && !observertypes.ChainParamsEqual(curParams, *evmParams) { + chainOb.SetChainParams(*evmParams) + co.logger.ZetaChainWatcher.Info().Msgf( + "updated chain params for chainID %d, new params: %v", chainID, *evmParams) + } + } else if chains.IsBitcoinChain(chainID) { + _, btcParams, found := coreContext.GetBTCChainParams() + + if found && !observertypes.ChainParamsEqual(curParams, *btcParams) { + chainOb.SetChainParams(*btcParams) + co.logger.ZetaChainWatcher.Info().Msgf( + "updated chain params for Bitcoin, new params: %v", *btcParams) + } + } + return chainOb, nil +} + +// GetPendingCctxsWithinRatelimit get pending cctxs across foreign chains within rate limit +func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Chain, logger zerolog.Logger) (map[int64][]*types.CrossChainTx, error) { + // get rate limiter flags + rateLimitFlags, err := co.bridge.GetRateLimiterFlags() + if err != nil { + return nil, err + } + + // apply rate limiter or not according to the flags + applyLimit := ratelimiter.IsRateLimiterUsable(rateLimitFlags) + + // fallback to non-rate-limited query if rate limiter is not usable + cctxsMap := make(map[int64][]*types.CrossChainTx) + if !applyLimit { + for _, chain := range foreignChains { + resp, _, err := co.bridge.ListPendingCctx(chain.ChainId) + if err == nil && resp != nil { + cctxsMap[chain.ChainId] = resp + } + } + return cctxsMap, nil + } + + // query rate limiter input + resp, err := co.bridge.GetRateLimiterInput(rateLimitFlags.Window) + if err != nil { + return nil, err + } + input, ok := ratelimiter.NewInput(resp) + if !ok { + return nil, fmt.Errorf("failed to create rate limiter input") + } + + // apply rate limiter + output := ratelimiter.ApplyRateLimiter(input, rateLimitFlags.Window, rateLimitFlags.Rate) + + // set metrics + percentage := zetamath.Percentage(output.CurrentWithdrawRate.BigInt(), rateLimitFlags.Rate.BigInt()) + if percentage != nil { + percentageFloat, _ := percentage.Float64() + metrics.PercentageOfRateReached.Set(percentageFloat) + logger.Info().Msgf("current rate limiter window: %d rate: %s, percentage: %f", + output.CurrentWithdrawWindow, output.CurrentWithdrawRate.String(), percentageFloat) + } + + return output.CctxsMap, nil +} + // StartCctxScheduler schedules keysigns for cctxs on each ZetaChain block (the ticker) func (co *CoreObserver) StartCctxScheduler(appContext *appcontext.AppContext) { outTxMan := outtxprocessor.NewOutTxProcessorManager(co.logger.ChainLogger) @@ -337,106 +440,3 @@ func (co *CoreObserver) ScheduleCctxBTC( } } } - -// GetUpdatedSigner returns signer with updated chain parameters -func (co *CoreObserver) GetUpdatedSigner(coreContext *corecontext.ZetaCoreContext, chainID int64) (interfaces.ChainSigner, error) { - signer, found := co.signerMap[chainID] - if !found { - return nil, fmt.Errorf("signer not found for chainID %d", chainID) - } - // update EVM signer parameters only. BTC signer doesn't use chain parameters for now. - if chains.IsEVMChain(chainID) { - evmParams, found := coreContext.GetEVMChainParams(chainID) - if found { - // update zeta connector and ERC20 custody addresses - zetaConnectorAddress := ethcommon.HexToAddress(evmParams.GetConnectorContractAddress()) - erc20CustodyAddress := ethcommon.HexToAddress(evmParams.GetErc20CustodyContractAddress()) - if zetaConnectorAddress != signer.GetZetaConnectorAddress() { - signer.SetZetaConnectorAddress(zetaConnectorAddress) - co.logger.ZetaChainWatcher.Info().Msgf( - "updated zeta connector address for chainID %d, new address: %s", chainID, zetaConnectorAddress) - } - if erc20CustodyAddress != signer.GetERC20CustodyAddress() { - signer.SetERC20CustodyAddress(erc20CustodyAddress) - co.logger.ZetaChainWatcher.Info().Msgf( - "updated ERC20 custody address for chainID %d, new address: %s", chainID, erc20CustodyAddress) - } - } - } - return signer, nil -} - -// GetUpdatedChainClient returns chain client object with updated chain parameters -func (co *CoreObserver) GetUpdatedChainClient(coreContext *corecontext.ZetaCoreContext, chainID int64) (interfaces.ChainClient, error) { - chainOb, found := co.clientMap[chainID] - if !found { - return nil, fmt.Errorf("chain client not found for chainID %d", chainID) - } - // update chain client chain parameters - curParams := chainOb.GetChainParams() - if chains.IsEVMChain(chainID) { - evmParams, found := coreContext.GetEVMChainParams(chainID) - if found && !observertypes.ChainParamsEqual(curParams, *evmParams) { - chainOb.SetChainParams(*evmParams) - co.logger.ZetaChainWatcher.Info().Msgf( - "updated chain params for chainID %d, new params: %v", chainID, *evmParams) - } - } else if chains.IsBitcoinChain(chainID) { - _, btcParams, found := coreContext.GetBTCChainParams() - - if found && !observertypes.ChainParamsEqual(curParams, *btcParams) { - chainOb.SetChainParams(*btcParams) - co.logger.ZetaChainWatcher.Info().Msgf( - "updated chain params for Bitcoin, new params: %v", *btcParams) - } - } - return chainOb, nil -} - -// GetPendingCctxsWithinRatelimit get pending cctxs across foreign chains within rate limit -func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Chain, logger zerolog.Logger) (map[int64][]*types.CrossChainTx, error) { - // get rate limiter flags - rateLimitFlags, err := co.bridge.GetRateLimiterFlags() - if err != nil { - return nil, err - } - - // apply rate limiter or not according to the flags - applyLimit := ratelimiter.IsRateLimiterUsable(rateLimitFlags) - - // fallback to non-rate-limited query if rate limiter is not usable - cctxsMap := make(map[int64][]*types.CrossChainTx) - if !applyLimit { - for _, chain := range foreignChains { - resp, _, err := co.bridge.ListPendingCctx(chain.ChainId) - if err == nil && resp != nil { - cctxsMap[chain.ChainId] = resp - } - } - return cctxsMap, nil - } - - // query rate limiter input - resp, err := co.bridge.GetRateLimiterInput(rateLimitFlags.Window) - if err != nil { - return nil, err - } - input, ok := ratelimiter.NewInput(resp) - if !ok { - return nil, fmt.Errorf("failed to create rate limiter input") - } - - // apply rate limiter - output := ratelimiter.ApplyRateLimiter(input, rateLimitFlags.Window, rateLimitFlags.Rate) - - // set metrics - percentage := zetamath.Percentage(output.CurrentWithdrawRate.BigInt(), rateLimitFlags.Rate.BigInt()) - if percentage != nil { - percentageFloat, _ := percentage.Float64() - metrics.PercentageOfRateReached.Set(percentageFloat) - logger.Info().Msgf("current rate limiter window: %d rate: %s, percentage: %f", - output.CurrentWithdrawWindow, output.CurrentWithdrawRate.String(), percentageFloat) - } - - return output.CctxsMap, nil -} From e5c4c1f240a7153ba7999bdc7f912daa5b876e89 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 2 May 2024 23:03:41 -0500 Subject: [PATCH 35/40] fix unit test --- zetaclient/core_context/zeta_core_context_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/zetaclient/core_context/zeta_core_context_test.go b/zetaclient/core_context/zeta_core_context_test.go index e9afb74f41..771ff6f920 100644 --- a/zetaclient/core_context/zeta_core_context_test.go +++ b/zetaclient/core_context/zeta_core_context_test.go @@ -158,10 +158,7 @@ func TestUpdateZetaCoreContext(t *testing.T) { ChainName: 2, ChainId: 2, }, - { - ChainName: 3, - ChainId: chains.ZetaTestnetChain().ChainId, - }, + chains.ZetaTestnetChain, } evmChainParamsToUpdate := map[int64]*observertypes.ChainParams{ 1: { From 77acc5bc75273b08ec6091f050c38e74e78e4ddc Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 3 May 2024 15:22:19 -0500 Subject: [PATCH 36/40] moved state irrelevant methods to types; renaming and cleaning --- changelog.md | 3 +- cmd/zetae2e/local/local.go | 1 - testutil/sample/crosschain.go | 26 ++- .../keeper/grpc_query_cctx_rate_limit.go | 105 ++------- .../keeper/grpc_query_cctx_rate_limit_test.go | 185 ++-------------- x/crosschain/keeper/rate_limiter_flags.go | 40 +--- .../keeper/rate_limiter_flags_test.go | 78 ++----- x/crosschain/types/rate_limiter_flags.go | 95 +++++++++ x/crosschain/types/rate_limiter_flags_test.go | 200 ++++++++++++++++++ zetaclient/core_context/zeta_core_context.go | 7 +- .../core_context/zeta_core_context_test.go | 8 +- zetaclient/ratelimiter/rate_limiter_test.go | 4 +- zetaclient/zetacore_observer.go | 104 ++++----- zetaclient/zetacore_observer_test.go | 2 +- 14 files changed, 446 insertions(+), 412 deletions(-) diff --git a/changelog.md b/changelog.md index d60f66427a..8b301fc1c7 100644 --- a/changelog.md +++ b/changelog.md @@ -9,7 +9,7 @@ ### Refactor * [2032](https://github.com/zeta-chain/node/pull/2032) - improve some general structure of the ZetaClient codebase - +* [2110](https://github.com/zeta-chain/node/pull/2110) - move non-query rate limiter logic to zetaclient side and code refactor. ## v16.0.0 @@ -57,7 +57,6 @@ * [2013](https://github.com/zeta-chain/node/pull/2013) - rename `GasPriceVoter` message to `VoteGasPrice` * [2059](https://github.com/zeta-chain/node/pull/2059) - Remove unused params from all functions in zetanode * [2076](https://github.com/zeta-chain/node/pull/2076) - automatically deposit native zeta to an address if it doesn't exist on ZEVM. -* [2110](https://github.com/zeta-chain/node/pull/2110) - move non-query rate limiter logic to zetaclient side and code refactor. * [2071](https://github.com/zeta-chain/node/pull/2071) - Modify chains struct to add all chain related information ### Features diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 39f3be0013..3103810305 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -313,7 +313,6 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestUpdateBytecodeZRC20Name, e2etests.TestUpdateBytecodeConnectorName, e2etests.TestDepositEtherLiquidityCapName, - e2etests.TestRateLimiterName, // TestMigrateChainSupportName tests EVM chain migration. Currently this test doesn't work with Anvil because pre-EIP1559 txs are not supported // See issue below for details diff --git a/testutil/sample/crosschain.go b/testutil/sample/crosschain.go index 72c93f3fad..9142d6f532 100644 --- a/testutil/sample/crosschain.go +++ b/testutil/sample/crosschain.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "math/rand" + "strings" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -50,10 +51,20 @@ func RateLimiterFlags() types.RateLimiterFlags { } } -func AssetRate() *types.AssetRate { +// CustomRateLimiterFlags creates a custom rate limiter flags with the given parameters +func CustomRateLimiterFlags(enabled bool, window int64, rate math.Uint, conversions []types.Conversion) types.RateLimiterFlags { + return types.RateLimiterFlags{ + Enabled: enabled, + Window: window, + Rate: rate, + Conversions: conversions, + } +} + +func AssetRate() types.AssetRate { r := Rand() - return &types.AssetRate{ + return types.AssetRate{ ChainId: r.Int63(), Asset: EthAddress().Hex(), Decimals: uint32(r.Uint64()), @@ -62,6 +73,17 @@ func AssetRate() *types.AssetRate { } } +// CustomAssetRate creates a custom asset rate with the given parameters +func CustomAssetRate(chainID int64, asset string, decimals uint32, coinType coin.CoinType, rate sdk.Dec) types.AssetRate { + return types.AssetRate{ + ChainId: chainID, + Asset: strings.ToLower(asset), + Decimals: decimals, + CoinType: coinType, + Rate: rate, + } +} + func OutTxTracker(t *testing.T, index string) types.OutTxTracker { r := newRandFromStringSeed(t, index) diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 75ef53df58..695c4029b2 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -3,11 +3,9 @@ package keeper import ( "context" "sort" - "strings" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "google.golang.org/grpc/codes" @@ -66,8 +64,11 @@ func (k Keeper) RateLimiterInput(c context.Context, req *types.QueryRateLimiterI // get foreign chains and conversion rates of foreign coins chains := k.zetaObserverKeeper.GetSupportedForeignChains(ctx) - assetRates := k.GetRateLimiterAssetRateList(ctx) - gasAssetRateMap, erc20AssetRateMap := BuildAssetRateMapFromList(assetRates) + _, assetRates, found := k.GetRateLimiterAssetRateList(ctx) + if !found { + return nil, status.Error(codes.Internal, "asset rates not found") + } + gasAssetRateMap, erc20AssetRateMap := types.BuildAssetRateMapFromList(assetRates) // query pending nonces of each foreign chain and get the lowest height of the pending cctxs lowestPendingCctxHeight := int64(0) @@ -129,7 +130,7 @@ func (k Keeper) RateLimiterInput(c context.Context, req *types.QueryRateLimiterI // sum up past cctxs' value within window if inWindow && isPast { - pastCctxsValue = pastCctxsValue.Add(ConvertCctxValue(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) + pastCctxsValue = pastCctxsValue.Add(types.ConvertCctxValue(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) } // add cctx to corresponding list @@ -140,7 +141,7 @@ func (k Keeper) RateLimiterInput(c context.Context, req *types.QueryRateLimiterI } else { cctxsPending = append(cctxsPending, cctx) // sum up non-past pending cctxs' value - pendingCctxsValue = pendingCctxsValue.Add(ConvertCctxValue(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) + pendingCctxsValue = pendingCctxsValue.Add(types.ConvertCctxValue(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) } } } @@ -191,7 +192,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que // check rate limit flags to decide if we should apply rate limit applyLimit := true - rateLimitFlags, found := k.GetRateLimiterFlags(ctx) + rateLimitFlags, assetRates, found := k.GetRateLimiterAssetRateList(ctx) if !found || !rateLimitFlags.Enabled { applyLimit = false } @@ -231,19 +232,10 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que leftWindowBoundary = 0 } - // get the conversion rates for all foreign coins - var gasAssetRateMap map[int64]*types.AssetRate - var erc20AssetRateMap map[int64]map[string]*types.AssetRate - var blockLimitInAzeta sdkmath.Int - var windowLimitInAzeta sdkmath.Int - if applyLimit { - assetRates := k.GetRateLimiterAssetRateList(ctx) - gasAssetRateMap, erc20AssetRateMap = BuildAssetRateMapFromList(assetRates) - - // initiate block limit and window limit in azeta - blockLimitInAzeta = sdkmath.NewIntFromBigInt(rateLimitFlags.Rate.BigInt()) - windowLimitInAzeta = blockLimitInAzeta.Mul(sdkmath.NewInt(rateLimitFlags.Window)) - } + // initiate block limit and window limit in azeta; build asset rate maps + blockLimitInAzeta := sdkmath.NewIntFromBigInt(rateLimitFlags.Rate.BigInt()) + windowLimitInAzeta := blockLimitInAzeta.Mul(sdkmath.NewInt(rateLimitFlags.Window)) + gasAssetRateMap, erc20AssetRateMap := types.BuildAssetRateMapFromList(assetRates) // the criteria to stop adding cctxs to the rpc response maxCCTXsReached := func(cctxs []*types.CrossChainTx) bool { @@ -322,7 +314,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que break } // skip the cctx if rate limit is exceeded but still accumulate the total withdraw value - if inWindow && rateLimitExceeded(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { + if inWindow && types.RateLimitExceeded(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { limitExceeded = true continue } @@ -355,7 +347,7 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que } // skip the cctx if rate limit is exceeded but still accumulate the total withdraw value - if rateLimitExceeded(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { + if types.RateLimitExceeded(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap, &totalWithdrawInAzeta, withdrawLimitInAzeta) { limitExceeded = true continue } @@ -388,72 +380,3 @@ func (k Keeper) ListPendingCctxWithinRateLimit(c context.Context, req *types.Que RateLimitExceeded: limitExceeded, }, nil } - -// ConvertCctxValue converts the value of the cctx to azeta using given conversion rates -func ConvertCctxValue( - chainID int64, - cctx *types.CrossChainTx, - gasAssetRateMap map[int64]*types.AssetRate, - erc20AssetRateMap map[int64]map[string]*types.AssetRate, -) sdkmath.Int { - var rate sdk.Dec - var decimals uint32 - switch cctx.InboundTxParams.CoinType { - case coin.CoinType_Zeta: - // no conversion needed for ZETA - return sdk.NewIntFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) - case coin.CoinType_Gas: - assetRate, found := gasAssetRateMap[chainID] - if !found { - // skip if no rate found for gas coin on this chainID - return sdk.NewInt(0) - } - rate = assetRate.Rate - decimals = assetRate.Decimals - case coin.CoinType_ERC20: - // get the ERC20 coin rate - _, found := erc20AssetRateMap[chainID] - if !found { - // skip if no rate found for this chainID - return sdk.NewInt(0) - } - assetRate := erc20AssetRateMap[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] - rate = assetRate.Rate - decimals = assetRate.Decimals - default: - // skip CoinType_Cmd - return sdk.NewInt(0) - } - // should not happen, return 0 to skip if it happens - if rate.IsNil() || rate.LTE(sdk.NewDec(0)) { - return sdkmath.NewInt(0) - } - - // the whole coin amounts of zeta and zrc20 - // given decimals = 6, the amount will be 10^6 = 1000000 - oneZeta := coin.AzetaPerZeta() - oneZrc20 := sdk.NewDec(10).Power(uint64(decimals)) - - // convert cctx asset amount into azeta amount - // given amountCctx = 2000000, rate = 0.8, decimals = 6 - // amountCctxDec: 2000000 * 0.8 = 1600000.0 - // amountAzetaDec: 1600000.0 * 10e18 / 10e6 = 1600000000000000000.0 - amountCctxDec := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) - amountAzetaDec := amountCctxDec.Mul(rate).Mul(oneZeta).Quo(oneZrc20) - return amountAzetaDec.TruncateInt() -} - -// rateLimitExceeded accumulates the cctx value and then checks if the rate limit is exceeded -// returns true if the rate limit is exceeded -func rateLimitExceeded( - chainID int64, - cctx *types.CrossChainTx, - gasAssetRateMap map[int64]*types.AssetRate, - erc20AssetRateMap map[int64]map[string]*types.AssetRate, - currentCctxValue *sdkmath.Int, - withdrawLimitInAzeta sdkmath.Int, -) bool { - amountZeta := ConvertCctxValue(chainID, cctx, gasAssetRateMap, erc20AssetRateMap) - *currentCctxValue = currentCctxValue.Add(amountZeta) - return currentCctxValue.GT(withdrawLimitInAzeta) -} 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 ea44c05cbb..fc5c96cf09 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go @@ -1,12 +1,9 @@ package keeper_test import ( - "fmt" - "strings" "testing" "cosmossdk.io/math" - sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/coin" @@ -94,168 +91,6 @@ func setupForeignCoins( } } -func Test_ConvertCctxValue(t *testing.T) { - // chain IDs - ethChainID := getValidEthChainID() - btcChainID := getValidBtcChainID() - - // zrc20 addresses for ETH, BTC, USDT and asset for USDT - zrc20ETH := sample.EthAddress().Hex() - zrc20BTC := sample.EthAddress().Hex() - zrc20USDT := sample.EthAddress().Hex() - assetUSDT := sample.EthAddress().Hex() - - k, ctx, _, zk := keepertest.CrosschainKeeper(t) - - // Set TSS - tss := sample.Tss() - zk.ObserverKeeper.SetTSS(ctx, tss) - - // Set foreign coins - setupForeignCoins(t, ctx, zk, zrc20ETH, zrc20BTC, zrc20USDT, assetUSDT) - - // Set rate limiter flags - rateLimiterFlags := createTestRateLimiterFlags(500, math.NewUint(10), zrc20ETH, zrc20BTC, zrc20USDT, "2500", "50000", "0.8") - k.SetRateLimiterFlags(ctx, *rateLimiterFlags) - - // get rate limiter rates - assetRates := k.GetRateLimiterAssetRateList(ctx) - gasAssetRateMap, erc20AssetRateMap := keeper.BuildAssetRateMapFromList(assetRates) - - // test cases - tests := []struct { - name string - - // input - chainID int64 - coinType coin.CoinType - asset string - amount math.Uint - gasAssetRates map[int64]*types.AssetRate - erc20AssetRates map[int64]map[string]*types.AssetRate - - // output - expectedValue sdkmath.Int - }{ - { - name: "should convert cctx ZETA value correctly", - chainID: ethChainID, - coinType: coin.CoinType_Zeta, - asset: "", - amount: sdk.NewUint(3e17), // 0.3 ZETA - gasAssetRates: gasAssetRateMap, - erc20AssetRates: erc20AssetRateMap, - expectedValue: sdk.NewInt(3e17), - }, - { - name: "should convert cctx ETH value correctly", - chainID: ethChainID, - coinType: coin.CoinType_Gas, - asset: "", - amount: sdk.NewUint(3e15), // 0.003 ETH - gasAssetRates: gasAssetRateMap, - erc20AssetRates: erc20AssetRateMap, - expectedValue: sdk.NewInt(75e17), // 0.003 ETH * 2500 = 7.5 ZETA - }, - { - name: "should convert cctx BTC value correctly", - chainID: btcChainID, - coinType: coin.CoinType_Gas, - asset: "", - amount: sdk.NewUint(70000), // 0.0007 BTC - gasAssetRates: gasAssetRateMap, - erc20AssetRates: erc20AssetRateMap, - expectedValue: sdk.NewInt(35).Mul(sdk.NewInt(1e18)), // 0.0007 BTC * 50000 = 35.0 ZETA - }, - { - name: "should convert cctx USDT value correctly", - chainID: ethChainID, - coinType: coin.CoinType_ERC20, - asset: assetUSDT, - amount: sdk.NewUint(3e6), // 3 USDT - gasAssetRates: gasAssetRateMap, - erc20AssetRates: erc20AssetRateMap, - expectedValue: sdk.NewInt(24e17), // 3 USDT * 0.8 = 2.4 ZETA - }, - { - name: "should return 0 if no gas asset rate found for chainID", - chainID: ethChainID, - coinType: coin.CoinType_Gas, - asset: "", - amount: sdk.NewUint(100), - gasAssetRates: nil, - erc20AssetRates: erc20AssetRateMap, - expectedValue: sdk.NewInt(0), - }, - { - name: "should return 0 if no erc20 asset rate found for chainID", - chainID: ethChainID, - coinType: coin.CoinType_ERC20, - asset: assetUSDT, - amount: sdk.NewUint(100), - gasAssetRates: gasAssetRateMap, - erc20AssetRates: nil, - expectedValue: sdk.NewInt(0), - }, - { - name: "should return 0 if coinType is CoinType_Cmd", - chainID: ethChainID, - coinType: coin.CoinType_Cmd, - asset: "", - amount: sdk.NewUint(100), - gasAssetRates: gasAssetRateMap, - erc20AssetRates: erc20AssetRateMap, - expectedValue: sdk.NewInt(0), - }, - { - name: "should return 0 on nil rate", - chainID: ethChainID, - coinType: coin.CoinType_Gas, - asset: "", - amount: sdk.NewUint(100), - gasAssetRates: func() map[int64]*types.AssetRate { - // set rate to nil - tempRates := k.GetRateLimiterAssetRateList(ctx) - nilAssetRateMap, _ := keeper.BuildAssetRateMapFromList(tempRates) - nilAssetRateMap[ethChainID].Rate = sdk.Dec{} - return nilAssetRateMap - }(), - erc20AssetRates: erc20AssetRateMap, - expectedValue: sdk.NewInt(0), - }, - { - name: "should return 0 on rate <= 0", - chainID: ethChainID, - coinType: coin.CoinType_ERC20, - asset: assetUSDT, - amount: sdk.NewUint(100), - gasAssetRates: gasAssetRateMap, - erc20AssetRates: func() map[int64]map[string]*types.AssetRate { - // set rate to 0 - tempRates := k.GetRateLimiterAssetRateList(ctx) - _, zeroAssetRateMap := keeper.BuildAssetRateMapFromList(tempRates) - zeroAssetRateMap[ethChainID][strings.ToLower(assetUSDT)].Rate = sdk.NewDec(0) - return zeroAssetRateMap - }(), - expectedValue: sdk.NewInt(0), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // create cctx with given input - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", tt.chainID, 1)) - cctx.InboundTxParams.CoinType = tt.coinType - cctx.InboundTxParams.Asset = tt.asset - cctx.GetCurrentOutTxParam().Amount = tt.amount - - // convert cctx value - value := keeper.ConvertCctxValue(tt.chainID, cctx, tt.gasAssetRates, tt.erc20AssetRates) - require.Equal(t, tt.expectedValue, value) - }) - } -} - func TestKeeper_RateLimiterInput(t *testing.T) { // create sample TSS tss := sample.Tss() @@ -512,17 +347,29 @@ func TestKeeper_RateLimiterInput_Errors(t *testing.T) { }) require.ErrorContains(t, err, "tss not found") }) - t.Run("pending nonces not found", func(t *testing.T) { + t.Run("asset rates not found", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) - // Set rate limiter flags as disabled - rFlags := sample.RateLimiterFlags() - k.SetRateLimiterFlags(ctx, rFlags) + // Set TSS but no rate limiter flags + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + + _, err := k.RateLimiterInput(ctx, &types.QueryRateLimiterInputRequest{ + Window: 100, + }) + require.ErrorContains(t, err, "asset rates not found") + }) + t.Run("pending nonces not found", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) // Set TSS tss := sample.Tss() zk.ObserverKeeper.SetTSS(ctx, tss) + // Set rate limiter flags as disabled + rFlags := sample.RateLimiterFlags() + k.SetRateLimiterFlags(ctx, rFlags) + _, err := k.RateLimiterInput(ctx, &types.QueryRateLimiterInputRequest{ Window: 100, }) diff --git a/x/crosschain/keeper/rate_limiter_flags.go b/x/crosschain/keeper/rate_limiter_flags.go index e8287e17f0..973a6a2554 100644 --- a/x/crosschain/keeper/rate_limiter_flags.go +++ b/x/crosschain/keeper/rate_limiter_flags.go @@ -5,7 +5,6 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -30,21 +29,21 @@ func (k Keeper) GetRateLimiterFlags(ctx sdk.Context) (val types.RateLimiterFlags } // GetRateLimiterAssetRateList returns a list of all foreign asset rate -func (k Keeper) GetRateLimiterAssetRateList(ctx sdk.Context) []*types.AssetRate { - rateLimiterFlags, _ := k.GetRateLimiterFlags(ctx) - - // the result list - assetRateList := make([]*types.AssetRate, 0) +func (k Keeper) GetRateLimiterAssetRateList(ctx sdk.Context) (flags types.RateLimiterFlags, assetRates []types.AssetRate, found bool) { + flags, found = k.GetRateLimiterFlags(ctx) + if !found { + return flags, nil, false + } // loop through the rate limiter flags to get the rate - for _, conversion := range rateLimiterFlags.Conversions { + for _, conversion := range flags.Conversions { fCoin, found := k.fungibleKeeper.GetForeignCoins(ctx, conversion.Zrc20) if !found { continue } // add asset rate to list - assetRateList = append(assetRateList, &types.AssetRate{ + assetRates = append(assetRates, types.AssetRate{ ChainId: fCoin.ForeignChainId, Asset: strings.ToLower(fCoin.Asset), Decimals: fCoin.Decimals, @@ -52,28 +51,5 @@ func (k Keeper) GetRateLimiterAssetRateList(ctx sdk.Context) []*types.AssetRate Rate: conversion.Rate, }) } - return assetRateList -} - -// BuildAssetRateMapFromList builds maps (foreign chain id -> asset -> rate) from a list of gas and erc20 asset rates -// The 1st map: foreign chain id -> gas coin asset rate -// The 2nd map: foreign chain id -> erc20 asset -> erc20 coin asset rate -func BuildAssetRateMapFromList(assetRates []*types.AssetRate) (map[int64]*types.AssetRate, map[int64]map[string]*types.AssetRate) { - // the result maps - gasAssetRateMap := make(map[int64]*types.AssetRate) - erc20AssetRateMap := make(map[int64]map[string]*types.AssetRate) - - // loop through the asset rates to build the maps - for _, assetRate := range assetRates { - chainID := assetRate.ChainId - if assetRate.CoinType == coin.CoinType_Gas { - gasAssetRateMap[chainID] = assetRate - } else { - if _, found := erc20AssetRateMap[chainID]; !found { - erc20AssetRateMap[chainID] = make(map[string]*types.AssetRate) - } - erc20AssetRateMap[chainID][strings.ToLower(assetRate.Asset)] = assetRate - } - } - return gasAssetRateMap, erc20AssetRateMap + return flags, assetRates, true } diff --git a/x/crosschain/keeper/rate_limiter_flags_test.go b/x/crosschain/keeper/rate_limiter_flags_test.go index 320085a1d1..6e0180bc88 100644 --- a/x/crosschain/keeper/rate_limiter_flags_test.go +++ b/x/crosschain/keeper/rate_limiter_flags_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "strings" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -10,7 +9,6 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" - "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" ) @@ -24,7 +22,7 @@ func createForeignCoinAndAssetRate( decimals uint32, coinType coin.CoinType, rate sdk.Dec, -) (fungibletypes.ForeignCoins, *types.AssetRate) { +) (fungibletypes.ForeignCoins, types.AssetRate) { // create foreign coin foreignCoin := sample.ForeignCoins(t, zrc20Addr) foreignCoin.Asset = asset @@ -33,13 +31,13 @@ func createForeignCoinAndAssetRate( foreignCoin.CoinType = coinType // create corresponding asset rate - assetRate := &types.AssetRate{ - ChainId: foreignCoin.ForeignChainId, - Asset: strings.ToLower(foreignCoin.Asset), - Decimals: foreignCoin.Decimals, - CoinType: foreignCoin.CoinType, - Rate: rate, - } + assetRate := sample.CustomAssetRate( + foreignCoin.ForeignChainId, + foreignCoin.Asset, + foreignCoin.Decimals, + foreignCoin.CoinType, + rate, + ) return foreignCoin, assetRate } @@ -59,7 +57,7 @@ func TestKeeper_GetRateLimiterFlags(t *testing.T) { require.Equal(t, flags, r) } -func TestKeeper_GetRateLimiterRateList(t *testing.T) { +func TestKeeper_GetRateLimiterAssetRateList(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) // create test flags @@ -67,7 +65,7 @@ func TestKeeper_GetRateLimiterRateList(t *testing.T) { zrc20GasAddr := sample.EthAddress().Hex() zrc20ERC20Addr1 := sample.EthAddress().Hex() zrc20ERC20Addr2 := sample.EthAddress().Hex() - flags := types.RateLimiterFlags{ + testflags := types.RateLimiterFlags{ Rate: sdk.NewUint(100), Conversions: []types.Conversion{ { @@ -85,8 +83,14 @@ func TestKeeper_GetRateLimiterRateList(t *testing.T) { }, } + // asset rates not found before setting flags + flags, assetRates, found := k.GetRateLimiterAssetRateList(ctx) + require.False(t, found) + require.Equal(t, types.RateLimiterFlags{}, flags) + require.Nil(t, assetRates) + // set flags - k.SetRateLimiterFlags(ctx, flags) + k.SetRateLimiterFlags(ctx, testflags) // add gas coin gasCoin, gasAssetRate := createForeignCoinAndAssetRate(t, zrc20GasAddr, "", chainID, 18, coin.CoinType_Gas, sdk.NewDec(1)) @@ -101,48 +105,8 @@ func TestKeeper_GetRateLimiterRateList(t *testing.T) { zk.FungibleKeeper.SetForeignCoins(ctx, erc20Coin2) // get rates - assetRates := k.GetRateLimiterAssetRateList(ctx) - require.EqualValues(t, []*types.AssetRate{gasAssetRate, erc20AssetRate1, erc20AssetRate2}, assetRates) -} - -func TestBuildAssetRateMapFromList(t *testing.T) { - // define asset rate list - assetRates := []*types.AssetRate{ - { - ChainId: 1, - Asset: "eth", - Decimals: 18, - CoinType: coin.CoinType_Gas, - Rate: sdk.NewDec(1), - }, - { - ChainId: 1, - Asset: "usdt", - Decimals: 6, - CoinType: coin.CoinType_ERC20, - Rate: sdk.NewDec(2), - }, - { - ChainId: 2, - Asset: "btc", - Decimals: 8, - CoinType: coin.CoinType_Gas, - Rate: sdk.NewDec(3), - }, - } - - // build asset rate map - gasAssetRateMap, erc20AssetRateMap := keeper.BuildAssetRateMapFromList(assetRates) - - // check length - require.Equal(t, 2, len(gasAssetRateMap)) - require.Equal(t, 1, len(erc20AssetRateMap)) - require.Equal(t, 1, len(erc20AssetRateMap[1])) - - // check gas asset rate map - require.EqualValues(t, assetRates[0], gasAssetRateMap[1]) - require.EqualValues(t, assetRates[2], gasAssetRateMap[2]) - - // check erc20 asset rate map - require.EqualValues(t, assetRates[1], erc20AssetRateMap[1]["usdt"]) + flags, assetRates, found = k.GetRateLimiterAssetRateList(ctx) + require.True(t, found) + require.Equal(t, testflags, flags) + require.EqualValues(t, []types.AssetRate{gasAssetRate, erc20AssetRate1, erc20AssetRate2}, assetRates) } diff --git a/x/crosschain/types/rate_limiter_flags.go b/x/crosschain/types/rate_limiter_flags.go index 93a9617961..59c98111f2 100644 --- a/x/crosschain/types/rate_limiter_flags.go +++ b/x/crosschain/types/rate_limiter_flags.go @@ -2,9 +2,12 @@ package types import ( "fmt" + "strings" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" + coin "github.com/zeta-chain/zetacore/pkg/coin" ) // Validate checks that the RateLimiterFlags is valid @@ -45,3 +48,95 @@ func (r RateLimiterFlags) GetConversionRate(zrc20 string) (sdk.Dec, bool) { } return sdk.NewDec(0), false } + +// BuildAssetRateMapFromList builds maps (foreign chain id -> asset -> rate) from a list of gas and erc20 asset rates +// The 1st map: foreign chain id -> gas coin asset rate +// The 2nd map: foreign chain id -> erc20 asset -> erc20 coin asset rate +func BuildAssetRateMapFromList(assetRates []AssetRate) (map[int64]AssetRate, map[int64]map[string]AssetRate) { + // the result maps + gasAssetRateMap := make(map[int64]AssetRate) + erc20AssetRateMap := make(map[int64]map[string]AssetRate) + + // loop through the asset rates to build the maps + for _, assetRate := range assetRates { + chainID := assetRate.ChainId + if assetRate.CoinType == coin.CoinType_Gas { + gasAssetRateMap[chainID] = assetRate + } else { + if _, found := erc20AssetRateMap[chainID]; !found { + erc20AssetRateMap[chainID] = make(map[string]AssetRate) + } + erc20AssetRateMap[chainID][strings.ToLower(assetRate.Asset)] = assetRate + } + } + return gasAssetRateMap, erc20AssetRateMap +} + +// ConvertCctxValue converts the value of the cctx to azeta using given conversion rates +func ConvertCctxValue( + chainID int64, + cctx *CrossChainTx, + gasAssetRateMap map[int64]AssetRate, + erc20AssetRateMap map[int64]map[string]AssetRate, +) sdkmath.Int { + var rate sdk.Dec + var decimals uint32 + switch cctx.InboundTxParams.CoinType { + case coin.CoinType_Zeta: + // no conversion needed for ZETA + return sdk.NewIntFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) + case coin.CoinType_Gas: + assetRate, found := gasAssetRateMap[chainID] + if !found { + // skip if no rate found for gas coin on this chainID + return sdk.NewInt(0) + } + rate = assetRate.Rate + decimals = assetRate.Decimals + case coin.CoinType_ERC20: + // get the ERC20 coin rate + _, found := erc20AssetRateMap[chainID] + if !found { + // skip if no rate found for this chainID + return sdk.NewInt(0) + } + assetRate := erc20AssetRateMap[chainID][strings.ToLower(cctx.InboundTxParams.Asset)] + rate = assetRate.Rate + decimals = assetRate.Decimals + default: + // skip CoinType_Cmd + return sdk.NewInt(0) + } + // should not happen, return 0 to skip if it happens + if rate.IsNil() || rate.LTE(sdk.NewDec(0)) { + return sdkmath.NewInt(0) + } + + // the whole coin amounts of zeta and zrc20 + // given decimals = 6, the amount will be 10^6 = 1000000 + oneZeta := coin.AzetaPerZeta() + oneZrc20 := sdk.NewDec(10).Power(uint64(decimals)) + + // convert cctx asset amount into azeta amount + // given amountCctx = 2000000, rate = 0.8, decimals = 6 + // amountCctxDec: 2000000 * 0.8 = 1600000.0 + // amountAzetaDec: 1600000.0 * 10e18 / 10e6 = 1600000000000000000.0 + amountCctxDec := sdk.NewDecFromBigInt(cctx.GetCurrentOutTxParam().Amount.BigInt()) + amountAzetaDec := amountCctxDec.Mul(rate).Mul(oneZeta).Quo(oneZrc20) + return amountAzetaDec.TruncateInt() +} + +// RateLimitExceeded accumulates the cctx value and then checks if the rate limit is exceeded +// returns true if the rate limit is exceeded +func RateLimitExceeded( + chainID int64, + cctx *CrossChainTx, + gasAssetRateMap map[int64]AssetRate, + erc20AssetRateMap map[int64]map[string]AssetRate, + currentCctxValue *sdkmath.Int, + withdrawLimitInAzeta sdkmath.Int, +) bool { + amountZeta := ConvertCctxValue(chainID, cctx, gasAssetRateMap, erc20AssetRateMap) + *currentCctxValue = currentCctxValue.Add(amountZeta) + return currentCctxValue.GT(withdrawLimitInAzeta) +} diff --git a/x/crosschain/types/rate_limiter_flags_test.go b/x/crosschain/types/rate_limiter_flags_test.go index d837fabc51..f8376d546a 100644 --- a/x/crosschain/types/rate_limiter_flags_test.go +++ b/x/crosschain/types/rate_limiter_flags_test.go @@ -1,10 +1,16 @@ package types_test import ( + "fmt" + "strings" "testing" + "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/testutil/sample" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -184,3 +190,197 @@ func TestRateLimiterFlags_GetConversionRate(t *testing.T) { }) } } + +func TestBuildAssetRateMapFromList(t *testing.T) { + // define asset rate list + assetRates := []types.AssetRate{ + { + ChainId: 1, + Asset: "eth", + Decimals: 18, + CoinType: coin.CoinType_Gas, + Rate: sdk.NewDec(1), + }, + { + ChainId: 1, + Asset: "usdt", + Decimals: 6, + CoinType: coin.CoinType_ERC20, + Rate: sdk.NewDec(2), + }, + { + ChainId: 2, + Asset: "btc", + Decimals: 8, + CoinType: coin.CoinType_Gas, + Rate: sdk.NewDec(3), + }, + } + + // build asset rate map + gasAssetRateMap, erc20AssetRateMap := types.BuildAssetRateMapFromList(assetRates) + + // check length + require.Equal(t, 2, len(gasAssetRateMap)) + require.Equal(t, 1, len(erc20AssetRateMap)) + require.Equal(t, 1, len(erc20AssetRateMap[1])) + + // check gas asset rate map + require.EqualValues(t, assetRates[0], gasAssetRateMap[1]) + require.EqualValues(t, assetRates[2], gasAssetRateMap[2]) + + // check erc20 asset rate map + require.EqualValues(t, assetRates[1], erc20AssetRateMap[1]["usdt"]) +} + +func TestConvertCctxValue(t *testing.T) { + // chain IDs + ethChainID := chains.GoerliLocalnetChain.ChainId + btcChainID := chains.BtcRegtestChain.ChainId + + // setup test asset rates + assetETH := sample.EthAddress().Hex() + assetBTC := sample.EthAddress().Hex() + assetUSDT := sample.EthAddress().Hex() + assetRateList := []types.AssetRate{ + sample.CustomAssetRate(ethChainID, assetETH, 18, coin.CoinType_Gas, sdk.NewDec(2500)), + sample.CustomAssetRate(btcChainID, assetBTC, 8, coin.CoinType_Gas, sdk.NewDec(50000)), + sample.CustomAssetRate(ethChainID, assetUSDT, 6, coin.CoinType_ERC20, sdk.MustNewDecFromStr("0.8")), + } + gasAssetRateMap, erc20AssetRateMap := types.BuildAssetRateMapFromList(assetRateList) + + // test cases + tests := []struct { + name string + + // input + chainID int64 + coinType coin.CoinType + asset string + amount math.Uint + gasAssetRates map[int64]types.AssetRate + erc20AssetRates map[int64]map[string]types.AssetRate + + // output + expectedValue sdkmath.Int + }{ + { + name: "should convert cctx ZETA value correctly", + chainID: ethChainID, + coinType: coin.CoinType_Zeta, + asset: "", + amount: sdk.NewUint(3e17), // 0.3 ZETA + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(3e17), + }, + { + name: "should convert cctx ETH value correctly", + chainID: ethChainID, + coinType: coin.CoinType_Gas, + asset: "", + amount: sdk.NewUint(3e15), // 0.003 ETH + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(75e17), // 0.003 ETH * 2500 = 7.5 ZETA + }, + { + name: "should convert cctx BTC value correctly", + chainID: btcChainID, + coinType: coin.CoinType_Gas, + asset: "", + amount: sdk.NewUint(70000), // 0.0007 BTC + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(35).Mul(sdk.NewInt(1e18)), // 0.0007 BTC * 50000 = 35.0 ZETA + }, + { + name: "should convert cctx USDT value correctly", + chainID: ethChainID, + coinType: coin.CoinType_ERC20, + asset: assetUSDT, + amount: sdk.NewUint(3e6), // 3 USDT + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(24e17), // 3 USDT * 0.8 = 2.4 ZETA + }, + { + name: "should return 0 if no gas asset rate found for chainID", + chainID: ethChainID, + coinType: coin.CoinType_Gas, + asset: "", + amount: sdk.NewUint(100), + gasAssetRates: nil, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(0), + }, + { + name: "should return 0 if no erc20 asset rate found for chainID", + chainID: ethChainID, + coinType: coin.CoinType_ERC20, + asset: assetUSDT, + amount: sdk.NewUint(100), + gasAssetRates: gasAssetRateMap, + erc20AssetRates: nil, + expectedValue: sdk.NewInt(0), + }, + { + name: "should return 0 if coinType is CoinType_Cmd", + chainID: ethChainID, + coinType: coin.CoinType_Cmd, + asset: "", + amount: sdk.NewUint(100), + gasAssetRates: gasAssetRateMap, + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(0), + }, + { + name: "should return 0 on nil rate", + chainID: ethChainID, + coinType: coin.CoinType_Gas, + asset: "", + amount: sdk.NewUint(100), + gasAssetRates: func() map[int64]types.AssetRate { + // set rate to nil + nilAssetRateMap, _ := types.BuildAssetRateMapFromList(assetRateList) + nilRate := nilAssetRateMap[ethChainID] + nilRate.Rate = sdk.Dec{} + nilAssetRateMap[ethChainID] = nilRate + return nilAssetRateMap + }(), + erc20AssetRates: erc20AssetRateMap, + expectedValue: sdk.NewInt(0), + }, + { + name: "should return 0 on rate <= 0", + chainID: ethChainID, + coinType: coin.CoinType_ERC20, + asset: assetUSDT, + amount: sdk.NewUint(100), + gasAssetRates: gasAssetRateMap, + erc20AssetRates: func() map[int64]map[string]types.AssetRate { + // set rate to 0 + _, zeroAssetRateMap := types.BuildAssetRateMapFromList(assetRateList) + zeroRate := zeroAssetRateMap[ethChainID][strings.ToLower(assetUSDT)] + zeroRate.Rate = sdk.NewDec(0) + zeroAssetRateMap[ethChainID][strings.ToLower(assetUSDT)] = zeroRate + return zeroAssetRateMap + }(), + expectedValue: sdk.NewInt(0), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create cctx with given input + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", tt.chainID, 1)) + cctx.InboundTxParams.CoinType = tt.coinType + cctx.InboundTxParams.Asset = tt.asset + cctx.GetCurrentOutTxParam().Amount = tt.amount + + // convert cctx value + value := types.ConvertCctxValue(tt.chainID, cctx, tt.gasAssetRates, tt.erc20AssetRates) + require.Equal(t, tt.expectedValue, value) + }) + } +} diff --git a/zetaclient/core_context/zeta_core_context.go b/zetaclient/core_context/zeta_core_context.go index 256684c895..8390775959 100644 --- a/zetaclient/core_context/zeta_core_context.go +++ b/zetaclient/core_context/zeta_core_context.go @@ -75,17 +75,21 @@ func (c *ZetaCoreContext) GetCurrentTssPubkey() string { return c.currentTssPubkey } +// GetEnabledChains returns all enabled chains including zetachain func (c *ZetaCoreContext) GetEnabledChains() []chains.Chain { c.coreContextLock.RLock() defer c.coreContextLock.RUnlock() + copiedChains := make([]chains.Chain, len(c.chainsEnabled)) copy(copiedChains, c.chainsEnabled) return copiedChains } -func (c *ZetaCoreContext) GetEnabledForeignChains() []chains.Chain { +// GetEnabledExternalChains returns all enabled external chains +func (c *ZetaCoreContext) GetEnabledExternalChains() []chains.Chain { c.coreContextLock.RLock() defer c.coreContextLock.RUnlock() + foreignChains := make([]chains.Chain, 0) for _, chain := range c.chainsEnabled { if !chain.IsZetaChain() { @@ -98,6 +102,7 @@ func (c *ZetaCoreContext) GetEnabledForeignChains() []chains.Chain { func (c *ZetaCoreContext) GetEVMChainParams(chainID int64) (*observertypes.ChainParams, bool) { c.coreContextLock.RLock() defer c.coreContextLock.RUnlock() + evmChainParams, found := c.evmChainParams[chainID] return evmChainParams, found } diff --git a/zetaclient/core_context/zeta_core_context_test.go b/zetaclient/core_context/zeta_core_context_test.go index 771ff6f920..625fb40269 100644 --- a/zetaclient/core_context/zeta_core_context_test.go +++ b/zetaclient/core_context/zeta_core_context_test.go @@ -69,8 +69,8 @@ func TestNewZetaCoreContext(t *testing.T) { // assert enabled chains require.Empty(t, len(zetaContext.GetEnabledChains())) - // assert foreign chains - require.Empty(t, len(zetaContext.GetEnabledForeignChains())) + // assert external chains + require.Empty(t, len(zetaContext.GetEnabledExternalChains())) // assert current tss pubkey require.Equal(t, "", zetaContext.GetCurrentTssPubkey()) @@ -196,8 +196,8 @@ func TestUpdateZetaCoreContext(t *testing.T) { // assert enabled chains updated require.Equal(t, enabledChainsToUpdate, zetaContext.GetEnabledChains()) - // assert enabled foreign chains - require.Equal(t, enabledChainsToUpdate[0:2], zetaContext.GetEnabledForeignChains()) + // assert enabled external chains + require.Equal(t, enabledChainsToUpdate[0:2], zetaContext.GetEnabledExternalChains()) // assert current tss pubkey updated require.Equal(t, tssPubKeyToUpdate, zetaContext.GetCurrentTssPubkey()) diff --git a/zetaclient/ratelimiter/rate_limiter_test.go b/zetaclient/ratelimiter/rate_limiter_test.go index 2012d19d60..0f6c3a0e73 100644 --- a/zetaclient/ratelimiter/rate_limiter_test.go +++ b/zetaclient/ratelimiter/rate_limiter_test.go @@ -14,7 +14,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/ratelimiter" ) -func Test_NewFilterInput(t *testing.T) { +func Test_NewInput(t *testing.T) { // sample response response := crosschaintypes.QueryRateLimiterInputResponse{ Height: 10, @@ -26,7 +26,7 @@ func Test_NewFilterInput(t *testing.T) { LowestPendingCctxHeight: 2, } - t.Run("should create a filter input from gRPC response", func(t *testing.T) { + t.Run("should create a input from gRPC response", func(t *testing.T) { filterInput, ok := ratelimiter.NewInput(response) require.True(t, ok) require.Equal(t, response.Height, filterInput.Height) diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index da45c4fb31..de2f9707f4 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -22,16 +22,20 @@ import ( ) const ( - // EVMOutboundTxLookbackFactor is the factor to determine how many nonces to look back for pending cctxs + // evmOutboundTxLookbackFactor is the factor to determine how many nonces to look back for pending cctxs // For example, give OutboundTxScheduleLookahead of 120, pending NonceLow of 1000 and factor of 1.0, // the scheduler need to be able to pick up and schedule any pending cctx with nonce < 880 (1000 - 120 * 1.0) // NOTE: 1.0 means look back the same number of cctxs as we look ahead - EVMOutboundTxLookbackFactor = 1.0 + evmOutboundTxLookbackFactor = 1.0 + + // sampling rate for sampled observer logger + loggerSamplingRate = 10 ) type ZetaCoreLog struct { - ChainLogger zerolog.Logger - ZetaChainWatcher zerolog.Logger + Observer zerolog.Logger + ObserverSampled zerolog.Logger + OutTxProcessor zerolog.Logger } // CoreObserver wraps the zetabridge, chain clients and signers. This is the high level object used for CCTX scheduling @@ -57,22 +61,23 @@ func NewCoreObserver( ts: ts, stop: make(chan struct{}), } - chainLogger := logger.With(). - Str("chain", "ZetaChain"). - Logger() + + // create loggers + chainLogger := logger.With().Str("chain", "ZetaChain").Logger() co.logger = ZetaCoreLog{ - ChainLogger: chainLogger, - ZetaChainWatcher: chainLogger.With().Str("module", "ZetaChainWatcher").Logger(), + Observer: chainLogger.With().Str("module", "Observer").Logger(), + OutTxProcessor: chainLogger.With().Str("module", "OutTxProcessor").Logger(), } + co.logger.ObserverSampled = co.logger.Observer.Sample(&zerolog.BasicSampler{N: loggerSamplingRate}) + // set bridge, signers and clients co.bridge = bridge co.signerMap = signerMap - co.clientMap = clientMap - co.logger.ChainLogger.Info().Msg("starting core observer") + co.logger.Observer.Info().Msg("starting core observer") balance, err := bridge.GetZetaHotKeyBalance() if err != nil { - co.logger.ChainLogger.Error().Err(err).Msg("error getting last balance of the hot key") + co.logger.Observer.Error().Err(err).Msg("error getting last balance of the hot key") } co.lastOperatorBalance = balance @@ -81,7 +86,7 @@ func NewCoreObserver( func (co *CoreObserver) MonitorCore(appContext *appcontext.AppContext) { myid := co.bridge.GetKeys().GetAddress() - co.logger.ZetaChainWatcher.Info().Msgf("Starting Send Scheduler for %s", myid) + co.logger.Observer.Info().Msgf("Starting cctx scheduler for %s", myid) go co.StartCctxScheduler(appContext) go func() { @@ -110,12 +115,12 @@ func (co *CoreObserver) GetUpdatedSigner(coreContext *corecontext.ZetaCoreContex erc20CustodyAddress := ethcommon.HexToAddress(evmParams.GetErc20CustodyContractAddress()) if zetaConnectorAddress != signer.GetZetaConnectorAddress() { signer.SetZetaConnectorAddress(zetaConnectorAddress) - co.logger.ZetaChainWatcher.Info().Msgf( + co.logger.Observer.Info().Msgf( "updated zeta connector address for chainID %d, new address: %s", chainID, zetaConnectorAddress) } if erc20CustodyAddress != signer.GetERC20CustodyAddress() { signer.SetERC20CustodyAddress(erc20CustodyAddress) - co.logger.ZetaChainWatcher.Info().Msgf( + co.logger.Observer.Info().Msgf( "updated ERC20 custody address for chainID %d, new address: %s", chainID, erc20CustodyAddress) } } @@ -135,7 +140,7 @@ func (co *CoreObserver) GetUpdatedChainClient(coreContext *corecontext.ZetaCoreC evmParams, found := coreContext.GetEVMChainParams(chainID) if found && !observertypes.ChainParamsEqual(curParams, *evmParams) { chainOb.SetChainParams(*evmParams) - co.logger.ZetaChainWatcher.Info().Msgf( + co.logger.Observer.Info().Msgf( "updated chain params for chainID %d, new params: %v", chainID, *evmParams) } } else if chains.IsBitcoinChain(chainID) { @@ -143,7 +148,7 @@ func (co *CoreObserver) GetUpdatedChainClient(coreContext *corecontext.ZetaCoreC if found && !observertypes.ChainParamsEqual(curParams, *btcParams) { chainOb.SetChainParams(*btcParams) - co.logger.ZetaChainWatcher.Info().Msgf( + co.logger.Observer.Info().Msgf( "updated chain params for Bitcoin, new params: %v", *btcParams) } } @@ -151,7 +156,7 @@ func (co *CoreObserver) GetUpdatedChainClient(coreContext *corecontext.ZetaCoreC } // GetPendingCctxsWithinRatelimit get pending cctxs across foreign chains within rate limit -func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Chain, logger zerolog.Logger) (map[int64][]*types.CrossChainTx, error) { +func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Chain) (map[int64][]*types.CrossChainTx, error) { // get rate limiter flags rateLimitFlags, err := co.bridge.GetRateLimiterFlags() if err != nil { @@ -191,7 +196,7 @@ func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Ch if percentage != nil { percentageFloat, _ := percentage.Float64() metrics.PercentageOfRateReached.Set(percentageFloat) - logger.Info().Msgf("current rate limiter window: %d rate: %s, percentage: %f", + co.logger.ObserverSampled.Info().Msgf("current rate limiter window: %d rate: %s, percentage: %f", output.CurrentWithdrawWindow, output.CurrentWithdrawRate.String(), percentageFloat) } @@ -200,24 +205,23 @@ func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Ch // StartCctxScheduler schedules keysigns for cctxs on each ZetaChain block (the ticker) func (co *CoreObserver) StartCctxScheduler(appContext *appcontext.AppContext) { - outTxMan := outtxprocessor.NewOutTxProcessorManager(co.logger.ChainLogger) + outTxMan := outtxprocessor.NewOutTxProcessorManager(co.logger.OutTxProcessor) observeTicker := time.NewTicker(3 * time.Second) - sampledLogger := co.logger.ZetaChainWatcher.Sample(&zerolog.BasicSampler{N: 10}) var lastBlockNum int64 for { select { case <-co.stop: - co.logger.ZetaChainWatcher.Warn().Msg("StartCctxScheduler: stopped") + co.logger.Observer.Warn().Msg("StartCctxScheduler: stopped") return case <-observeTicker.C: { bn, err := co.bridge.GetZetaBlockHeight() if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msg("StartCctxScheduler: GetZetaBlockHeight fail") + co.logger.Observer.Error().Err(err).Msg("StartCctxScheduler: GetZetaBlockHeight fail") continue } if bn < 0 { - co.logger.ZetaChainWatcher.Error().Msg("StartCctxScheduler: GetZetaBlockHeight returned negative height") + co.logger.Observer.Error().Msg("StartCctxScheduler: GetZetaBlockHeight returned negative height") continue } if lastBlockNum == 0 { @@ -226,12 +230,12 @@ func (co *CoreObserver) StartCctxScheduler(appContext *appcontext.AppContext) { if bn > lastBlockNum { // we have a new block bn = lastBlockNum + 1 if bn%10 == 0 { - co.logger.ZetaChainWatcher.Debug().Msgf("StartCctxScheduler: ZetaCore heart beat: %d", bn) + co.logger.Observer.Debug().Msgf("StartCctxScheduler: ZetaCore heart beat: %d", bn) } balance, err := co.bridge.GetZetaHotKeyBalance() if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("couldn't get operator balance") + co.logger.Observer.Error().Err(err).Msgf("couldn't get operator balance") } else { diff := co.lastOperatorBalance.Sub(balance) if diff.GT(sdkmath.NewInt(0)) && diff.LT(sdkmath.NewInt(math.MaxInt64)) { @@ -243,18 +247,18 @@ func (co *CoreObserver) StartCctxScheduler(appContext *appcontext.AppContext) { // set current hot key burn rate metrics.HotKeyBurnRate.Set(float64(co.ts.HotKeyBurnRate.GetBurnRate().Int64())) - // get supported foreign chains + // get supported external chains coreContext := appContext.ZetaCoreContext() - foreignChains := coreContext.GetEnabledForeignChains() + externalChains := coreContext.GetEnabledExternalChains() - // query pending cctxs across all foreign chains within rate limit - cctxMap, err := co.GetPendingCctxsWithinRatelimit(foreignChains, sampledLogger) + // query pending cctxs across all external chains within rate limit + cctxMap, err := co.GetPendingCctxsWithinRatelimit(externalChains) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("StartCctxScheduler: GetPendingCctxsWithinRatelimit failed") + co.logger.Observer.Error().Err(err).Msgf("StartCctxScheduler: GetPendingCctxsWithinRatelimit failed") } // schedule keysign for pending cctxs on each chain - for _, c := range foreignChains { + for _, c := range externalChains { // get cctxs from map and set pending transactions prometheus gauge cctxList := cctxMap[c.ChainId] metrics.PendingTxsPerChain.WithLabelValues(c.ChainName.String()).Set(float64(len(cctxList))) @@ -265,12 +269,12 @@ func (co *CoreObserver) StartCctxScheduler(appContext *appcontext.AppContext) { // update chain parameters for signer and chain client signer, err := co.GetUpdatedSigner(coreContext, c.ChainId) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("StartCctxScheduler: GetUpdatedSigner failed for chain %d", c.ChainId) + co.logger.Observer.Error().Err(err).Msgf("StartCctxScheduler: GetUpdatedSigner failed for chain %d", c.ChainId) continue } ob, err := co.GetUpdatedChainClient(coreContext, c.ChainId) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("StartCctxScheduler: GetUpdatedChainClient failed for chain %d", c.ChainId) + co.logger.Observer.Error().Err(err).Msgf("StartCctxScheduler: GetUpdatedChainClient failed for chain %d", c.ChainId) continue } if !corecontext.IsOutboundObservationEnabled(coreContext, ob.GetChainParams()) { @@ -284,7 +288,7 @@ func (co *CoreObserver) StartCctxScheduler(appContext *appcontext.AppContext) { } else if chains.IsBitcoinChain(c.ChainId) { co.ScheduleCctxBTC(outTxMan, zetaHeight, c.ChainId, cctxList, ob, signer) } else { - co.logger.ZetaChainWatcher.Error().Msgf("StartCctxScheduler: unsupported chain %d", c.ChainId) + co.logger.Observer.Error().Msgf("StartCctxScheduler: unsupported chain %d", c.ChainId) continue } } @@ -308,7 +312,7 @@ func (co *CoreObserver) ScheduleCctxEVM( signer interfaces.ChainSigner) { res, err := co.bridge.GetAllOutTxTrackerByChain(chainID, interfaces.Ascending) if err != nil { - co.logger.ZetaChainWatcher.Warn().Err(err).Msgf("ScheduleCctxEVM: GetAllOutTxTrackerByChain failed for chain %d", chainID) + co.logger.Observer.Warn().Err(err).Msgf("ScheduleCctxEVM: GetAllOutTxTrackerByChain failed for chain %d", chainID) return } trackerMap := make(map[uint64]bool) @@ -317,7 +321,7 @@ func (co *CoreObserver) ScheduleCctxEVM( } outboundScheduleLookahead := ob.GetChainParams().OutboundTxScheduleLookahead // #nosec G701 always in range - outboundScheduleLookback := uint64(float64(outboundScheduleLookahead) * EVMOutboundTxLookbackFactor) + outboundScheduleLookback := uint64(float64(outboundScheduleLookahead) * evmOutboundTxLookbackFactor) // #nosec G701 positive outboundScheduleInterval := uint64(ob.GetChainParams().OutboundTxScheduleInterval) @@ -327,23 +331,23 @@ func (co *CoreObserver) ScheduleCctxEVM( outTxID := outtxprocessor.ToOutTxID(cctx.Index, params.ReceiverChainId, nonce) if params.ReceiverChainId != chainID { - co.logger.ZetaChainWatcher.Error().Msgf("ScheduleCctxEVM: outtx %s chainid mismatch: want %d, got %d", outTxID, chainID, params.ReceiverChainId) + co.logger.Observer.Error().Msgf("ScheduleCctxEVM: outtx %s chainid mismatch: want %d, got %d", outTxID, chainID, params.ReceiverChainId) continue } if params.OutboundTxTssNonce > cctxList[0].GetCurrentOutTxParam().OutboundTxTssNonce+outboundScheduleLookback { - co.logger.ZetaChainWatcher.Error().Msgf("ScheduleCctxEVM: nonce too high: signing %d, earliest pending %d, chain %d", + co.logger.Observer.Error().Msgf("ScheduleCctxEVM: nonce too high: signing %d, earliest pending %d, chain %d", params.OutboundTxTssNonce, cctxList[0].GetCurrentOutTxParam().OutboundTxTssNonce, chainID) break } // try confirming the outtx - included, _, err := ob.IsOutboundProcessed(cctx, co.logger.ZetaChainWatcher) + included, _, err := ob.IsOutboundProcessed(cctx, co.logger.Observer) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("ScheduleCctxEVM: IsOutboundProcessed faild for chain %d nonce %d", chainID, nonce) + co.logger.Observer.Error().Err(err).Msgf("ScheduleCctxEVM: IsOutboundProcessed faild for chain %d nonce %d", chainID, nonce) continue } if included { - co.logger.ZetaChainWatcher.Info().Msgf("ScheduleCctxEVM: outtx %s already included; do not schedule keysign", outTxID) + co.logger.Observer.Info().Msgf("ScheduleCctxEVM: outtx %s already included; do not schedule keysign", outTxID) continue } @@ -371,7 +375,7 @@ func (co *CoreObserver) ScheduleCctxEVM( // otherwise, the normal interval is used if nonce%outboundScheduleInterval == zetaHeight%outboundScheduleInterval && !outTxMan.IsOutTxActive(outTxID) { outTxMan.StartTryProcess(outTxID) - co.logger.ZetaChainWatcher.Debug().Msgf("ScheduleCctxEVM: sign outtx %s with value %d\n", outTxID, cctx.GetCurrentOutTxParam().Amount) + co.logger.Observer.Debug().Msgf("ScheduleCctxEVM: sign outtx %s with value %d\n", outTxID, cctx.GetCurrentOutTxParam().Amount) go signer.TryProcessOutTx(cctx, outTxMan, outTxID, ob, co.bridge, zetaHeight) } @@ -395,7 +399,7 @@ func (co *CoreObserver) ScheduleCctxBTC( signer interfaces.ChainSigner) { btcClient, ok := ob.(*bitcoin.BTCChainClient) if !ok { // should never happen - co.logger.ZetaChainWatcher.Error().Msgf("scheduleCctxBTC: chain client is not a bitcoin client") + co.logger.Observer.Error().Msgf("ScheduleCctxBTC: chain client is not a bitcoin client") return } // #nosec G701 positive @@ -409,17 +413,17 @@ func (co *CoreObserver) ScheduleCctxBTC( outTxID := outtxprocessor.ToOutTxID(cctx.Index, params.ReceiverChainId, nonce) if params.ReceiverChainId != chainID { - co.logger.ZetaChainWatcher.Error().Msgf("scheduleCctxBTC: outtx %s chainid mismatch: want %d, got %d", outTxID, chainID, params.ReceiverChainId) + co.logger.Observer.Error().Msgf("ScheduleCctxBTC: outtx %s chainid mismatch: want %d, got %d", outTxID, chainID, params.ReceiverChainId) continue } // try confirming the outtx - included, confirmed, err := btcClient.IsOutboundProcessed(cctx, co.logger.ZetaChainWatcher) + included, confirmed, err := btcClient.IsOutboundProcessed(cctx, co.logger.Observer) if err != nil { - co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxBTC: IsOutboundProcessed faild for chain %d nonce %d", chainID, nonce) + co.logger.Observer.Error().Err(err).Msgf("ScheduleCctxBTC: IsOutboundProcessed faild for chain %d nonce %d", chainID, nonce) continue } if included || confirmed { - co.logger.ZetaChainWatcher.Info().Msgf("scheduleCctxBTC: outtx %s already included; do not schedule keysign", outTxID) + co.logger.Observer.Info().Msgf("ScheduleCctxBTC: outtx %s already included; do not schedule keysign", outTxID) continue } @@ -429,13 +433,13 @@ func (co *CoreObserver) ScheduleCctxBTC( } // stop if lookahead is reached if int64(idx) >= lookahead { // 2 bitcoin confirmations span is 20 minutes on average. We look ahead up to 100 pending cctx to target TPM of 5. - co.logger.ZetaChainWatcher.Warn().Msgf("scheduleCctxBTC: lookahead reached, signing %d, earliest pending %d", nonce, cctxList[0].GetCurrentOutTxParam().OutboundTxTssNonce) + co.logger.Observer.Warn().Msgf("ScheduleCctxBTC: lookahead reached, signing %d, earliest pending %d", nonce, cctxList[0].GetCurrentOutTxParam().OutboundTxTssNonce) break } // try confirming the outtx or scheduling a keysign if nonce%interval == zetaHeight%interval && !outTxMan.IsOutTxActive(outTxID) { outTxMan.StartTryProcess(outTxID) - co.logger.ZetaChainWatcher.Debug().Msgf("scheduleCctxBTC: sign outtx %s with value %d\n", outTxID, params.Amount) + co.logger.Observer.Debug().Msgf("ScheduleCctxBTC: sign outtx %s with value %d\n", outTxID, params.Amount) go signer.TryProcessOutTx(cctx, outTxMan, outTxID, ob, co.bridge, zetaHeight) } } diff --git a/zetaclient/zetacore_observer_test.go b/zetaclient/zetacore_observer_test.go index e5a439c2e2..33ec1c01e7 100644 --- a/zetaclient/zetacore_observer_test.go +++ b/zetaclient/zetacore_observer_test.go @@ -320,7 +320,7 @@ func Test_GetPendingCctxsWithinRatelimit(t *testing.T) { observer := MockCoreObserver(t, bridge, ethChain, btcChain, ethChainParams, btcChainParams) // run the test - cctxsMap, err := observer.GetPendingCctxsWithinRatelimit(foreignChains, zerolog.Logger{}) + cctxsMap, err := observer.GetPendingCctxsWithinRatelimit(foreignChains) if tt.fail { require.Error(t, err) require.Nil(t, cctxsMap) From fab00cdc7cfa270fa4f4c28dec1e9f6e5a80e099 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 3 May 2024 15:27:52 -0500 Subject: [PATCH 37/40] code indentation and update variable --- zetaclient/zetacore_observer.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index de2f9707f4..474eb99ac5 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -164,11 +164,11 @@ func (co *CoreObserver) GetPendingCctxsWithinRatelimit(foreignChains []chains.Ch } // apply rate limiter or not according to the flags - applyLimit := ratelimiter.IsRateLimiterUsable(rateLimitFlags) + rateLimiterUsable := ratelimiter.IsRateLimiterUsable(rateLimitFlags) // fallback to non-rate-limited query if rate limiter is not usable cctxsMap := make(map[int64][]*types.CrossChainTx) - if !applyLimit { + if !rateLimiterUsable { for _, chain := range foreignChains { resp, _, err := co.bridge.ListPendingCctx(chain.ChainId) if err == nil && resp != nil { @@ -309,7 +309,8 @@ func (co *CoreObserver) ScheduleCctxEVM( chainID int64, cctxList []*types.CrossChainTx, ob interfaces.ChainClient, - signer interfaces.ChainSigner) { + signer interfaces.ChainSigner, +) { res, err := co.bridge.GetAllOutTxTrackerByChain(chainID, interfaces.Ascending) if err != nil { co.logger.Observer.Warn().Err(err).Msgf("ScheduleCctxEVM: GetAllOutTxTrackerByChain failed for chain %d", chainID) @@ -396,7 +397,8 @@ func (co *CoreObserver) ScheduleCctxBTC( chainID int64, cctxList []*types.CrossChainTx, ob interfaces.ChainClient, - signer interfaces.ChainSigner) { + signer interfaces.ChainSigner, +) { btcClient, ok := ob.(*bitcoin.BTCChainClient) if !ok { // should never happen co.logger.Observer.Error().Msgf("ScheduleCctxBTC: chain client is not a bitcoin client") From 56d2cc18666e4150324f9e597f48525f3d463437 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 3 May 2024 15:44:39 -0500 Subject: [PATCH 38/40] replace with --- zetaclient/core_context/zeta_core_context.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zetaclient/core_context/zeta_core_context.go b/zetaclient/core_context/zeta_core_context.go index 8390775959..0eb0341755 100644 --- a/zetaclient/core_context/zeta_core_context.go +++ b/zetaclient/core_context/zeta_core_context.go @@ -90,13 +90,13 @@ func (c *ZetaCoreContext) GetEnabledExternalChains() []chains.Chain { c.coreContextLock.RLock() defer c.coreContextLock.RUnlock() - foreignChains := make([]chains.Chain, 0) + externalChains := make([]chains.Chain, 0) for _, chain := range c.chainsEnabled { - if !chain.IsZetaChain() { - foreignChains = append(foreignChains, chain) + if chain.IsExternal { + externalChains = append(externalChains, chain) } } - return foreignChains + return externalChains } func (c *ZetaCoreContext) GetEVMChainParams(chainID int64) (*observertypes.ChainParams, bool) { From 6b04e01126756ff2233c88f14946107d646384f3 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 3 May 2024 16:11:55 -0500 Subject: [PATCH 39/40] fix unit test --- zetaclient/core_context/zeta_core_context_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/zetaclient/core_context/zeta_core_context_test.go b/zetaclient/core_context/zeta_core_context_test.go index 625fb40269..c2517f66da 100644 --- a/zetaclient/core_context/zeta_core_context_test.go +++ b/zetaclient/core_context/zeta_core_context_test.go @@ -151,12 +151,14 @@ func TestUpdateZetaCoreContext(t *testing.T) { } enabledChainsToUpdate := []chains.Chain{ { - ChainName: 1, - ChainId: 1, + ChainName: 1, + ChainId: 1, + IsExternal: true, }, { - ChainName: 2, - ChainId: 2, + ChainName: 2, + ChainId: 2, + IsExternal: true, }, chains.ZetaTestnetChain, } From f63f26488a1b9daacb8bed020c01880451458c0d Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Tue, 7 May 2024 10:24:24 -0500 Subject: [PATCH 40/40] added extra unit tests and improved func name --- pkg/math/float.go | 7 ++++++- pkg/math/float_test.go | 14 ++++++++++++++ x/crosschain/keeper/grpc_query_cctx_rate_limit.go | 4 ++-- x/crosschain/types/rate_limiter_flags.go | 6 +++--- x/crosschain/types/rate_limiter_flags_test.go | 2 +- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/pkg/math/float.go b/pkg/math/float.go index bf17753cab..b2fa2a7036 100644 --- a/pkg/math/float.go +++ b/pkg/math/float.go @@ -6,12 +6,17 @@ import ( // Percentage calculates the percentage of A over B. func Percentage(a, b *big.Int) *big.Float { + // cannot calculate if either a or b is nil + if a == nil || b == nil { + return nil + } + // if a is zero, return nil to avoid division by zero if b.Cmp(big.NewInt(0)) == 0 { return nil } - // convert a and a to big.Float + // convert a and b to big.Float floatA := new(big.Float).SetInt(a) floatB := new(big.Float).SetInt(b) diff --git a/pkg/math/float_test.go b/pkg/math/float_test.go index 1ccdc9d589..171645beb8 100644 --- a/pkg/math/float_test.go +++ b/pkg/math/float_test.go @@ -37,6 +37,20 @@ func TestPercentage(t *testing.T) { percentage: nil, fail: true, }, + { + name: "nil numerator", + numerator: nil, + denominator: big.NewInt(1000), + percentage: nil, + fail: true, + }, + { + name: "nil denominator", + numerator: big.NewInt(165), + denominator: nil, + percentage: nil, + fail: true, + }, } for _, tc := range testCases { diff --git a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go index 695c4029b2..1bb3c552a7 100644 --- a/x/crosschain/keeper/grpc_query_cctx_rate_limit.go +++ b/x/crosschain/keeper/grpc_query_cctx_rate_limit.go @@ -130,7 +130,7 @@ func (k Keeper) RateLimiterInput(c context.Context, req *types.QueryRateLimiterI // sum up past cctxs' value within window if inWindow && isPast { - pastCctxsValue = pastCctxsValue.Add(types.ConvertCctxValue(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) + pastCctxsValue = pastCctxsValue.Add(types.ConvertCctxValueToAzeta(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) } // add cctx to corresponding list @@ -141,7 +141,7 @@ func (k Keeper) RateLimiterInput(c context.Context, req *types.QueryRateLimiterI } else { cctxsPending = append(cctxsPending, cctx) // sum up non-past pending cctxs' value - pendingCctxsValue = pendingCctxsValue.Add(types.ConvertCctxValue(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) + pendingCctxsValue = pendingCctxsValue.Add(types.ConvertCctxValueToAzeta(chain.ChainId, cctx, gasAssetRateMap, erc20AssetRateMap)) } } } diff --git a/x/crosschain/types/rate_limiter_flags.go b/x/crosschain/types/rate_limiter_flags.go index 59c98111f2..27d7614623 100644 --- a/x/crosschain/types/rate_limiter_flags.go +++ b/x/crosschain/types/rate_limiter_flags.go @@ -72,8 +72,8 @@ func BuildAssetRateMapFromList(assetRates []AssetRate) (map[int64]AssetRate, map return gasAssetRateMap, erc20AssetRateMap } -// ConvertCctxValue converts the value of the cctx to azeta using given conversion rates -func ConvertCctxValue( +// ConvertCctxValueToAzeta converts the value of the cctx to azeta using given conversion rates +func ConvertCctxValueToAzeta( chainID int64, cctx *CrossChainTx, gasAssetRateMap map[int64]AssetRate, @@ -136,7 +136,7 @@ func RateLimitExceeded( currentCctxValue *sdkmath.Int, withdrawLimitInAzeta sdkmath.Int, ) bool { - amountZeta := ConvertCctxValue(chainID, cctx, gasAssetRateMap, erc20AssetRateMap) + amountZeta := ConvertCctxValueToAzeta(chainID, cctx, gasAssetRateMap, erc20AssetRateMap) *currentCctxValue = currentCctxValue.Add(amountZeta) return currentCctxValue.GT(withdrawLimitInAzeta) } diff --git a/x/crosschain/types/rate_limiter_flags_test.go b/x/crosschain/types/rate_limiter_flags_test.go index f8376d546a..e13a22d78b 100644 --- a/x/crosschain/types/rate_limiter_flags_test.go +++ b/x/crosschain/types/rate_limiter_flags_test.go @@ -379,7 +379,7 @@ func TestConvertCctxValue(t *testing.T) { cctx.GetCurrentOutTxParam().Amount = tt.amount // convert cctx value - value := types.ConvertCctxValue(tt.chainID, cctx, tt.gasAssetRates, tt.erc20AssetRates) + value := types.ConvertCctxValueToAzeta(tt.chainID, cctx, tt.gasAssetRates, tt.erc20AssetRates) require.Equal(t, tt.expectedValue, value) }) }