From bb5c1c99b4810c07364d34b6e04fa5741fccc5d7 Mon Sep 17 00:00:00 2001 From: Nicolas Lara <nicolaslara@gmail.com> Date: Mon, 31 Oct 2022 05:40:38 +0100 Subject: [PATCH] Rate limit - Cleaner tests (#3183) * improved testing framework * can test both send and recv for success and failure * cleanner testing framework * added contract instantiation * working wasm integration * added params for contract config * extracted param registration * active rate limiting * calculating channel value * cleaner tests * fix issue with epochs * fixed tests * testing rate limit reset * linting * added receive middleware * added test for non-configured channel * make format * Revert "make format" This reverts commit 9ffdc37c3d473e3c640e8fd85872f3a2f178c857. * only applying format to ibc-rate-limit * applying fmt to app.go * added gov_module and changed no-quota default to "allow all" * added asymetric quotas * moved getters to modules.go * initial work to support multiple quotas * added multiple quotas * small fixes * reordered imports * added management messages * reorganized management messages and experimenting with e2e testing * commenting out test configuration test for now * added query * added flow unit test * cleanup * added AddChannel tests * format * test values are properly stored * testing remove channel * some more rate limiting tests * moved tests about test setup to the right place * fixed params * merged main * running gofumpt * added ibc-rate-limiting contract * added ibc-rate-limit middleware * added chain integration and tests * reverted change to match merged branch in main (#2341 instead of #2274) * added cosmwasm workflow * added a migrate message * added some doc comments to the state * added doc comments * fixed dependency after merging https://github.com/osmosis-labs/cosmos-sdk/pull/312 * added migration msg * added workflow * experimenting with better workflow * added missing $ * using env * Update x/ibc-rate-limit/contracts/rate-limiter/src/msg.rs Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com> * using stable for clippy * removed gitkeep * using the minimal profile for clippy * experimenting with cache * removed target from lints * cleaner matrix? * COmments & questions * debugging * more debugging * debug faster * quick cache debug * typo * quick workflow check * working tests with optimization * testing artifacts * split the wasm target into its own step * artifacts without slash * full working tests * clippy fixes * workflow without test data checks and clippy fixes * renamed CHANNEL_FLOWS * renaming and code clenaup * more renames * renames and code fixes * reordered imports * cargo fmt * added danom tracking * cleanup * refactoring * changes to the expiration logic so that balances are calculated based on the direction (as suggested by @ValarDragon) * slightly slower but considerably cleanner * cleanup attributes and removed redundancy when not testing * update to edition 2021 * added comments explaining the tests * removed .beaker * unified gitignore * removed second gitignore * better doc comments * spelling * spelling * added channel value cache * updated the middlware to use the new contract interface * update middleware to match new contract interface * added missing updates * updated dependencies * added missing helpers * go.mod changes shouldn't be in this branch * Revert "go.mod changes shouldn't be in this branch" This reverts commit f8b972a5ea2f2c2b8007fa31c3db5d4450d8ab56. * moved send and receive to sudo * reorganizing * calling the contract via sudo * lint * removed gitkeep * using sudo instead of execute * cleaned up and updated contract and integration * updated x86 test wasm file * fixed bad print * storing and instantiating the contract * setting up E2E tests for ibc rate limits * fixed proposal. Now just have to get the math right and cleanup * Using the supply for the channel value * experimenting with e2e tests * passing the contract keeper instead of instantiating it each time * changes from code review * added contract from main and changes to the middleware from code review * using the correct bank supply method * debugging issues with e2e tests. Everything works after one interaction from the cli, but not before * updated dependency to match latest sdk form (now that the changes to this repo have been applied) * working E2E test for rate limiting * added e2e tests and changes from code review * removed debug logs * remove debug logs * updated test to also use GetSupplyWithOffset * using correct GetSupplyWithOffset method * using correct GetSupplyWithOffset method * lint * removed e2e from this branch as it's not doing anything without the integration * lint * tests fail on CI because of "inactive" proposal. Is the deposit the issue? * remove rate limiting after the test so it doesn't interfeer with the other tests * using standard proposals instead of expedited * added packet reverts on unsuccessful acks and timeouts * lint * ran gofumpt * lint * added undo to the contract * integrating undo * updated contract with x86 wasm file * added undo for sent packages when they are rejected bia timeout or a bad ack * added a readme * markdown lint * added readme * abstracted params * better params and param tests * using a helper function instead of returning from the test * updated contract to allow for undo * added undo, readme, and cleanup based on reviews * updated wasm file with x86 version * using string params in e2e tests * updated to v12 * removed unnecessary keeper * only exposing what's needed * refactoring * updated types * added shell history to gitignore * adding only one wasm file to the codebase * remove test for same wasm files. No longer needed. * refactor based on code review * removed integration tests as they won't pass without integration * added params unit test * reorganized tests * reorganizing tests * refactoring * added address length limit * added tests and fixed lack of return * remove tests from bad merge * remove from bad merge again * comment * test helpers for cosmwasm contracts * added helpers for ibctesting * comments * removed unnecessary txConfig * fixed typos * clearer comment * using second helper function for ExportGenesis * added new wasm file * updated the contract to cosmwasm 1.1 and Uint256 for amounts * Fixed send with ibc assets. Better tests and error messages * updated contract with x86 version * gofumpt * fixed clippy errors * using the escrowed value as the channel value for native tokens * gofumpt * update fail string * initial experiments with moving the calculations into the contract * initial experiments with using the packet inside the contract * improved tests. Experiments with packet in the contract. * original contract * cleaner tests * more test cleanup * cleaner tests * cleanup * align values from the tests and the contract * fixed amounts for receive and cleanup code * removed redundant wrapping logic * adaped failed send test to the new testing abstractions * only manipulate time on chain A * remove commented out block * changed lints to stable so they change less often * gofumpt * update channel value tests * added x86 version of the contract for ci * remove lint type that doesn't exist on stable Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com> Co-authored-by: Dev Ojha <dojha@berkeley.edu> --- .github/workflows/contracts.yml | 12 +- .gitignore | 4 + app/apptesting/events.go | 6 + app/keepers/keepers.go | 32 +- tests/e2e/configurer/chain/commands.go | 2 +- tests/e2e/e2e_test.go | 123 +++++ .../rate-limiter/src/contract_tests.rs | 18 +- .../contracts/rate-limiter/src/error.rs | 8 +- .../rate-limiter/src/integration_tests.rs | 22 +- .../contracts/rate-limiter/src/packet.rs | 64 +++ .../contracts/rate-limiter/src/state.rs | 59 ++- x/ibc-rate-limit/ibc_middleware_test.go | 462 ++++++++++++++++++ x/ibc-rate-limit/ibc_module.go | 24 +- x/ibc-rate-limit/ics4_wrapper.go | 15 +- x/ibc-rate-limit/rate_limit.go | 44 +- x/ibc-rate-limit/testdata/rate_limiter.wasm | Bin 199435 -> 203516 bytes x/ibc-rate-limit/testutil/chain.go | 96 ++++ x/ibc-rate-limit/testutil/wasm.go | 70 +++ x/ibc-rate-limit/types/errors.go | 3 +- 19 files changed, 1012 insertions(+), 52 deletions(-) create mode 100644 x/ibc-rate-limit/contracts/rate-limiter/src/packet.rs create mode 100644 x/ibc-rate-limit/ibc_middleware_test.go create mode 100644 x/ibc-rate-limit/testutil/chain.go create mode 100644 x/ibc-rate-limit/testutil/wasm.go diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index f5f5301c3..50dc9addb 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -82,11 +82,11 @@ jobs: path: ${{ matrix.contract.workdir }}${{ matrix.contract.build }} retention-days: 1 -# - name: Check Test Data -# working-directory: ${{ matrix.contract.workdir }} -# if: ${{ matrix.contract.output != null }} -# run: > -# diff ${{ matrix.contract.output }} ${{ matrix.contract.build }} + - name: Check Test Data + working-directory: ${{ matrix.contract.workdir }} + if: ${{ matrix.contract.output != null }} + run: > + diff ${{ matrix.contract.output }} ${{ matrix.contract.build }} lints: @@ -107,7 +107,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly + toolchain: stable override: true components: rustfmt, clippy diff --git a/.gitignore b/.gitignore index 713ac5a10..ff99e2886 100644 --- a/.gitignore +++ b/.gitignore @@ -230,3 +230,7 @@ Cargo.lock .beaker blocks.db **/blocks.db* + +# Ignore e2e test artifacts (which clould leak information if commited) +.ash_history +.bash_history \ No newline at end of file diff --git a/app/apptesting/events.go b/app/apptesting/events.go index 7d0a4d4df..cdfae5028 100644 --- a/app/apptesting/events.go +++ b/app/apptesting/events.go @@ -21,11 +21,17 @@ func (s *KeeperTestHelper) AssertEventEmitted(ctx sdk.Context, eventTypeExpected func (s *KeeperTestHelper) FindEvent(events []sdk.Event, name string) sdk.Event { index := slices.IndexFunc(events, func(e sdk.Event) bool { return e.Type == name }) + if index == -1 { + return sdk.Event{} + } return events[index] } func (s *KeeperTestHelper) ExtractAttributes(event sdk.Event) map[string]string { attrs := make(map[string]string) + if event.Attributes == nil { + return attrs + } for _, a := range event.Attributes { attrs[string(a.Key)] = string(a.Value) } diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 7804dc340..83c53e3f2 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -2,6 +2,7 @@ package keepers import ( "github.com/CosmWasm/wasmd/x/wasm" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -32,6 +33,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/upgrade" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + ibcratelimit "github.com/osmosis-labs/osmosis/v12/x/ibc-rate-limit" + ibcratelimittypes "github.com/osmosis-labs/osmosis/v12/x/ibc-rate-limit/types" icahost "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host" icahostkeeper "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/keeper" @@ -110,10 +113,13 @@ type AppKeepers struct { SuperfluidKeeper *superfluidkeeper.Keeper GovKeeper *govkeeper.Keeper WasmKeeper *wasm.Keeper + ContractKeeper *wasmkeeper.PermissionedKeeper TokenFactoryKeeper *tokenfactorykeeper.Keeper + // IBC modules // transfer module - TransferModule transfer.AppModule + TransferModule transfer.AppModule + RateLimitingICS4Wrapper *ibcratelimit.ICS4Wrapper // keys to access the substores keys map[string]*sdk.KVStoreKey @@ -195,12 +201,24 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.ScopedIBCKeeper, ) + // ChannelKeeper wrapper for rate limiting SendPacket(). The wasmKeeper needs to be added after it's created + rateLimitingParams := appKeepers.GetSubspace(ibcratelimittypes.ModuleName) + rateLimitingParams = rateLimitingParams.WithKeyTable(ibcratelimittypes.ParamKeyTable()) + rateLimitingICS4Wrapper := ibcratelimit.NewICS4Middleware( + appKeepers.IBCKeeper.ChannelKeeper, + appKeepers.AccountKeeper, + nil, + appKeepers.BankKeeper, + rateLimitingParams, + ) + appKeepers.RateLimitingICS4Wrapper = &rateLimitingICS4Wrapper + // Create Transfer Keepers transferKeeper := ibctransferkeeper.NewKeeper( appCodec, appKeepers.keys[ibctransfertypes.StoreKey], appKeepers.GetSubspace(ibctransfertypes.ModuleName), - appKeepers.IBCKeeper.ChannelKeeper, + appKeepers.RateLimitingICS4Wrapper, // The ICS4Wrapper is replaced by the rateLimitingICS4Wrapper instead of the channel appKeepers.IBCKeeper.ChannelKeeper, &appKeepers.IBCKeeper.PortKeeper, appKeepers.AccountKeeper, @@ -211,6 +229,9 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.TransferModule = transfer.NewAppModule(*appKeepers.TransferKeeper) transferIBCModule := transfer.NewIBCModule(*appKeepers.TransferKeeper) + // RateLimiting IBC Middleware + rateLimitingTransferModule := ibcratelimit.NewIBCModule(transferIBCModule, appKeepers.RateLimitingICS4Wrapper) + icaHostKeeper := icahostkeeper.NewKeeper( appCodec, appKeepers.keys[icahosttypes.StoreKey], appKeepers.GetSubspace(icahosttypes.SubModuleName), @@ -226,7 +247,8 @@ func (appKeepers *AppKeepers) InitNormalKeepers( // Create static IBC router, add transfer route, then set and seal it ibcRouter := porttypes.NewRouter() ibcRouter.AddRoute(icahosttypes.SubModuleName, icaHostIBCModule). - AddRoute(ibctransfertypes.ModuleName, transferIBCModule) + // The transferIBC module is replaced by rateLimitingTransferModule + AddRoute(ibctransfertypes.ModuleName, &rateLimitingTransferModule) // Note: the sealing is done after creating wasmd and wiring that up // create evidence keeper with router @@ -343,6 +365,9 @@ func (appKeepers *AppKeepers) InitNormalKeepers( wasmOpts..., ) appKeepers.WasmKeeper = &wasmKeeper + // Update the ICS4Wrapper with the proper contractKeeper + appKeepers.ContractKeeper = wasmkeeper.NewDefaultPermissionKeeper(appKeepers.WasmKeeper) + appKeepers.RateLimitingICS4Wrapper.ContractKeeper = appKeepers.ContractKeeper // wire up x/wasm to IBC ibcRouter.AddRoute(wasm.ModuleName, wasm.NewIBCHandler(appKeepers.WasmKeeper, appKeepers.IBCKeeper.ChannelKeeper)) @@ -437,6 +462,7 @@ func (appKeepers *AppKeepers) initParamsKeeper(appCodec codec.BinaryCodec, legac paramsKeeper.Subspace(wasm.ModuleName) paramsKeeper.Subspace(tokenfactorytypes.ModuleName) paramsKeeper.Subspace(twaptypes.ModuleName) + paramsKeeper.Subspace(ibcratelimittypes.ModuleName) return paramsKeeper } diff --git a/tests/e2e/configurer/chain/commands.go b/tests/e2e/configurer/chain/commands.go index ee070b4b7..335d4924e 100644 --- a/tests/e2e/configurer/chain/commands.go +++ b/tests/e2e/configurer/chain/commands.go @@ -105,7 +105,7 @@ func (n *NodeConfig) FailIBCTransfer(from, recipient, amount string) { cmd := []string{"osmosisd", "tx", "ibc-transfer", "transfer", "transfer", "channel-0", recipient, amount, fmt.Sprintf("--from=%s", from)} - _, _, err := n.containerManager.ExecTxCmdWithSuccessString(n.t, n.chainId, n.Name, cmd, "rate limit exceeded") + _, _, err := n.containerManager.ExecTxCmdWithSuccessString(n.t, n.chainId, n.Name, cmd, "Rate Limit exceeded") require.NoError(n.t, err) n.LogActionF("Failed to send IBC transfer (as expected)") diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 5c9b97e0a..ec98a2908 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -1,12 +1,17 @@ package e2e import ( + "encoding/json" "fmt" + "io" "os" "path/filepath" "strconv" "time" + paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" + ibcratelimittypes "github.com/osmosis-labs/osmosis/v12/x/ibc-rate-limit/types" + sdk "github.com/cosmos/cosmos-sdk/types" coretypes "github.com/tendermint/tendermint/rpc/core/types" @@ -107,6 +112,124 @@ func (s *IntegrationTestSuite) TestSuperfluidVoting() { ) } +// Copy a file from A to B with io.Copy +func copyFile(a, b string) error { + source, err := os.Open(a) + if err != nil { + return err + } + defer source.Close() + destination, err := os.Create(b) + if err != nil { + return err + } + defer destination.Close() + _, err = io.Copy(destination, source) + if err != nil { + return err + } + return nil +} + +func (s *IntegrationTestSuite) TestIBCTokenTransferRateLimiting() { + if s.skipIBC { + s.T().Skip("Skipping IBC tests") + } + chainA := s.configurer.GetChainConfig(0) + chainB := s.configurer.GetChainConfig(1) + + node, err := chainA.GetDefaultNode() + s.NoError(err) + + supply, err := node.QueryTotalSupply() + s.NoError(err) + osmoSupply := supply.AmountOf("uosmo") + + // balance, err := node.QueryBalances(chainA.NodeConfigs[1].PublicAddress) + // s.NoError(err) + + f, err := osmoSupply.ToDec().Float64() + s.NoError(err) + + over := f * 0.02 + + // Sending >1% + chainA.SendIBC(chainB, chainB.NodeConfigs[0].PublicAddress, sdk.NewInt64Coin(initialization.OsmoDenom, int64(over))) + + // copy the contract from x/rate-limit/testdata/ + wd, err := os.Getwd() + s.NoError(err) + // co up two levels + projectDir := filepath.Dir(filepath.Dir(wd)) + fmt.Println(wd, projectDir) + err = copyFile(projectDir+"/x/ibc-rate-limit/testdata/rate_limiter.wasm", wd+"/scripts/rate_limiter.wasm") + s.NoError(err) + node.StoreWasmCode("rate_limiter.wasm", initialization.ValidatorWalletName) + chainA.LatestCodeId += 1 + node.InstantiateWasmContract( + strconv.Itoa(chainA.LatestCodeId), + fmt.Sprintf(`{"gov_module": "%s", "ibc_module": "%s", "paths": [{"channel_id": "channel-0", "denom": "%s", "quotas": [{"name":"testQuota", "duration": 86400, "send_recv": [1, 1]}] } ] }`, node.PublicAddress, node.PublicAddress, initialization.OsmoToken.Denom), + initialization.ValidatorWalletName) + + // Using code_id 1 because this is the only contract right now. This may need to change if more contracts are added + contracts, err := node.QueryContractsFromId(chainA.LatestCodeId) + s.NoError(err) + s.Require().Len(contracts, 1, "Wrong number of contracts for the rate limiter") + + proposal := paramsutils.ParamChangeProposalJSON{ + Title: "Param Change", + Description: "Changing the rate limit contract param", + Changes: paramsutils.ParamChangesJSON{ + paramsutils.ParamChangeJSON{ + Subspace: ibcratelimittypes.ModuleName, + Key: "contract", + Value: []byte(fmt.Sprintf(`"%s"`, contracts[0])), + }, + }, + Deposit: "625000000uosmo", + } + proposalJson, err := json.Marshal(proposal) + s.NoError(err) + + node.SubmitParamChangeProposal(string(proposalJson), initialization.ValidatorWalletName) + chainA.LatestProposalNumber += 1 + + for _, n := range chainA.NodeConfigs { + n.VoteYesProposal(initialization.ValidatorWalletName, chainA.LatestProposalNumber) + } + + // The value is returned as a string, so we have to unmarshal twice + type Params struct { + Key string `json:"key"` + Subspace string `json:"subspace"` + Value string `json:"value"` + } + + s.Eventually( + func() bool { + var params Params + node.QueryParams(ibcratelimittypes.ModuleName, "contract", ¶ms) + var val string + err := json.Unmarshal([]byte(params.Value), &val) + if err != nil { + return false + } + return val != "" + }, + 1*time.Minute, + 10*time.Millisecond, + "Osmosis node failed to retrieve params", + ) + + // Sending <1%. Should work + chainA.SendIBC(chainB, chainB.NodeConfigs[0].PublicAddress, sdk.NewInt64Coin(initialization.OsmoDenom, 1)) + // Sending >1%. Should fail + node.FailIBCTransfer(initialization.ValidatorWalletName, chainB.NodeConfigs[0].PublicAddress, fmt.Sprintf("%duosmo", int(over))) + + // Removing the rate limit so it doesn't affect other tests + node.WasmExecute(contracts[0], `{"remove_path": {"channel_id": "channel-0", "denom": "uosmo"}}`, initialization.ValidatorWalletName) +} + // TestAddToExistingLockPostUpgrade ensures addToExistingLock works for locks created preupgrade. func (s *IntegrationTestSuite) TestAddToExistingLockPostUpgrade() { if s.skipUpgrade { diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/contract_tests.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/contract_tests.rs index 16bc08802..fa5b99e49 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/contract_tests.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/contract_tests.rs @@ -52,7 +52,7 @@ fn consume_allowance() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_300_u32.into(), funds: 300_u32.into(), }; let res = sudo(deps.as_mut(), mock_env(), msg).unwrap(); @@ -64,7 +64,7 @@ fn consume_allowance() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_300_u32.into(), funds: 300_u32.into(), }; let err = sudo(deps.as_mut(), mock_env(), msg).unwrap_err(); @@ -91,7 +91,7 @@ fn symetric_flows_dont_consume_allowance() { let send_msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_300_u32.into(), funds: 300_u32.into(), }; let recv_msg = SudoMsg::RecvPacket { @@ -154,7 +154,7 @@ fn asymetric_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_060_u32.into(), funds: 60_u32.into(), }; let res = sudo(deps.as_mut(), mock_env(), msg).unwrap(); @@ -166,7 +166,7 @@ fn asymetric_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_060_u32.into(), funds: 60_u32.into(), }; @@ -195,7 +195,7 @@ fn asymetric_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_060_u32.into(), funds: 60_u32.into(), }; let err = sudo(deps.as_mut(), mock_env(), msg.clone()).unwrap_err(); @@ -205,7 +205,7 @@ fn asymetric_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_060_u32.into(), funds: 30_u32.into(), }; let res = sudo(deps.as_mut(), mock_env(), msg.clone()).unwrap(); @@ -256,7 +256,7 @@ fn query_state() { let send_msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_300_u32.into(), funds: 300_u32.into(), }; sudo(deps.as_mut(), mock_env(), send_msg.clone()).unwrap(); @@ -343,7 +343,7 @@ fn undo_send() { let send_msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_300_u32.into(), funds: 300_u32.into(), }; let undo_msg = SudoMsg::UndoSend { diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/error.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/error.rs index dc40f708d..367180baf 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/error.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/error.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{StdError, Timestamp}; +use cosmwasm_std::{StdError, Timestamp, Uint256}; use thiserror::Error; #[derive(Error, Debug)] @@ -9,10 +9,14 @@ pub enum ContractError { #[error("Unauthorized")] Unauthorized {}, - #[error("IBC Rate Limit exceded for channel {channel:?} and denom {denom:?}. Try again after {reset:?}")] + #[error("IBC Rate Limit exceeded for {channel}/{denom}. Tried to transfer {amount} which exceeds capacity on the '{quota_name}' quota ({used}/{max}). Try again after {reset:?}")] RateLimitExceded { channel: String, denom: String, + amount: Uint256, + quota_name: String, + used: Uint256, + max: Uint256, reset: Timestamp, }, diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/integration_tests.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/integration_tests.rs index 66a145b39..d5d76acb0 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/integration_tests.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/integration_tests.rs @@ -82,7 +82,7 @@ fn expiration() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_300_u32.into(), funds: 300_u32.into(), }; let cosmos_msg = cw_rate_limit_contract.sudo(msg); @@ -105,7 +105,7 @@ fn expiration() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_300_u32.into(), funds: 300_u32.into(), }; let cosmos_msg = cw_rate_limit_contract.sudo(msg); @@ -123,7 +123,7 @@ fn expiration() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 3_000_u32.into(), + channel_value: 3_300_u32.into(), funds: 300_u32.into(), }; @@ -162,7 +162,7 @@ fn multiple_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 100_u32.into(), + channel_value: 101_u32.into(), funds: 1_u32.into(), }; let cosmos_msg = cw_rate_limit_contract.sudo(msg); @@ -172,7 +172,7 @@ fn multiple_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 100_u32.into(), + channel_value: 101_u32.into(), funds: 1_u32.into(), }; let cosmos_msg = cw_rate_limit_contract.sudo(msg); @@ -188,7 +188,7 @@ fn multiple_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 100_u32.into(), + channel_value: 101_u32.into(), funds: 1_u32.into(), }; @@ -207,7 +207,7 @@ fn multiple_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 100_u32.into(), + channel_value: 101_u32.into(), funds: 1_u32.into(), }; let cosmos_msg = cw_rate_limit_contract.sudo(msg); @@ -224,7 +224,7 @@ fn multiple_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 100_u32.into(), + channel_value: 101_u32.into(), funds: 1_u32.into(), }; let cosmos_msg = cw_rate_limit_contract.sudo(msg); @@ -240,7 +240,7 @@ fn multiple_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 100_u32.into(), + channel_value: 101_u32.into(), funds: 1_u32.into(), }; let cosmos_msg = cw_rate_limit_contract.sudo(msg); @@ -257,7 +257,7 @@ fn multiple_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 100_u32.into(), + channel_value: 101_u32.into(), funds: 1_u32.into(), }; let cosmos_msg = cw_rate_limit_contract.sudo(msg); @@ -272,7 +272,7 @@ fn multiple_quotas() { let msg = SudoMsg::SendPacket { channel_id: format!("channel"), denom: format!("denom"), - channel_value: 100_u32.into(), + channel_value: 101_u32.into(), funds: 1_u32.into(), }; let cosmos_msg = cw_rate_limit_contract.sudo(msg); diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/packet.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/packet.rs new file mode 100644 index 000000000..6bc5b8cfe --- /dev/null +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/packet.rs @@ -0,0 +1,64 @@ +use cosmwasm_std::{Addr, Deps, Timestamp}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct Height { + /// Previously known as "epoch" + revision_number: Option<u64>, + + /// The height of a block + revision_height: Option<u64>, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct FungibleTokenData { + denom: String, + amount: u128, + sender: Addr, + receiver: Addr, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct Packet { + pub sequence: u64, + pub source_port: String, + pub source_channel: String, + pub destination_port: String, + pub destination_channel: String, + pub data: FungibleTokenData, + pub timeout_height: Height, + pub timeout_timestamp: Option<Timestamp>, +} + +impl Packet { + pub fn channel_value(&self, _deps: Deps) -> u128 { + // let balance = deps.querier.query_all_balances("address", self.data.denom); + // deps.querier.sup + return 125000000000011250 * 2; + } + + pub fn get_funds(&self) -> u128 { + return self.data.amount; + } + + fn local_channel(&self) -> String { + // Pick the appropriate channel depending on whether this is a send or a recv + return self.destination_channel.clone(); + } + + fn local_demom(&self) -> String { + // This should actually convert the denom from the packet to the osmosis denom, but for now, just returning this + return self.data.denom.clone(); + } + + pub fn path_data(&self) -> (String, String) { + let denom = self.local_demom(); + let channel = if denom.starts_with("ibc/") { + self.local_channel() + } else { + "any".to_string() // native tokens are rate limited globally + }; + + return (channel, denom); + } +} diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/state.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/state.rs index 523794648..e28fc1004 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/state.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/state.rs @@ -102,6 +102,15 @@ impl Flow { } } + /// returns the balance in a direction. This is used for displaying cleaner errors + pub fn balance_on(&self, direction: &FlowType) -> Uint256 { + let (balance_in, balance_out) = self.balance(); + match direction { + FlowType::In => balance_in, + FlowType::Out => balance_out, + } + } + /// If now is greater than the period_end, the Flow is considered expired. pub fn is_expired(&self, now: Timestamp) -> bool { self.period_end < now @@ -182,6 +191,15 @@ impl Quota { None => (0_u32.into(), 0_u32.into()), // This should never happen, but ig the channel value is not set, we disallow any transfer } } + + /// returns the capacity in a direction. This is used for displaying cleaner errors + pub fn capacity_on(&self, direction: &FlowType) -> Uint256 { + let (max_in, max_out) = self.capacity(); + match direction { + FlowType::In => max_in, + FlowType::Out => max_out, + } + } } impl From<&QuotaMsg> for Quota { @@ -209,6 +227,29 @@ pub struct RateLimit { pub flow: Flow, } +// The channel value on send depends on the amount on escrow. The ibc transfer +// module modifies the escrow amount by "funds" on sends before calling the +// contract. This function takes that into account so that the channel value +// that we track matches the channel value at the moment when the ibc +// transaction started executing +fn calculate_channel_value( + channel_value: Uint256, + denom: &str, + funds: Uint256, + direction: &FlowType, +) -> Uint256 { + match direction { + FlowType::Out => { + if denom.contains("ibc") { + channel_value + funds // Non-Native tokens get removed from the supply on send. Add that amount back + } else { + channel_value - funds // Native tokens increase escrow amount on send. Remove that amount here + } + } + FlowType::In => channel_value, + } +} + impl RateLimit { /// Checks if a transfer is allowed and updates the data structures /// accordingly. @@ -224,10 +265,22 @@ impl RateLimit { channel_value: Uint256, now: Timestamp, ) -> Result<Self, ContractError> { + // Flow used before this transaction is applied. + // This is used to make error messages more informative + let initial_flow = self.flow.balance_on(direction); + + // Apply the transfer. From here on, we will updated the flow with the new transfer + // and check if it exceeds the quota at the current time + let expired = self.flow.apply_transfer(direction, funds, now, &self.quota); // Cache the channel value if it has never been set or it has expired. if self.quota.channel_value.is_none() || expired { - self.quota.channel_value = Some(channel_value) + self.quota.channel_value = Some(calculate_channel_value( + channel_value, + &path.denom, + funds, + direction, + )) } let (max_in, max_out) = self.quota.capacity(); @@ -236,6 +289,10 @@ impl RateLimit { true => Err(ContractError::RateLimitExceded { channel: path.channel.to_string(), denom: path.denom.to_string(), + amount: funds, + quota_name: self.quota.name.to_string(), + used: initial_flow, + max: self.quota.capacity_on(direction), reset: self.flow.period_end, }), false => Ok(RateLimit { diff --git a/x/ibc-rate-limit/ibc_middleware_test.go b/x/ibc-rate-limit/ibc_middleware_test.go new file mode 100644 index 000000000..497916d8b --- /dev/null +++ b/x/ibc-rate-limit/ibc_middleware_test.go @@ -0,0 +1,462 @@ +package ibc_rate_limit_test + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + "testing" + "time" + + ibc_rate_limit "github.com/osmosis-labs/osmosis/v12/x/ibc-rate-limit" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/osmosis-labs/osmosis/v12/app" + "github.com/osmosis-labs/osmosis/v12/app/apptesting" + "github.com/osmosis-labs/osmosis/v12/x/ibc-rate-limit/testutil" + "github.com/osmosis-labs/osmosis/v12/x/ibc-rate-limit/types" + "github.com/stretchr/testify/suite" +) + +type MiddlewareTestSuite struct { + apptesting.KeeperTestHelper + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *osmosisibctesting.TestChain + chainB *osmosisibctesting.TestChain + path *ibctesting.Path +} + +// Setup +func TestMiddlewareTestSuite(t *testing.T) { + suite.Run(t, new(MiddlewareTestSuite)) +} + +func SetupTestingApp() (ibctesting.TestingApp, map[string]json.RawMessage) { + osmosisApp := app.Setup(false) + return osmosisApp, app.NewDefaultGenesisState() +} + +func NewTransferPath(chainA, chainB *osmosisibctesting.TestChain) *ibctesting.Path { + path := ibctesting.NewPath(chainA.TestChain, chainB.TestChain) + path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort + path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort + path.EndpointA.ChannelConfig.Version = transfertypes.Version + path.EndpointB.ChannelConfig.Version = transfertypes.Version + return path +} + +func (suite *MiddlewareTestSuite) SetupTest() { + suite.Setup() + ibctesting.DefaultTestingAppInit = SetupTestingApp + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = &osmosisibctesting.TestChain{ + TestChain: suite.coordinator.GetChain(ibctesting.GetChainID(1)), + } + // Remove epochs to prevent minting + suite.chainA.MoveEpochsToTheFuture() + suite.chainB = &osmosisibctesting.TestChain{ + TestChain: suite.coordinator.GetChain(ibctesting.GetChainID(2)), + } + suite.path = NewTransferPath(suite.chainA, suite.chainB) + suite.coordinator.Setup(suite.path) +} + +// Helpers +func (suite *MiddlewareTestSuite) MessageFromAToB(denom string, amount sdk.Int) sdk.Msg { + coin := sdk.NewCoin(denom, amount) + port := suite.path.EndpointA.ChannelConfig.PortID + channel := suite.path.EndpointA.ChannelID + accountFrom := suite.chainA.SenderAccount.GetAddress().String() + accountTo := suite.chainB.SenderAccount.GetAddress().String() + timeoutHeight := clienttypes.NewHeight(0, 100) + return transfertypes.NewMsgTransfer( + port, + channel, + coin, + accountFrom, + accountTo, + timeoutHeight, + 0, + ) +} + +func (suite *MiddlewareTestSuite) MessageFromBToA(denom string, amount sdk.Int) sdk.Msg { + coin := sdk.NewCoin(denom, amount) + port := suite.path.EndpointB.ChannelConfig.PortID + channel := suite.path.EndpointB.ChannelID + accountFrom := suite.chainB.SenderAccount.GetAddress().String() + accountTo := suite.chainA.SenderAccount.GetAddress().String() + timeoutHeight := clienttypes.NewHeight(0, 100) + return transfertypes.NewMsgTransfer( + port, + channel, + coin, + accountFrom, + accountTo, + timeoutHeight, + 0, + ) +} + +// Tests that a receiver address longer than 4096 is not accepted +func (suite *MiddlewareTestSuite) TestInvalidReceiver() { + msg := transfertypes.NewMsgTransfer( + suite.path.EndpointB.ChannelConfig.PortID, + suite.path.EndpointB.ChannelID, + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1)), + suite.chainB.SenderAccount.GetAddress().String(), + strings.Repeat("x", 4097), + clienttypes.NewHeight(0, 100), + 0, + ) + _, ack, _ := suite.FullSendBToA(msg) + suite.Require().Contains(string(ack), "error", + "acknowledgment is not an error") + suite.Require().Contains(string(ack), sdkerrors.ErrInvalidAddress.Error(), + "acknowledgment error is not of the right type") +} + +func (suite *MiddlewareTestSuite) FullSendBToA(msg sdk.Msg) (*sdk.Result, string, error) { + sendResult, err := suite.chainB.SendMsgsNoCheck(msg) + suite.Require().NoError(err) + + packet, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents()) + suite.Require().NoError(err) + + err = suite.path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + res, err := suite.path.EndpointA.RecvPacketWithResult(packet) + suite.Require().NoError(err) + + ack, err := ibctesting.ParseAckFromEvents(res.GetEvents()) + + err = suite.path.EndpointA.UpdateClient() + suite.Require().NoError(err) + err = suite.path.EndpointB.UpdateClient() + suite.Require().NoError(err) + + return sendResult, string(ack), err +} + +func (suite *MiddlewareTestSuite) FullSendAToB(msg sdk.Msg) (*sdk.Result, string, error) { + sendResult, err := suite.chainA.SendMsgsNoCheck(msg) + if err != nil { + return nil, "", err + } + + packet, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents()) + if err != nil { + return nil, "", err + } + + err = suite.path.EndpointB.UpdateClient() + if err != nil { + return nil, "", err + } + + res, err := suite.path.EndpointB.RecvPacketWithResult(packet) + if err != nil { + return nil, "", err + } + + ack, err := ibctesting.ParseAckFromEvents(res.GetEvents()) + if err != nil { + return nil, "", err + } + + err = suite.path.EndpointA.UpdateClient() + if err != nil { + return nil, "", err + } + err = suite.path.EndpointB.UpdateClient() + if err != nil { + return nil, "", err + } + + return sendResult, string(ack), nil +} + +func (suite *MiddlewareTestSuite) AssertReceive(success bool, msg sdk.Msg) (string, error) { + _, ack, err := suite.FullSendBToA(msg) + if success { + suite.Require().NoError(err) + suite.Require().NotContains(string(ack), "error", + "acknowledgment is an error") + } else { + suite.Require().Contains(string(ack), "error", + "acknowledgment is not an error") + suite.Require().Contains(string(ack), types.ErrRateLimitExceeded.Error(), + "acknowledgment error is not of the right type") + } + return ack, err +} + +func (suite *MiddlewareTestSuite) AssertSend(success bool, msg sdk.Msg) (*sdk.Result, error) { + r, _, err := suite.FullSendAToB(msg) + if success { + suite.Require().NoError(err, "IBC send failed. Expected success. %s", err) + } else { + suite.Require().Error(err, "IBC send succeeded. Expected failure") + suite.ErrorContains(err, types.ErrRateLimitExceeded.Error(), "Bad error type") + } + return r, err +} + +func (suite *MiddlewareTestSuite) BuildChannelQuota(name, denom string, duration, send_precentage, recv_percentage uint32) string { + return fmt.Sprintf(` + {"channel_id": "channel-0", "denom": "%s", "quotas": [{"name":"%s", "duration": %d, "send_recv":[%d, %d]}] } + `, denom, name, duration, send_precentage, recv_percentage) +} + +// Tests + +// Test that Sending IBC messages works when the middleware isn't configured +func (suite *MiddlewareTestSuite) TestSendTransferNoContract() { + one := sdk.NewInt(1) + suite.AssertSend(true, suite.MessageFromAToB(sdk.DefaultBondDenom, one)) +} + +// Test that Receiving IBC messages works when the middleware isn't configured +func (suite *MiddlewareTestSuite) TestReceiveTransferNoContract() { + one := sdk.NewInt(1) + suite.AssertReceive(true, suite.MessageFromBToA(sdk.DefaultBondDenom, one)) +} + +func (suite *MiddlewareTestSuite) initializeEscrow() (totalEscrow, expectedSed sdk.Int) { + osmosisApp := suite.chainA.GetOsmosisApp() + supply := osmosisApp.BankKeeper.GetSupplyWithOffset(suite.chainA.GetContext(), sdk.DefaultBondDenom) + + // Move some funds from chainA to chainB so that there is something in escrow + // Each user has 10% of the supply, so we send most of the funds from one user to chainA + transferAmount := supply.Amount.QuoRaw(20) + + // When sending, the amount we're sending goes into escrow before we enter the middleware and thus + // it's used as part of the channel value in the rate limiting contract + // To account for that, we subtract the amount we'll send first (2.5% of transferAmount) here + sendAmount := transferAmount.QuoRaw(40) + + // Send from A to B + _, _, err := suite.FullSendAToB(suite.MessageFromAToB(sdk.DefaultBondDenom, transferAmount.Sub(sendAmount))) + suite.Require().NoError(err) + // Send from A to B + _, _, err = suite.FullSendBToA(suite.MessageFromBToA(sdk.DefaultBondDenom, transferAmount.Sub(sendAmount))) + suite.Require().NoError(err) + + return transferAmount, sendAmount +} + +func (suite *MiddlewareTestSuite) fullSendTest(native bool) map[string]string { + quotaPercentage := 5 + suite.initializeEscrow() + // Get the denom and amount to send + denom := sdk.DefaultBondDenom + if !native { + denomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", "channel-0", denom)) + denom = denomTrace.IBCDenom() + } + + osmosisApp := suite.chainA.GetOsmosisApp() + + // This is the first one. Inside the tests. It works as expected. + channelValue := ibc_rate_limit.CalculateChannelValue(suite.chainA.GetContext(), denom, "transfer", "channel-0", osmosisApp.BankKeeper) + + // The amount to be sent is send 2.5% (quota is 5%) + quota := channelValue.QuoRaw(int64(100 / quotaPercentage)) + sendAmount := quota.QuoRaw(2) + + fmt.Printf("Testing send rate limiting for denom=%s, channelValue=%s, quota=%s, sendAmount=%s\n", denom, channelValue, quota, sendAmount) + + // Setup contract + suite.chainA.StoreContractCode(&suite.Suite) + quotas := suite.BuildChannelQuota("weekly", denom, 604800, 5, 5) + fmt.Println(quotas) + addr := suite.chainA.InstantiateContract(&suite.Suite, quotas) + suite.chainA.RegisterRateLimitingContract(addr) + + // TODO: Remove native from MessafeFrom calls + // send 2.5% (quota is 5%) + fmt.Println("trying to send ", sendAmount) + suite.AssertSend(true, suite.MessageFromAToB(denom, sendAmount)) + + // send 2.5% (quota is 5%) + fmt.Println("trying to send ", sendAmount) + r, _ := suite.AssertSend(true, suite.MessageFromAToB(denom, sendAmount)) + + // Calculate remaining allowance in the quota + attrs := suite.ExtractAttributes(suite.FindEvent(r.GetEvents(), "wasm")) + + used, ok := sdk.NewIntFromString(attrs["weekly_used_out"]) + suite.Require().True(ok) + + suite.Require().Equal(used, sendAmount.MulRaw(2)) + + // Sending above the quota should fail. We use 2 instead of 1 here to avoid rounding issues + suite.AssertSend(false, suite.MessageFromAToB(denom, sdk.NewInt(2))) + return attrs +} + +// Test rate limiting on sends +func (suite *MiddlewareTestSuite) TestSendTransferWithRateLimitingNative() { + suite.fullSendTest(true) +} + +// Test rate limiting on sends +func (suite *MiddlewareTestSuite) TestSendTransferWithRateLimitingNonNative() { + suite.fullSendTest(false) +} + +// Test rate limits are reset when the specified time period has passed +func (suite *MiddlewareTestSuite) TestSendTransferReset() { + // Same test as above, but the quotas get reset after time passes + attrs := suite.fullSendTest(true) + parts := strings.Split(attrs["weekly_period_end"], ".") // Splitting timestamp into secs and nanos + secs, err := strconv.ParseInt(parts[0], 10, 64) + suite.Require().NoError(err) + nanos, err := strconv.ParseInt(parts[1], 10, 64) + suite.Require().NoError(err) + resetTime := time.Unix(secs, nanos) + + // Move chainA forward one block + suite.chainA.NextBlock() + suite.chainA.SenderAccount.SetSequence(suite.chainA.SenderAccount.GetSequence() + 1) + + // Reset time + one second + oneSecAfterReset := resetTime.Add(time.Second) + suite.coordinator.IncrementTimeBy(oneSecAfterReset.Sub(suite.coordinator.CurrentTime)) + + // Sending should succeed again + suite.AssertSend(true, suite.MessageFromAToB(sdk.DefaultBondDenom, sdk.NewInt(1))) +} + +// Test rate limiting on receives +func (suite *MiddlewareTestSuite) fullRecvTest(native bool) { + quotaPercentage := 5 + suite.initializeEscrow() + // Get the denom and amount to send + denom := sdk.DefaultBondDenom + if !native { + denomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", "channel-0", denom)) + denom = denomTrace.IBCDenom() + } + + osmosisApp := suite.chainA.GetOsmosisApp() + + // This is the first one. Inside the tests. It works as expected. + channelValue := ibc_rate_limit.CalculateChannelValue(suite.chainA.GetContext(), denom, "transfer", "channel-0", osmosisApp.BankKeeper) + + // The amount to be sent is send 2.5% (quota is 5%) + quota := channelValue.QuoRaw(int64(100 / quotaPercentage)) + sendAmount := quota.QuoRaw(2) + + fmt.Printf("Testing recv rate limiting for denom=%s, channelValue=%s, quota=%s, sendAmount=%s\n", denom, channelValue, quota, sendAmount) + + // Setup contract + suite.chainA.StoreContractCode(&suite.Suite) + quotas := suite.BuildChannelQuota("weekly", denom, 604800, 5, 5) + addr := suite.chainA.InstantiateContract(&suite.Suite, quotas) + suite.chainA.RegisterRateLimitingContract(addr) + + // receive 2.5% (quota is 5%) + suite.AssertReceive(true, suite.MessageFromBToA(denom, sendAmount)) + + // receive 2.5% (quota is 5%) + suite.AssertReceive(true, suite.MessageFromBToA(denom, sendAmount)) + + // Sending above the quota should fail. We send 2 instead of 1 to account for rounding errors + suite.AssertReceive(false, suite.MessageFromBToA(denom, sdk.NewInt(2))) +} + +func (suite *MiddlewareTestSuite) TestRecvTransferWithRateLimitingNative() { + suite.fullRecvTest(true) +} + +func (suite *MiddlewareTestSuite) TestRecvTransferWithRateLimitingNonNative() { + suite.fullRecvTest(false) +} + +// Test no rate limiting occurs when the contract is set, but not quotas are condifured for the path +func (suite *MiddlewareTestSuite) TestSendTransferNoQuota() { + // Setup contract + suite.chainA.StoreContractCode(&suite.Suite) + addr := suite.chainA.InstantiateContract(&suite.Suite, ``) + suite.chainA.RegisterRateLimitingContract(addr) + + // send 1 token. + // If the contract doesn't have a quota for the current channel, all transfers are allowed + suite.AssertSend(true, suite.MessageFromAToB(sdk.DefaultBondDenom, sdk.NewInt(1))) +} + +// Test rate limits are reverted if a "send" fails +func (suite *MiddlewareTestSuite) TestFailedSendTransfer() { + suite.initializeEscrow() + // Setup contract + suite.chainA.StoreContractCode(&suite.Suite) + quotas := suite.BuildChannelQuota("weekly", sdk.DefaultBondDenom, 604800, 1, 1) + addr := suite.chainA.InstantiateContract(&suite.Suite, quotas) + suite.chainA.RegisterRateLimitingContract(addr) + + // Get the escrowed amount + osmosisApp := suite.chainA.GetOsmosisApp() + escrowAddress := transfertypes.GetEscrowAddress("transfer", "channel-0") + escrowed := osmosisApp.BankKeeper.GetBalance(suite.chainA.GetContext(), escrowAddress, sdk.DefaultBondDenom) + + quota := escrowed.Amount.QuoRaw(100) // 1% of the escrowed amount + + // Use the whole quota + coins := sdk.NewCoin(sdk.DefaultBondDenom, quota) + port := suite.path.EndpointA.ChannelConfig.PortID + channel := suite.path.EndpointA.ChannelID + accountFrom := suite.chainA.SenderAccount.GetAddress().String() + timeoutHeight := clienttypes.NewHeight(0, 100) + msg := transfertypes.NewMsgTransfer(port, channel, coins, accountFrom, "INVALID", timeoutHeight, 0) + + // Sending the message manually because AssertSend updates both clients. We need to update the clients manually + // for this test so that the failure to receive on chain B happens after the second packet is sent from chain A. + // That way we validate that chain A is blocking as expected, but the flow is reverted after the receive failure is + // acknowledged on chain A + res, err := suite.chainA.SendMsgsNoCheck(msg) + suite.Require().NoError(err) + + // Sending again fails as the quota is filled + suite.AssertSend(false, suite.MessageFromAToB(sdk.DefaultBondDenom, quota)) + + // Move forward one block + suite.chainA.NextBlock() + suite.chainA.SenderAccount.SetSequence(suite.chainA.SenderAccount.GetSequence() + 1) + suite.chainA.Coordinator.IncrementTime() + + // Update both clients + err = suite.path.EndpointA.UpdateClient() + suite.Require().NoError(err) + err = suite.path.EndpointB.UpdateClient() + suite.Require().NoError(err) + + // Execute the acknowledgement from chain B in chain A + + // extract the sent packet + packet, err := ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + + // recv in chain b + res, err = suite.path.EndpointB.RecvPacketWithResult(packet) + + // get the ack from the chain b's response + ack, err := ibctesting.ParseAckFromEvents(res.GetEvents()) + suite.Require().NoError(err) + + // manually relay it to chain a + err = suite.path.EndpointA.AcknowledgePacket(packet, ack) + suite.Require().NoError(err) + + // We should be able to send again because the packet that exceeded the quota failed and has been reverted + suite.AssertSend(true, suite.MessageFromAToB(sdk.DefaultBondDenom, sdk.NewInt(1))) +} diff --git a/x/ibc-rate-limit/ibc_module.go b/x/ibc-rate-limit/ibc_module.go index c1df7c921..433826ddd 100644 --- a/x/ibc-rate-limit/ibc_module.go +++ b/x/ibc-rate-limit/ibc_module.go @@ -103,12 +103,27 @@ func (im *IBCModule) OnChanCloseConfirm( return im.app.OnChanCloseConfirm(ctx, portID, channelID) } +func ValidateReceiverAddress(packet channeltypes.Packet) error { + var packetData transfertypes.FungibleTokenPacketData + if err := json.Unmarshal(packet.GetData(), &packetData); err != nil { + return err + } + if len(packetData.Receiver) >= 4096 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "IBC Receiver address too long. Max supported length is %d", 4096) + } + return nil +} + // OnRecvPacket implements the IBCModule interface func (im *IBCModule) OnRecvPacket( ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress, ) exported.Acknowledgement { + if err := ValidateReceiverAddress(packet); err != nil { + return channeltypes.NewErrorAcknowledgement(err.Error()) + } + contract := im.ics4Middleware.GetParams(ctx) if contract == "" { // The contract has not been configured. Continue as usual @@ -116,9 +131,10 @@ func (im *IBCModule) OnRecvPacket( } amount, denom, err := GetFundsFromPacket(packet) if err != nil { - return channeltypes.NewErrorAcknowledgement("bad packet") + return channeltypes.NewErrorAcknowledgement("bad packet in rate limit's OnRecvPacket") } - channelValue := im.ics4Middleware.CalculateChannelValue(ctx, denom) + + channelValue := im.ics4Middleware.CalculateChannelValue(ctx, denom, packet) err = CheckAndUpdateRateLimits( ctx, @@ -127,11 +143,11 @@ func (im *IBCModule) OnRecvPacket( contract, channelValue, packet.GetDestChannel(), - denom, + denom, // We always use the packet's denom here, as we want the limits to be the same on both directions amount, ) if err != nil { - return channeltypes.NewErrorAcknowledgement(types.RateLimitExceededMsg) + return channeltypes.NewErrorAcknowledgement(types.ErrRateLimitExceeded.Error()) } // if this returns an Acknowledgement that isn't successful, all state changes are discarded diff --git a/x/ibc-rate-limit/ics4_wrapper.go b/x/ibc-rate-limit/ics4_wrapper.go index bdf7e935a..453de40a4 100644 --- a/x/ibc-rate-limit/ics4_wrapper.go +++ b/x/ibc-rate-limit/ics4_wrapper.go @@ -53,9 +53,11 @@ func (i *ICS4Wrapper) SendPacket(ctx sdk.Context, chanCap *capabilitytypes.Capab amount, denom, err := GetFundsFromPacket(packet) if err != nil { - return sdkerrors.Wrap(err, "Rate limited SendPacket") + return sdkerrors.Wrap(err, "Rate limit SendPacket") } - channelValue := i.CalculateChannelValue(ctx, denom) + + channelValue := i.CalculateChannelValue(ctx, denom, packet) + err = CheckAndUpdateRateLimits( ctx, i.ContractKeeper, @@ -63,11 +65,11 @@ func (i *ICS4Wrapper) SendPacket(ctx sdk.Context, chanCap *capabilitytypes.Capab contract, channelValue, packet.GetSourceChannel(), - denom, + denom, // We always use the packet's denom here, as we want the limits to be the same on both directions amount, ) if err != nil { - return sdkerrors.Wrap(err, "Rate limited SendPacket") + return sdkerrors.Wrap(err, "bad packet in rate limit's SendPacket") } return i.channel.SendPacket(ctx, chanCap, packet) @@ -84,6 +86,7 @@ func (i *ICS4Wrapper) GetParams(ctx sdk.Context) (contract string) { // CalculateChannelValue The value of an IBC channel. This is calculated using the denom supplied by the sender. // if the denom is not correct, the transfer should fail somewhere else on the call chain -func (i *ICS4Wrapper) CalculateChannelValue(ctx sdk.Context, denom string) sdk.Int { - return i.bankKeeper.GetSupplyWithOffset(ctx, denom).Amount +func (i *ICS4Wrapper) CalculateChannelValue(ctx sdk.Context, denom string, packet exported.PacketI) sdk.Int { + // The logic is etracted into a function here so that it can be used within the tests + return CalculateChannelValue(ctx, denom, packet.GetSourcePort(), packet.GetSourceChannel(), i.bankKeeper) } diff --git a/x/ibc-rate-limit/rate_limit.go b/x/ibc-rate-limit/rate_limit.go index 665f04b29..5c91e5ffe 100644 --- a/x/ibc-rate-limit/rate_limit.go +++ b/x/ibc-rate-limit/rate_limit.go @@ -2,10 +2,13 @@ package ibc_rate_limit import ( "encoding/json" + "strings" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/osmosis-labs/osmosis/v12/x/ibc-rate-limit/types" ) @@ -15,11 +18,6 @@ var ( msgRecv = "recv_packet" ) -type PacketData struct { - Denom string `json:"denom"` - Amount string `json:"amount"` -} - func CheckAndUpdateRateLimits(ctx sdk.Context, contractKeeper *wasmkeeper.PermissionedKeeper, msgType, contract string, channelValue sdk.Int, sourceChannel, denom string, @@ -42,6 +40,7 @@ func CheckAndUpdateRateLimits(ctx sdk.Context, contractKeeper *wasmkeeper.Permis } _, err = contractKeeper.Sudo(ctx, contractAddr, sendPacketMsg) + if err != nil { return sdkerrors.Wrap(types.ErrRateLimitExceeded, err.Error()) } @@ -128,10 +127,41 @@ func BuildWasmExecMsg(msgType, sourceChannel, denom string, channelValue sdk.Int } func GetFundsFromPacket(packet exported.PacketI) (string, string, error) { - var packetData PacketData + var packetData transfertypes.FungibleTokenPacketData err := json.Unmarshal(packet.GetData(), &packetData) if err != nil { return "", "", err } - return packetData.Amount, packetData.Denom, nil + return packetData.Amount, GetLocalDenom(packetData.Denom), nil +} + +func GetLocalDenom(denom string) string { + // Expected denoms in the following cases: + // + // send non-native: transfer/channel-0/denom -> ibc/xxx + // send native: denom -> denom + // recv (B)non-native: denom + // recv (B)native: transfer/channel-0/denom + // + if strings.HasPrefix(denom, "transfer/") { + denomTrace := transfertypes.ParseDenomTrace(denom) + return denomTrace.IBCDenom() + } else { + return denom + } +} + +func CalculateChannelValue(ctx sdk.Context, denom string, port, channel string, bankKeeper bankkeeper.Keeper) sdk.Int { + if strings.HasPrefix(denom, "ibc/") { + return bankKeeper.GetSupplyWithOffset(ctx, denom).Amount + } + + if channel == "any" { + // ToDo: Get all channels and sum the escrow addr value over all the channels + escrowAddress := transfertypes.GetEscrowAddress(port, channel) + return bankKeeper.GetBalance(ctx, escrowAddress, denom).Amount + } else { + escrowAddress := transfertypes.GetEscrowAddress(port, channel) + return bankKeeper.GetBalance(ctx, escrowAddress, denom).Amount + } } diff --git a/x/ibc-rate-limit/testdata/rate_limiter.wasm b/x/ibc-rate-limit/testdata/rate_limiter.wasm index caf63c41459ca8361691cf27ae6c738682946e40..e19651209c4009fd4bd73bae23f49c69424e91d3 100644 GIT binary patch delta 74291 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 N{I=n@4nOqE{{r<#fm;9o delta 70741 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 UUUjuw0~#;H?=JlM;|KcvFIw7UWB>pF diff --git a/x/ibc-rate-limit/testutil/chain.go b/x/ibc-rate-limit/testutil/chain.go new file mode 100644 index 000000000..3ab9c26f0 --- /dev/null +++ b/x/ibc-rate-limit/testutil/chain.go @@ -0,0 +1,96 @@ +package osmosisibctesting + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v3/testing/simapp/helpers" + "github.com/osmosis-labs/osmosis/v12/app" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +type TestChain struct { + *ibctesting.TestChain +} + +// SendMsgsNoCheck overrides ibctesting.TestChain.SendMsgs so that it doesn't check for errors. That should be handled by the caller +func (chain *TestChain) SendMsgsNoCheck(msgs ...sdk.Msg) (*sdk.Result, error) { + // ensure the chain has the latest time + chain.Coordinator.UpdateTimeForChain(chain.TestChain) + + _, r, err := SignAndDeliver( + chain.TxConfig, + chain.App.GetBaseApp(), + chain.GetContext().BlockHeader(), + msgs, + chain.ChainID, + []uint64{chain.SenderAccount.GetAccountNumber()}, + []uint64{chain.SenderAccount.GetSequence()}, + chain.SenderPrivKey, + ) + if err != nil { + return nil, err + } + + // SignAndDeliver calls app.Commit() + chain.NextBlock() + + // increment sequence for successful transaction execution + err = chain.SenderAccount.SetSequence(chain.SenderAccount.GetSequence() + 1) + if err != nil { + return nil, err + } + + chain.Coordinator.IncrementTime() + + return r, nil +} + +// SignAndDeliver signs and delivers a transaction without asserting the results. This overrides the function +// from ibctesting +func SignAndDeliver( + txCfg client.TxConfig, app *baseapp.BaseApp, header tmproto.Header, msgs []sdk.Msg, + chainID string, accNums, accSeqs []uint64, priv ...cryptotypes.PrivKey, +) (sdk.GasInfo, *sdk.Result, error) { + tx, _ := helpers.GenTx( + txCfg, + msgs, + sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, + helpers.DefaultGenTxGas, + chainID, + accNums, + accSeqs, + priv..., + ) + + // Simulate a sending a transaction and committing a block + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + gInfo, res, err := app.Deliver(txCfg.TxEncoder(), tx) + + app.EndBlock(abci.RequestEndBlock{}) + app.Commit() + + return gInfo, res, err +} + +// Move epochs to the future to avoid issues with minting +func (chain *TestChain) MoveEpochsToTheFuture() { + epochsKeeper := chain.GetOsmosisApp().EpochsKeeper + ctx := chain.GetContext() + for _, epoch := range epochsKeeper.AllEpochInfos(ctx) { + epoch.StartTime = ctx.BlockTime().Add(time.Hour * 24 * 30) + epochsKeeper.DeleteEpochInfo(chain.GetContext(), epoch.Identifier) + _ = epochsKeeper.AddEpochInfo(ctx, epoch) + } +} + +// GetOsmosisApp returns the current chain's app as an OsmosisApp +func (chain *TestChain) GetOsmosisApp() *app.OsmosisApp { + v, _ := chain.App.(*app.OsmosisApp) + return v +} diff --git a/x/ibc-rate-limit/testutil/wasm.go b/x/ibc-rate-limit/testutil/wasm.go new file mode 100644 index 000000000..2beabb9c0 --- /dev/null +++ b/x/ibc-rate-limit/testutil/wasm.go @@ -0,0 +1,70 @@ +package osmosisibctesting + +import ( + "fmt" + "io/ioutil" + + "github.com/stretchr/testify/require" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/osmosis-labs/osmosis/v12/x/ibc-rate-limit/types" + "github.com/stretchr/testify/suite" +) + +func (chain *TestChain) StoreContractCode(suite *suite.Suite) { + osmosisApp := chain.GetOsmosisApp() + + govKeeper := osmosisApp.GovKeeper + wasmCode, err := ioutil.ReadFile("./testdata/rate_limiter.wasm") + suite.Require().NoError(err) + + addr := osmosisApp.AccountKeeper.GetModuleAddress(govtypes.ModuleName) + src := wasmtypes.StoreCodeProposalFixture(func(p *wasmtypes.StoreCodeProposal) { + p.RunAs = addr.String() + p.WASMByteCode = wasmCode + }) + + // when stored + storedProposal, err := govKeeper.SubmitProposal(chain.GetContext(), src, false) + suite.Require().NoError(err) + + // and proposal execute + handler := govKeeper.Router().GetRoute(storedProposal.ProposalRoute()) + err = handler(chain.GetContext(), storedProposal.GetContent()) + suite.Require().NoError(err) +} + +func (chain *TestChain) InstantiateContract(suite *suite.Suite, quotas string) sdk.AccAddress { + osmosisApp := chain.GetOsmosisApp() + transferModule := osmosisApp.AccountKeeper.GetModuleAddress(transfertypes.ModuleName) + govModule := osmosisApp.AccountKeeper.GetModuleAddress(govtypes.ModuleName) + + initMsgBz := []byte(fmt.Sprintf(`{ + "gov_module": "%s", + "ibc_module":"%s", + "paths": [%s] + }`, + govModule, transferModule, quotas)) + + contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) + codeID := uint64(1) + creator := osmosisApp.AccountKeeper.GetModuleAddress(govtypes.ModuleName) + addr, _, err := contractKeeper.Instantiate(chain.GetContext(), codeID, creator, creator, initMsgBz, "rate limiting contract", nil) + suite.Require().NoError(err) + return addr +} + +func (chain *TestChain) RegisterRateLimitingContract(addr []byte) { + addrStr, err := sdk.Bech32ifyAddressBytes("osmo", addr) + require.NoError(chain.T, err) + params, err := types.NewParams(addrStr) + require.NoError(chain.T, err) + osmosisApp := chain.GetOsmosisApp() + paramSpace, ok := osmosisApp.AppKeepers.ParamsKeeper.GetSubspace(types.ModuleName) + require.True(chain.T, ok) + paramSpace.SetParamSet(chain.GetContext(), ¶ms) +} diff --git a/x/ibc-rate-limit/types/errors.go b/x/ibc-rate-limit/types/errors.go index 67d81abeb..5394ce11e 100644 --- a/x/ibc-rate-limit/types/errors.go +++ b/x/ibc-rate-limit/types/errors.go @@ -5,8 +5,7 @@ import ( ) var ( - RateLimitExceededMsg = "rate limit exceeded" - ErrRateLimitExceeded = sdkerrors.Register(ModuleName, 2, RateLimitExceededMsg) + ErrRateLimitExceeded = sdkerrors.Register(ModuleName, 2, "rate limit exceeded") ErrBadMessage = sdkerrors.Register(ModuleName, 3, "bad message") ErrContractError = sdkerrors.Register(ModuleName, 4, "contract error") ) -- GitLab