Skip to content

Commit 7c98376

Browse files
ws4charlielumtiskingpinXD
authored
refactor: move rate limiter to zetaclient and add metrics (#2110)
* initial commit of grpc pending cctx query with rate limiter * replace big.Float with sdk.Dec and update mock rate limiter flags * split big loop into backwards loop and forwards loop to be more accurate * adjust zetaclient code to query pending cctx with rate limit * update change log and add one more rate limiter flag test * use outboun amount for calculation * some minimum code refactor * created separate file for cctx query with rate limit * improved a few error handlling * use old cctx query as fallback when rate limiter is disabled; some renaming * fixed unit test compile * added unit test for fallback query * added unit tests for cctx value conversion * add changelog entry * added unit tests for query pending cctxs within rate limit * added total value in rate limiter window for monitoring purpose * Update x/crosschain/keeper/grpc_query_cctx_rate_limit.go Co-authored-by: Lucas Bertrand <[email protected]> * change variable name fCoin to foreignCoin * Update x/fungible/keeper/foreign_coins.go Co-authored-by: Lucas Bertrand <[email protected]> * Update x/crosschain/keeper/grpc_query_cctx_rate_limit_test.go Co-authored-by: Lucas Bertrand <[email protected]> * converted rate limiter query unit tests to table test * handle edge case when pending cctxs span wider block range than sliding window * added zero rate check; added comment to make unit test clearer * added unit test and note for method GetAllForeignCoinMap * treat Rate as average block rate; stop outbound when current rate limit exceeds Rate; updated metrics * refactor: allow zeta deposits to new zevm address (#2076) * allow zevm coin deposit to unknow addresses * add e2e tests * add changelog * add comments * add commented unit tests back * replace sdk.Dec with sdkmath.Int to represent cctx value in azeta * 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 <[email protected]> * add back other advanced tests * make generate * add comment * fix eth liquidity cap test * fix withdraw count --------- Co-authored-by: Charlie Chen <[email protected]> Co-authored-by: Charlie Chen <[email protected]> * removed incorrect Note * initiated rate limiter refactor and added metrics * added more unit tests and updated changelog * print more details from rate limiter output * reorder observer methods declaration * fix unit test * moved state irrelevant methods to types; renaming and cleaning * code indentation and update variable * replace with * fix unit test * added extra unit tests and improved func name --------- Co-authored-by: Lucas Bertrand <[email protected]> Co-authored-by: Tanmay <[email protected]>
1 parent b460556 commit 7c98376

31 files changed

+3359
-620
lines changed

changelog.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414
### Refactor
1515

1616
* [2094](https://github.com/zeta-chain/node/pull/2094) - upgrade go-tss to use cosmos v0.47
17-
18-
### Refactor
19-
17+
* [2110](https://github.com/zeta-chain/node/pull/2110) - move non-query rate limiter logic to zetaclient side and code refactor.
2018
* [2032](https://github.com/zeta-chain/node/pull/2032) - improve some general structure of the ZetaClient codebase
2119
* [2071](https://github.com/zeta-chain/node/pull/2071) - Modify chains struct to add all chain related information
2220
* [2124](https://github.com/zeta-chain/node/pull/2124) - removed unused variables and method

docs/openapi/openapi.swagger.yaml

+52
Original file line numberDiff line numberDiff line change
@@ -29015,6 +29015,32 @@ paths:
2901529015
$ref: '#/definitions/googlerpcStatus'
2901629016
tags:
2901729017
- Query
29018+
/zeta-chain/crosschain/rateLimiterInput:
29019+
get:
29020+
summary: Queries the input data of rate limiter.
29021+
operationId: Query_RateLimiterInput
29022+
responses:
29023+
"200":
29024+
description: A successful response.
29025+
schema:
29026+
$ref: '#/definitions/crosschainQueryRateLimiterInputResponse'
29027+
default:
29028+
description: An unexpected error response.
29029+
schema:
29030+
$ref: '#/definitions/googlerpcStatus'
29031+
parameters:
29032+
- name: limit
29033+
in: query
29034+
required: false
29035+
type: integer
29036+
format: int64
29037+
- name: window
29038+
in: query
29039+
required: false
29040+
type: string
29041+
format: int64
29042+
tags:
29043+
- Query
2901829044
/zeta-chain/crosschain/zetaAccounting:
2901929045
get:
2902029046
operationId: Query_ZetaAccounting
@@ -56829,6 +56855,32 @@ definitions:
5682956855
properties:
5683056856
rateLimiterFlags:
5683156857
$ref: '#/definitions/crosschainRateLimiterFlags'
56858+
crosschainQueryRateLimiterInputResponse:
56859+
type: object
56860+
properties:
56861+
height:
56862+
type: string
56863+
format: int64
56864+
cctxs_missed:
56865+
type: array
56866+
items:
56867+
type: object
56868+
$ref: '#/definitions/crosschainCrossChainTx'
56869+
cctxs_pending:
56870+
type: array
56871+
items:
56872+
type: object
56873+
$ref: '#/definitions/crosschainCrossChainTx'
56874+
total_pending:
56875+
type: string
56876+
format: uint64
56877+
past_cctxs_value:
56878+
type: string
56879+
pending_cctxs_value:
56880+
type: string
56881+
lowest_pending_cctx_height:
56882+
type: string
56883+
format: int64
5683256884
crosschainQueryZetaAccountingResponse:
5683356885
type: object
5683456886
properties:

pkg/math/float.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package math
2+
3+
import (
4+
"math/big"
5+
)
6+
7+
// Percentage calculates the percentage of A over B.
8+
func Percentage(a, b *big.Int) *big.Float {
9+
// cannot calculate if either a or b is nil
10+
if a == nil || b == nil {
11+
return nil
12+
}
13+
14+
// if a is zero, return nil to avoid division by zero
15+
if b.Cmp(big.NewInt(0)) == 0 {
16+
return nil
17+
}
18+
19+
// convert a and b to big.Float
20+
floatA := new(big.Float).SetInt(a)
21+
floatB := new(big.Float).SetInt(b)
22+
23+
// calculate the percentage of a over b
24+
percentage := new(big.Float).Quo(floatA, floatB)
25+
percentage.Mul(percentage, big.NewFloat(100))
26+
27+
return percentage
28+
}

pkg/math/float_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package math
2+
3+
import (
4+
"fmt"
5+
"math/big"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestPercentage(t *testing.T) {
12+
testCases := []struct {
13+
name string
14+
numerator *big.Int
15+
denominator *big.Int
16+
percentage *big.Float
17+
fail bool
18+
}{
19+
{
20+
name: "positive percentage",
21+
numerator: big.NewInt(165),
22+
denominator: big.NewInt(1000),
23+
percentage: big.NewFloat(16.5),
24+
fail: false,
25+
},
26+
{
27+
name: "negative percentage",
28+
numerator: big.NewInt(-165),
29+
denominator: big.NewInt(1000),
30+
percentage: big.NewFloat(-16.5),
31+
fail: false,
32+
},
33+
{
34+
name: "zero denominator",
35+
numerator: big.NewInt(1),
36+
denominator: big.NewInt(0),
37+
percentage: nil,
38+
fail: true,
39+
},
40+
{
41+
name: "nil numerator",
42+
numerator: nil,
43+
denominator: big.NewInt(1000),
44+
percentage: nil,
45+
fail: true,
46+
},
47+
{
48+
name: "nil denominator",
49+
numerator: big.NewInt(165),
50+
denominator: nil,
51+
percentage: nil,
52+
fail: true,
53+
},
54+
}
55+
56+
for _, tc := range testCases {
57+
tc := tc
58+
t.Run(tc.name, func(t *testing.T) {
59+
percentage := Percentage(tc.numerator, tc.denominator)
60+
fmt.Printf("percentage: %v\n", percentage)
61+
if tc.fail {
62+
require.Nil(t, percentage)
63+
} else {
64+
require.True(t, percentage.Cmp(tc.percentage) == 0)
65+
}
66+
})
67+
}
68+
}

proto/zetachain/zetacore/crosschain/query.proto

+21
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ service Query {
142142
returns (QueryRateLimiterFlagsResponse) {
143143
option (google.api.http).get = "/zeta-chain/crosschain/rateLimiterFlags";
144144
}
145+
146+
// Queries the input data of rate limiter.
147+
rpc RateLimiterInput(QueryRateLimiterInputRequest)
148+
returns (QueryRateLimiterInputResponse) {
149+
option (google.api.http).get = "/zeta-chain/crosschain/rateLimiterInput";
150+
}
145151
}
146152

147153
message QueryZetaAccountingRequest {}
@@ -270,6 +276,21 @@ message QueryListPendingCctxResponse {
270276
uint64 totalPending = 2;
271277
}
272278

279+
message QueryRateLimiterInputRequest {
280+
uint32 limit = 1;
281+
int64 window = 2;
282+
}
283+
284+
message QueryRateLimiterInputResponse {
285+
int64 height = 1;
286+
repeated CrossChainTx cctxs_missed = 2;
287+
repeated CrossChainTx cctxs_pending = 3;
288+
uint64 total_pending = 4;
289+
string past_cctxs_value = 5;
290+
string pending_cctxs_value = 6;
291+
int64 lowest_pending_cctx_height = 7;
292+
}
293+
273294
message QueryListPendingCctxWithinRateLimitRequest { uint32 limit = 1; }
274295

275296
message QueryListPendingCctxWithinRateLimitResponse {

proto/zetachain/zetacore/crosschain/rate_limiter_flags.proto

+12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ syntax = "proto3";
22
package zetachain.zetacore.crosschain;
33

44
import "gogoproto/gogo.proto";
5+
import "zetachain/zetacore/pkg/coin/coin.proto";
56

67
option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types";
78

@@ -28,3 +29,14 @@ message Conversion {
2829
(gogoproto.nullable) = false
2930
];
3031
}
32+
33+
message AssetRate {
34+
int64 chainId = 1;
35+
string asset = 2;
36+
uint32 decimals = 3;
37+
pkg.coin.CoinType coin_type = 4;
38+
string rate = 5 [
39+
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
40+
(gogoproto.nullable) = false
41+
];
42+
}

testutil/sample/crosschain.go

+62
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package sample
33
import (
44
"encoding/base64"
55
"encoding/json"
6+
"fmt"
67
"math/rand"
8+
"strings"
79
"testing"
810

911
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -49,6 +51,39 @@ func RateLimiterFlags() types.RateLimiterFlags {
4951
}
5052
}
5153

54+
// CustomRateLimiterFlags creates a custom rate limiter flags with the given parameters
55+
func CustomRateLimiterFlags(enabled bool, window int64, rate math.Uint, conversions []types.Conversion) types.RateLimiterFlags {
56+
return types.RateLimiterFlags{
57+
Enabled: enabled,
58+
Window: window,
59+
Rate: rate,
60+
Conversions: conversions,
61+
}
62+
}
63+
64+
func AssetRate() types.AssetRate {
65+
r := Rand()
66+
67+
return types.AssetRate{
68+
ChainId: r.Int63(),
69+
Asset: EthAddress().Hex(),
70+
Decimals: uint32(r.Uint64()),
71+
CoinType: coin.CoinType_ERC20,
72+
Rate: sdk.NewDec(r.Int63()),
73+
}
74+
}
75+
76+
// CustomAssetRate creates a custom asset rate with the given parameters
77+
func CustomAssetRate(chainID int64, asset string, decimals uint32, coinType coin.CoinType, rate sdk.Dec) types.AssetRate {
78+
return types.AssetRate{
79+
ChainId: chainID,
80+
Asset: strings.ToLower(asset),
81+
Decimals: decimals,
82+
CoinType: coinType,
83+
Rate: rate,
84+
}
85+
}
86+
5287
func OutTxTracker(t *testing.T, index string) types.OutTxTracker {
5388
r := newRandFromStringSeed(t, index)
5489

@@ -173,6 +208,33 @@ func CrossChainTx(t *testing.T, index string) *types.CrossChainTx {
173208
}
174209
}
175210

211+
// CustomCctxsInBlockRange create 1 cctx per block in block range [lowBlock, highBlock] (inclusive)
212+
func CustomCctxsInBlockRange(
213+
t *testing.T,
214+
lowBlock uint64,
215+
highBlock uint64,
216+
chainID int64,
217+
coinType coin.CoinType,
218+
asset string,
219+
amount uint64,
220+
status types.CctxStatus,
221+
) (cctxs []*types.CrossChainTx) {
222+
// create 1 cctx per block
223+
for i := lowBlock; i <= highBlock; i++ {
224+
nonce := i - 1
225+
cctx := CrossChainTx(t, fmt.Sprintf("%d-%d", chainID, nonce))
226+
cctx.CctxStatus.Status = status
227+
cctx.InboundTxParams.CoinType = coinType
228+
cctx.InboundTxParams.Asset = asset
229+
cctx.InboundTxParams.InboundTxObservedExternalHeight = i
230+
cctx.GetCurrentOutTxParam().ReceiverChainId = chainID
231+
cctx.GetCurrentOutTxParam().Amount = sdk.NewUint(amount)
232+
cctx.GetCurrentOutTxParam().OutboundTxTssNonce = nonce
233+
cctxs = append(cctxs, cctx)
234+
}
235+
return cctxs
236+
}
237+
176238
func LastBlockHeight(t *testing.T, index string) *types.LastBlockHeight {
177239
r := newRandFromStringSeed(t, index)
178240

0 commit comments

Comments
 (0)