diff --git a/Makefile b/Makefile index 682f8489195..099b4bd2440 100644 --- a/Makefile +++ b/Makefile @@ -310,6 +310,10 @@ docker-build-debug: @DOCKER_BUILDKIT=1 docker build -t osmosis:${COMMIT} --build-arg BASE_IMG_TAG=debug -f Dockerfile . @DOCKER_BUILDKIT=1 docker tag osmosis:${COMMIT} osmosis:debug +docker-build-debug-alpine: + @DOCKER_BUILDKIT=1 docker build -t osmosis:${COMMIT} --build-arg BASE_IMG_TAG=debug --build-arg RUNNER_IMAGE=$(RUNNER_BASE_IMAGE_ALPINE) -f Dockerfile . + @DOCKER_BUILDKIT=1 docker tag osmosis:${COMMIT} osmosis:debug + docker-build-e2e-init-chain: @DOCKER_BUILDKIT=1 docker build -t osmosis-e2e-init-chain:debug --build-arg E2E_SCRIPT_NAME=chain --platform=linux/x86_64 -f tests/e2e/initialization/init.Dockerfile . diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 5d5be979e03..40bc3b90838 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -37,6 +37,8 @@ import ( downtimedetector "github.com/osmosis-labs/osmosis/v13/x/downtime-detector" downtimetypes "github.com/osmosis-labs/osmosis/v13/x/downtime-detector/types" ibchooks "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks" + ibchookskeeper "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/keeper" + ibchookstypes "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types" ibcratelimit "github.com/osmosis-labs/osmosis/v13/x/ibc-rate-limit" ibcratelimittypes "github.com/osmosis-labs/osmosis/v13/x/ibc-rate-limit/types" "github.com/osmosis-labs/osmosis/v13/x/swaprouter" @@ -110,6 +112,7 @@ type AppKeepers struct { DowntimeKeeper *downtimedetector.Keeper SlashingKeeper *slashingkeeper.Keeper IBCKeeper *ibckeeper.Keeper + IBCHooksKeeper *ibchookskeeper.Keeper ICAHostKeeper *icahostkeeper.Keeper TransferKeeper *ibctransferkeeper.Keeper EvidenceKeeper *evidencekeeper.Keeper @@ -222,7 +225,13 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.ScopedIBCKeeper, ) - appKeepers.WireICS20PreWasmKeeper(appCodec, bApp) + // Configure the hooks keeper + hooksKeeper := ibchookskeeper.NewKeeper( + appKeepers.keys[ibchookstypes.StoreKey], + ) + appKeepers.IBCHooksKeeper = &hooksKeeper + + appKeepers.WireICS20PreWasmKeeper(appCodec, bApp, appKeepers.IBCHooksKeeper) icaHostKeeper := icahostkeeper.NewKeeper( appCodec, appKeepers.keys[icahosttypes.StoreKey], @@ -428,9 +437,11 @@ func (appKeepers *AppKeepers) InitNormalKeepers( // appkeepers.WasmHooks AND appKeepers.RateLimitingICS4Wrapper func (appKeepers *AppKeepers) WireICS20PreWasmKeeper( appCodec codec.Codec, - bApp *baseapp.BaseApp) { + bApp *baseapp.BaseApp, + hooksKeeper *ibchookskeeper.Keeper, +) { // Setup the ICS4Wrapper used by the hooks middleware - wasmHooks := ibchooks.NewWasmHooks(nil) // The contract keeper needs to be set later + wasmHooks := ibchooks.NewWasmHooks(hooksKeeper, nil) // The contract keeper needs to be set later appKeepers.Ics20WasmHooks = &wasmHooks appKeepers.HooksICS4Wrapper = ibchooks.NewICS4Middleware( appKeepers.IBCKeeper.ChannelKeeper, @@ -640,5 +651,6 @@ func KVStoreKeys() []string { tokenfactorytypes.StoreKey, valsetpreftypes.StoreKey, protorevtypes.StoreKey, + ibchookstypes.StoreKey, } } diff --git a/app/modules.go b/app/modules.go index 92cd0702c8d..a92abc791e6 100644 --- a/app/modules.go +++ b/app/modules.go @@ -9,6 +9,7 @@ import ( ibc "github.com/cosmos/ibc-go/v3/modules/core" ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" + ibchookstypes "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types" ica "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts" icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" @@ -85,7 +86,7 @@ import ( var moduleAccountPermissions = map[string][]string{ authtypes.FeeCollectorName: nil, distrtypes.ModuleName: nil, - ibc_hooks.ModuleName: nil, + ibchookstypes.ModuleName: nil, icatypes.ModuleName: nil, minttypes.ModuleName: {authtypes.Minter, authtypes.Burner}, minttypes.DeveloperVestingModuleAcctName: nil, @@ -242,7 +243,7 @@ func OrderInitGenesis(allModuleNames []string) []string { // wasm after ibc transfer wasm.ModuleName, // ibc_hooks after auth keeper - ibc_hooks.ModuleName, + ibchookstypes.ModuleName, } } diff --git a/app/upgrades/v13/upgrade_test.go b/app/upgrades/v13/upgrade_test.go index 137566eb6e7..9570c959afe 100644 --- a/app/upgrades/v13/upgrade_test.go +++ b/app/upgrades/v13/upgrade_test.go @@ -2,6 +2,7 @@ package v13_test import ( "fmt" + ibchookstypes "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types" "testing" ibcratelimittypes "github.com/osmosis-labs/osmosis/v13/x/ibc-rate-limit/types" @@ -58,14 +59,14 @@ func (suite *UpgradeTestSuite) TestUpgrade() { suite.App.AccountKeeper.RemoveAccount(suite.Ctx, acc) // Because of SDK version map bug, we can't do the following, and instaed do a massive hack // vm := suite.App.UpgradeKeeper.GetModuleVersionMap(suite.Ctx) - // delete(vm, ibc_hooks.ModuleName) + // delete(vm, ibchookstypes.ModuleName) // OR - // vm[ibc_hooks.ModuleName] = 0 + // vm[ibchookstypes.ModuleName] = 0 // suite.App.UpgradeKeeper.SetModuleVersionMap(suite.Ctx, vm) upgradeStoreKey := suite.App.AppKeepers.GetKey(upgradetypes.StoreKey) store := suite.Ctx.KVStore(upgradeStoreKey) versionStore := prefix.NewStore(store, []byte{upgradetypes.VersionMapByte}) - versionStore.Delete([]byte(ibc_hooks.ModuleName)) + versionStore.Delete([]byte(ibchookstypes.ModuleName)) hasAcc := suite.App.AccountKeeper.HasAccount(suite.Ctx, ibc_hooks.WasmHookModuleAccountAddr) suite.Require().False(hasAcc) diff --git a/app/upgrades/v14/constants.go b/app/upgrades/v14/constants.go index ef09b17b2e9..b803896348b 100644 --- a/app/upgrades/v14/constants.go +++ b/app/upgrades/v14/constants.go @@ -2,6 +2,7 @@ package v14 import ( store "github.com/cosmos/cosmos-sdk/store/types" + ibchookstypes "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types" "github.com/osmosis-labs/osmosis/v13/app/upgrades" downtimetypes "github.com/osmosis-labs/osmosis/v13/x/downtime-detector/types" @@ -17,7 +18,7 @@ var Upgrade = upgrades.Upgrade{ UpgradeName: UpgradeName, CreateUpgradeHandler: CreateUpgradeHandler, StoreUpgrades: store.StoreUpgrades{ - Added: []string{valsetpreftypes.StoreKey, protorevtypes.StoreKey, swaproutertypes.StoreKey, downtimetypes.StoreKey}, + Added: []string{valsetpreftypes.StoreKey, protorevtypes.StoreKey, swaproutertypes.StoreKey, downtimetypes.StoreKey, ibchookstypes.StoreKey}, Deleted: []string{}, }, } diff --git a/x/ibc-hooks/README.md b/x/ibc-hooks/README.md index 3434f20afbb..fb4cd21ebb1 100644 --- a/x/ibc-hooks/README.md +++ b/x/ibc-hooks/README.md @@ -114,6 +114,42 @@ In wasm hooks, post packet execution: * if wasm message has error, return ErrAck * otherwise continue through middleware +## Ack callbacks +A contract that sends an IBC transfer, may need to listen for the ACK from that packet. To allow +contracts to listen on the ack of specific packets, we provide Ack callbacks. -### Testing strategy \ No newline at end of file +### Design + +The sender of an IBC transfer packet may specify a callback for when the ack of that packet is received in the memo +field of the transfer packet. + +Crucially, _only_ the IBC packet sender can set the callback. + +### Use case + +The crosschain swaps implementation sends an IBC transfer. If the transfer were to fail, we want to allow the sender +to be able to retrieve their funds (which would otherwise be stuck in the contract). To do this, we allow users to +retrieve the funds after the timeout has passed, but without the ack information, we cannot guarantee that the send +hasn't failed (i.e.: returned an error ack notifying that the receiving change didn't accept it) + +### Implementation + +#### Callback information in memo + +For the callback to be processed, the transfer packet's memo should contain the following in its JSON: + +`{"ibc_callback": "osmo1contractAddr"}` + +The wasm hooks will keep the mapping from the packet's channel and sequence to the contract in storage. When an ack is +received, it will notify the specified contract via a sudo message. + +#### Interface for receiving the Ack + +The contract that awaits the callback should implement the following interface for a sudo message: + +* `ReceiveAck { channel: String, sequence: u64, ack: String, success: bool }` + +# Testing strategy + +See go tests. \ No newline at end of file diff --git a/x/ibc-hooks/bytecode/counter.wasm b/x/ibc-hooks/bytecode/counter.wasm index 55f18df6dd3..af14177e329 100644 Binary files a/x/ibc-hooks/bytecode/counter.wasm and b/x/ibc-hooks/bytecode/counter.wasm differ diff --git a/x/ibc-hooks/bytecode/crosschain_swaps.wasm b/x/ibc-hooks/bytecode/crosschain_swaps.wasm new file mode 100644 index 00000000000..600d52eb956 Binary files /dev/null and b/x/ibc-hooks/bytecode/crosschain_swaps.wasm differ diff --git a/x/ibc-hooks/bytecode/swaprouter.wasm b/x/ibc-hooks/bytecode/swaprouter.wasm new file mode 100644 index 00000000000..684df978d16 Binary files /dev/null and b/x/ibc-hooks/bytecode/swaprouter.wasm differ diff --git a/x/ibc-hooks/genesis.go b/x/ibc-hooks/genesis.go index 57d26b37477..9ed1cde20e3 100644 --- a/x/ibc-hooks/genesis.go +++ b/x/ibc-hooks/genesis.go @@ -4,10 +4,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" + "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types" + "github.com/osmosis-labs/osmosis/v13/osmoutils" ) -var WasmHookModuleAccountAddr sdk.AccAddress = address.Module(ModuleName, []byte("wasm-hook intermediary account")) +var WasmHookModuleAccountAddr sdk.AccAddress = address.Module(types.ModuleName, []byte("wasm-hook intermediary account")) func IbcHooksInitGenesis(ctx sdk.Context, ak osmoutils.AccountKeeper) { err := osmoutils.CreateModuleAccount(ctx, ak, WasmHookModuleAccountAddr) diff --git a/x/ibc-hooks/ibc_middleware_test.go b/x/ibc-hooks/ibc_middleware_test.go index 2a0f175b4ec..8653d9537e3 100644 --- a/x/ibc-hooks/ibc_middleware_test.go +++ b/x/ibc-hooks/ibc_middleware_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - ibc_hooks "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks" + ibchooks "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks" "github.com/osmosis-labs/osmosis/v13/osmoutils" @@ -201,7 +201,7 @@ func (suite *HooksTestSuite) receivePacketWithSequence(receiver, memo string, pr func (suite *HooksTestSuite) TestRecvTransferWithMetadata() { // Setup contract suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/echo.wasm") - addr := suite.chainA.InstantiateContract(&suite.Suite, "{}") + addr := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1) ackBytes := suite.receivePacket(addr.String(), fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": {"echo": {"msg": "test"} } } }`, addr)) ackStr := string(ackBytes) @@ -217,7 +217,7 @@ func (suite *HooksTestSuite) TestRecvTransferWithMetadata() { func (suite *HooksTestSuite) TestFundsAreTransferredToTheContract() { // Setup contract suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/echo.wasm") - addr := suite.chainA.InstantiateContract(&suite.Suite, "{}") + addr := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1) // Check that the contract has no funds localDenom := osmoutils.MustExtractDenomFromPacketOnRecv(suite.makeMockPacket("", "", 0)) @@ -243,7 +243,7 @@ func (suite *HooksTestSuite) TestFundsAreTransferredToTheContract() { func (suite *HooksTestSuite) TestFundsAreReturnedOnFailedContractExec() { // Setup contract suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/echo.wasm") - addr := suite.chainA.InstantiateContract(&suite.Suite, "{}") + addr := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1) // Check that the contract has no funds localDenom := osmoutils.MustExtractDenomFromPacketOnRecv(suite.makeMockPacket("", "", 0)) @@ -311,7 +311,7 @@ func (suite *HooksTestSuite) TestPacketsThatShouldBeSkipped() { func (suite *HooksTestSuite) TestFundTracking() { // Setup contract suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/counter.wasm") - addr := suite.chainA.InstantiateContract(&suite.Suite, `{"count": 0}`) + addr := suite.chainA.InstantiateContract(&suite.Suite, `{"count": 0}`, 1) // Check that the contract has no funds localDenom := osmoutils.MustExtractDenomFromPacketOnRecv(suite.makeMockPacket("", "", 0)) @@ -325,13 +325,13 @@ func (suite *HooksTestSuite) TestFundTracking() { state := suite.chainA.QueryContract( &suite.Suite, addr, - []byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, ibc_hooks.WasmHookModuleAccountAddr))) + []byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, ibchooks.WasmHookModuleAccountAddr))) suite.Require().Equal(`{"count":0}`, state) state = suite.chainA.QueryContract( &suite.Suite, addr, - []byte(fmt.Sprintf(`{"get_total_funds": {"addr": "%s"}}`, ibc_hooks.WasmHookModuleAccountAddr))) - suite.Require().Equal(`{"total_funds":[]}`, state) + []byte(fmt.Sprintf(`{"get_total_funds": {"addr": "%s"}}`, ibchooks.WasmHookModuleAccountAddr))) + suite.Require().Equal(`{"total_funds":[{"denom":"ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878","amount":"1"}]}`, state) suite.receivePacketWithSequence( addr.String(), @@ -339,15 +339,126 @@ func (suite *HooksTestSuite) TestFundTracking() { state = suite.chainA.QueryContract( &suite.Suite, addr, - []byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, ibc_hooks.WasmHookModuleAccountAddr))) + []byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, ibchooks.WasmHookModuleAccountAddr))) suite.Require().Equal(`{"count":1}`, state) state = suite.chainA.QueryContract( &suite.Suite, addr, - []byte(fmt.Sprintf(`{"get_total_funds": {"addr": "%s"}}`, ibc_hooks.WasmHookModuleAccountAddr))) - suite.Require().Equal(`{"total_funds":[{"denom":"ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878","amount":"1"}]}`, state) + []byte(fmt.Sprintf(`{"get_total_funds": {"addr": "%s"}}`, ibchooks.WasmHookModuleAccountAddr))) + suite.Require().Equal(`{"total_funds":[{"denom":"ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878","amount":"2"}]}`, state) // Check that the token has now been transferred to the contract balance = suite.chainA.GetOsmosisApp().BankKeeper.GetBalance(suite.chainA.GetContext(), addr, localDenom) suite.Require().Equal(sdk.NewInt(2), balance.Amount) } + +// custom MsgTransfer constructor that supports Memo +func NewMsgTransfer( + token sdk.Coin, sender, receiver string, memo string, +) *transfertypes.MsgTransfer { + return &transfertypes.MsgTransfer{ + SourcePort: "transfer", + SourceChannel: "channel-0", + Token: token, + Sender: sender, + Receiver: receiver, + TimeoutHeight: clienttypes.NewHeight(0, 100), + TimeoutTimestamp: 0, + Memo: memo, + } +} + +type Direction int64 + +const ( + AtoB Direction = iota + BtoA +) + +func (suite *HooksTestSuite) GetEndpoints(direction Direction) (sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint) { + switch direction { + case AtoB: + sender = suite.path.EndpointA + receiver = suite.path.EndpointB + case BtoA: + sender = suite.path.EndpointB + receiver = suite.path.EndpointA + } + return sender, receiver +} + +func (suite *HooksTestSuite) RelayPacket(packet channeltypes.Packet, direction Direction) (*sdk.Result, []byte) { + sender, receiver := suite.GetEndpoints(direction) + + err := receiver.UpdateClient() + suite.Require().NoError(err) + + // receiver Receives + receiveResult, err := receiver.RecvPacketWithResult(packet) + suite.Require().NoError(err) + + ack, err := ibctesting.ParseAckFromEvents(receiveResult.GetEvents()) + suite.Require().NoError(err) + + // sender Acknowledges + err = sender.AcknowledgePacket(packet, ack) + suite.Require().NoError(err) + + err = sender.UpdateClient() + suite.Require().NoError(err) + err = receiver.UpdateClient() + suite.Require().NoError(err) + + return receiveResult, ack +} + +func (suite *HooksTestSuite) FullSend(msg sdk.Msg, direction Direction) (*sdk.Result, *sdk.Result, string, error) { + var sender *osmosisibctesting.TestChain + switch direction { + case AtoB: + sender = suite.chainA + case BtoA: + sender = suite.chainB + } + sendResult, err := sender.SendMsgsNoCheck(msg) + suite.Require().NoError(err) + + packet, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents()) + suite.Require().NoError(err) + + receiveResult, ack := suite.RelayPacket(packet, direction) + + return sendResult, receiveResult, string(ack), err +} + +func (suite *HooksTestSuite) TestAcks() { + suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/counter.wasm") + addr := suite.chainA.InstantiateContract(&suite.Suite, `{"count": 0}`, 1) + + // Generate swap instructions for the contract + callbackMemo := fmt.Sprintf(`{"ibc_callback":"%s"}`, addr) + // Send IBC transfer with the memo with crosschain-swap instructions + transferMsg := NewMsgTransfer(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)), suite.chainA.SenderAccount.GetAddress().String(), addr.String(), callbackMemo) + suite.FullSend(transferMsg, AtoB) + + // The test contract will increment the counter for itself every time it receives an ack + state := suite.chainA.QueryContract( + &suite.Suite, addr, + []byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, addr))) + suite.Require().Equal(`{"count":1}`, state) + + suite.FullSend(transferMsg, AtoB) + state = suite.chainA.QueryContract( + &suite.Suite, addr, + []byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, addr))) + suite.Require().Equal(`{"count":2}`, state) + +} + +func (suite *HooksTestSuite) TestSendWithoutMemo() { + // Sending a packet without memo to ensure that the ibc_callback middleware doesn't interfere with a regular send + transferMsg := NewMsgTransfer(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), "") + _, _, ack, err := suite.FullSend(transferMsg, AtoB) + suite.Require().NoError(err) + suite.Require().Contains(ack, "result") +} diff --git a/x/ibc-hooks/keeper/keeper.go b/x/ibc-hooks/keeper/keeper.go new file mode 100644 index 00000000000..22e6a1d7c0a --- /dev/null +++ b/x/ibc-hooks/keeper/keeper.go @@ -0,0 +1,53 @@ +package keeper + +import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type ( + Keeper struct { + storeKey sdk.StoreKey + } +) + +// NewKeeper returns a new instance of the x/ibchooks keeper +func NewKeeper( + storeKey sdk.StoreKey, +) Keeper { + return Keeper{ + storeKey: storeKey, + } +} + +// Logger returns a logger for the x/tokenfactory module +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +func GetPacketKey(channel string, packetSequence uint64) []byte { + return []byte(fmt.Sprintf("%s::%d", channel, packetSequence)) +} + +// StorePacketCallback stores which contract will be listening for the ack or timeout of a packet +func (k Keeper) StorePacketCallback(ctx sdk.Context, channel string, packetSequence uint64, contract string) { + store := ctx.KVStore(k.storeKey) + store.Set(GetPacketKey(channel, packetSequence), []byte(contract)) +} + +// GetPacketCallback returns the bech32 addr of the contract that is expecting a callback from a packet +func (k Keeper) GetPacketCallback(ctx sdk.Context, channel string, packetSequence uint64) string { + store := ctx.KVStore(k.storeKey) + return string(store.Get(GetPacketKey(channel, packetSequence))) +} + +// DeletePacketCallback deletes the callback from storage once it has been processed +func (k Keeper) DeletePacketCallback(ctx sdk.Context, channel string, packetSequence uint64) { + store := ctx.KVStore(k.storeKey) + store.Delete(GetPacketKey(channel, packetSequence)) +} diff --git a/x/ibc-hooks/sdkmodule.go b/x/ibc-hooks/sdkmodule.go index d28b18b282f..0b0c08845dc 100644 --- a/x/ibc-hooks/sdkmodule.go +++ b/x/ibc-hooks/sdkmodule.go @@ -11,6 +11,8 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" + "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/osmosis-labs/osmosis/v13/osmoutils" @@ -19,12 +21,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) -type Ibcmodule struct{} - var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - ModuleName = "ibchooks" + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} ) // AppModuleBasic defines the basic application module used by the mint module. @@ -34,7 +33,7 @@ var _ module.AppModuleBasic = AppModuleBasic{} // Name returns the mint module's name. func (AppModuleBasic) Name() string { - return ModuleName + return types.ModuleName } // RegisterLegacyAminoCodec registers the mint module's types on the given LegacyAmino codec. @@ -88,7 +87,7 @@ func NewAppModule(ak osmoutils.AccountKeeper) AppModule { // Name returns the mint module's name. func (AppModule) Name() string { - return ModuleName + return types.ModuleName } // RegisterInvariants registers the mint module invariants. @@ -105,7 +104,7 @@ func (AppModule) QuerierRoute() string { // LegacyQuerierHandler returns the x/mint module's sdk.Querier. func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { return func(sdk.Context, []string, abci.RequestQuery) ([]byte, error) { - return nil, fmt.Errorf("legacy querier not supported for the x/%s module", ModuleName) + return nil, fmt.Errorf("legacy querier not supported for the x/%s module", types.ModuleName) } } diff --git a/x/ibc-hooks/testutils/Cargo.lock b/x/ibc-hooks/testutils/Cargo.lock new file mode 100644 index 00000000000..52e7e6ca1ef --- /dev/null +++ b/x/ibc-hooks/testutils/Cargo.lock @@ -0,0 +1,815 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "anyhow" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" + +[[package]] +name = "cosmwasm-crypto" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28376836c7677e1ea6d6656a754582e88b91e544ce22fae42956d5fe5549a958" +dependencies = [ + "digest 0.10.5", + "ed25519-zebra", + "k256", + "rand_core 0.6.4", + "thiserror", +] + +[[package]] +name = "cosmwasm-derive" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb69f4f7a8a4bce68c8fbd3646238fede1e77056e4ea31c5b6bfc37b709eec3" +dependencies = [ + "syn", +] + +[[package]] +name = "cosmwasm-schema" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a227cfeb9a7152b26a354b1c990e930e962f75fd68f57ab5ae2ef888c8524292" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3626cb42eef870de67f791e873711255325224d86f281bf628c42abd295f3a14" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cosmwasm-std" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bf9157d060abbc55152aeadcace799d03dc630575daa66604079a1206cb060" +dependencies = [ + "base64", + "cosmwasm-crypto", + "cosmwasm-derive", + "derivative", + "forward_ref", + "hex", + "schemars", + "serde", + "serde-json-wasm", + "thiserror", + "uint", +] + +[[package]] +name = "cosmwasm-storage" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b61fcfef87d15af0263e2e4d792af80355929674a3b4e29ffb3c898ec6e25852" +dependencies = [ + "cosmwasm-std", + "serde", +] + +[[package]] +name = "counter" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-multi-test", + "cw-storage-plus", + "cw2", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cw-multi-test" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7192aec80d0c01a0e5941392eea7e2b7e212ee74ca7f430bfdc899420c055ef6" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "derivative", + "itertools", + "prost", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-storage-plus" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b6f91c0b94481a3e9ef1ceb183c37d00764f8751e39b45fc09f4d9b970d469" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "cw-utils" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a84c6c1c0acc3616398eba50783934bd6c964bad6974241eaee3460c8f5b26" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw2" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "schemars", + "serde", +] + +[[package]] +name = "der" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "echo" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-multi-test", + "cw-storage-plus", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek", + "hashbrown", + "hex", + "rand_core 0.6.4", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.5", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.5", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "schemars" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" + +[[package]] +name = "serde" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-json-wasm" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.5", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.5", + "rand_core 0.6.4", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "uint" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45526d29728d135c2900b0d30573fe3ee79fceb12ef534c7bb30e810a91b601" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/x/ibc-hooks/testutils/contracts/counter/src/contract.rs b/x/ibc-hooks/testutils/contracts/counter/src/contract.rs index be67b59302b..db9d9e0e723 100644 --- a/x/ibc-hooks/testutils/contracts/counter/src/contract.rs +++ b/x/ibc-hooks/testutils/contracts/counter/src/contract.rs @@ -36,6 +36,40 @@ pub fn instantiate( .add_attribute("count", msg.count.to_string())) } +pub mod utils { + use cosmwasm_std::Addr; + + use super::*; + + pub fn update_counter( + deps: DepsMut, + sender: Addr, + update_counter: &dyn Fn(&Option) -> i32, + update_funds: &dyn Fn(&Option) -> Vec, + ) -> Result { + COUNTERS + .update( + deps.storage, + sender.clone(), + |state| -> Result<_, ContractError> { + match state { + None => Ok(Counter { + count: update_counter(&None), + total_funds: update_funds(&None), + owner: sender, + }), + Some(counter) => Ok(Counter { + count: update_counter(&Some(counter.clone())), + total_funds: update_funds(&Some(counter)), + owner: sender, + }), + } + }, + ) + .map(|_r| true) + } +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, @@ -53,47 +87,59 @@ pub mod execute { use super::*; pub fn increment(deps: DepsMut, info: MessageInfo) -> Result { - COUNTERS.update( - deps.storage, - info.sender.clone(), - |state| -> Result<_, ContractError> { - match state { - None => Ok(Counter { - count: 0, - total_funds: vec![], - owner: info.sender.clone(), - }), - Some(counter) => Ok(Counter { - count: counter.count + 1, - total_funds: naive_add_coins(&info.funds, &counter.total_funds), - owner: info.sender.clone(), - }), - } + utils::update_counter( + deps, + info.sender, + &|counter| match counter { + None => 0, + Some(counter) => counter.count + 1, + }, + &|counter| match counter { + None => info.funds.clone(), + Some(counter) => naive_add_coins(&info.funds, &counter.total_funds), }, )?; - Ok(Response::new().add_attribute("action", "increment")) } pub fn reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result { - COUNTERS.update( - deps.storage, - info.sender.clone(), - |state| -> Result<_, ContractError> { - match state { - None => Err(ContractError::Unauthorized {}), - Some(state) if state.owner != info.sender.clone() => { - Err(ContractError::Unauthorized {}) - } - _ => Ok(Counter { - count, - total_funds: vec![], - owner: info.sender.clone(), - }), - } + utils::update_counter(deps, info.sender, &|_counter| count, &|_counter| vec![])?; + Ok(Response::new().add_attribute("action", "reset")) + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> Result { + match msg { + SudoMsg::ReceiveAck { + channel: _, + sequence: _, + ack: _, + success, + } => sudo::receive_ack(deps, env.contract.address, success), + } +} + +pub mod sudo { + use cosmwasm_std::Addr; + + use super::*; + + pub fn receive_ack( + deps: DepsMut, + contract: Addr, + _success: bool, + ) -> Result { + utils::update_counter( + deps, + contract, + &|counter| match counter { + None => 1, + Some(counter) => counter.count + 1, }, + &|_counter| vec![], )?; - Ok(Response::new().add_attribute("action", "reset")) + Ok(Response::new().add_attribute("action", "ack")) } } @@ -106,7 +152,6 @@ pub fn naive_add_coins(lhs: &Vec, rhs: &Vec) -> Vec { coins.insert(coin.denom.clone(), coin.amount); } - for coin in rhs { coins .entry(coin.denom.clone()) @@ -260,12 +305,20 @@ mod tests { // beneficiary can release it let unauth_info = mock_info("anyone", &coins(2, "token")); - let msg = ExecuteMsg::Reset { count: 5 }; - let res = execute(deps.as_mut(), mock_env(), unauth_info, msg); - match res { - Err(ContractError::Unauthorized {}) => {} - _ => panic!("Must return unauthorized error"), - } + let msg = ExecuteMsg::Reset { count: 7 }; + let _res = execute(deps.as_mut(), mock_env(), unauth_info, msg); + + // should be 7 + let res = query( + deps.as_ref(), + mock_env(), + QueryMsg::GetCount { + addr: Addr::unchecked("anyone"), + }, + ) + .unwrap(); + let value: GetCountResponse = from_binary(&res).unwrap(); + assert_eq!(7, value.count); // only the original creator can reset the counter let auth_info = mock_info("creator", &coins(2, "token")); @@ -284,4 +337,42 @@ mod tests { let value: GetCountResponse = from_binary(&res).unwrap(); assert_eq!(5, value.count); } + + #[test] + fn acks() { + let mut deps = mock_dependencies(); + let env = mock_env(); + let get_msg = QueryMsg::GetCount { + addr: Addr::unchecked(env.clone().contract.address), + }; + + // No acks + query(deps.as_ref(), env.clone(), get_msg.clone()).unwrap_err(); + + let msg = SudoMsg::ReceiveAck { + channel: format!("channel-0"), + sequence: 1, + ack: String::new(), + success: true, + }; + let _res = sudo(deps.as_mut(), env.clone(), msg).unwrap(); + + // should increase counter by 1 + let res = query(deps.as_ref(), env.clone(), get_msg.clone()).unwrap(); + let value: GetCountResponse = from_binary(&res).unwrap(); + assert_eq!(1, value.count); + + let msg = SudoMsg::ReceiveAck { + channel: format!("channel-0"), + sequence: 1, + ack: String::new(), + success: true, + }; + let _res = sudo(deps.as_mut(), env.clone(), msg).unwrap(); + + // should increase counter by 1 + let res = query(deps.as_ref(), env, get_msg).unwrap(); + let value: GetCountResponse = from_binary(&res).unwrap(); + assert_eq!(2, value.count); + } } diff --git a/x/ibc-hooks/testutils/contracts/counter/src/msg.rs b/x/ibc-hooks/testutils/contracts/counter/src/msg.rs index 6c29fdf2508..334791b673b 100644 --- a/x/ibc-hooks/testutils/contracts/counter/src/msg.rs +++ b/x/ibc-hooks/testutils/contracts/counter/src/msg.rs @@ -32,3 +32,13 @@ pub struct GetCountResponse { pub struct GetTotalFundsResponse { pub total_funds: Vec, } + +#[cw_serde] +pub enum SudoMsg { + ReceiveAck { + channel: String, + sequence: u64, + ack: String, + success: bool, + }, +} diff --git a/x/ibc-hooks/types/keys.go b/x/ibc-hooks/types/keys.go new file mode 100644 index 00000000000..b28ee341b15 --- /dev/null +++ b/x/ibc-hooks/types/keys.go @@ -0,0 +1,7 @@ +package types + +const ( + ModuleName = "ibchooks" + StoreKey = "hooks-for-ibc" // not using the module name because of collisions with key "ibc" + IBCCallbackKey = "ibc_callback" +) diff --git a/x/ibc-hooks/wasm_hook.go b/x/ibc-hooks/wasm_hook.go index 4eca8bf1bc5..b5cd9ce2877 100644 --- a/x/ibc-hooks/wasm_hook.go +++ b/x/ibc-hooks/wasm_hook.go @@ -4,11 +4,15 @@ import ( "encoding/json" "fmt" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" "github.com/osmosis-labs/osmosis/v13/osmoutils" - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" sdk "github.com/cosmos/cosmos-sdk/types" transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" @@ -24,14 +28,22 @@ type ContractAck struct { type WasmHooks struct { ContractKeeper *wasmkeeper.PermissionedKeeper + ibcHooksKeeper *keeper.Keeper } -func NewWasmHooks(contractKeeper *wasmkeeper.PermissionedKeeper) WasmHooks { - return WasmHooks{ContractKeeper: contractKeeper} +func NewWasmHooks(ibcHooksKeeper *keeper.Keeper, contractKeeper *wasmkeeper.PermissionedKeeper) WasmHooks { + return WasmHooks{ + ContractKeeper: contractKeeper, + ibcHooksKeeper: ibcHooksKeeper, + } +} + +func (h WasmHooks) ProperlyConfigured() bool { + return h.ContractKeeper != nil && h.ibcHooksKeeper != nil } func (h WasmHooks) OnRecvPacketOverride(im IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) ibcexported.Acknowledgement { - if h.ContractKeeper == nil { + if !h.ProperlyConfigured() { // Not configured return im.App.OnRecvPacket(ctx, packet, relayer) } @@ -119,33 +131,34 @@ func isIcs20Packet(packet channeltypes.Packet) (isIcs20 bool, ics20data transfer return true, data } -func isMemoWasmRouted(memo string) (isWasmRouted bool, metadata map[string]interface{}) { - metadata = make(map[string]interface{}) +// jsonStringHasKey parses the memo as a json object and checks if it contains the key. +func jsonStringHasKey(memo, key string) (found bool, jsonObject map[string]interface{}) { + jsonObject = make(map[string]interface{}) // If there is no memo, the packet was either sent with an earlier version of IBC, or the memo was // intentionally left blank. Nothing to do here. Ignore the packet and pass it down the stack. if len(memo) == 0 { - return false, metadata + return false, jsonObject } - // the metadata must be a valid JSON object - err := json.Unmarshal([]byte(memo), &metadata) + // the jsonObject must be a valid JSON object + err := json.Unmarshal([]byte(memo), &jsonObject) if err != nil { - return false, metadata + return false, jsonObject } - // If the key "wasm" doesn't exist, there's nothing to do on this hook. Continue by passing the packet + // If the key doesn't exist, there's nothing to do on this hook. Continue by passing the packet // down the stack - _, ok := metadata["wasm"] + _, ok := jsonObject[key] if !ok { - return false, metadata + return false, jsonObject } - return true, metadata + return true, jsonObject } func ValidateAndParseMemo(memo string, receiver string) (isWasmRouted bool, contractAddr sdk.AccAddress, msgBytes []byte, err error) { - isWasmRouted, metadata := isMemoWasmRouted(memo) + isWasmRouted, metadata := jsonStringHasKey(memo, "wasm") if !isWasmRouted { return isWasmRouted, sdk.AccAddress{}, nil, nil } @@ -202,3 +215,118 @@ func ValidateAndParseMemo(memo string, receiver string) (isWasmRouted bool, cont return isWasmRouted, contractAddr, msgBytes, nil } + +func (h WasmHooks) SendPacketOverride(i ICS4Middleware, ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI) error { + concretePacket, ok := packet.(channeltypes.Packet) + if !ok { + return i.channel.SendPacket(ctx, chanCap, packet) // continue + } + + isIcs20, data := isIcs20Packet(concretePacket) + if !isIcs20 { + return i.channel.SendPacket(ctx, chanCap, packet) // continue + } + + isCallbackRouted, metadata := jsonStringHasKey(data.GetMemo(), types.IBCCallbackKey) + if !isCallbackRouted { + return i.channel.SendPacket(ctx, chanCap, packet) // continue + } + + // We remove the callback metadata from the memo as it has already been processed. + + // If the only available key in the memo is the callback, we should remove the memo + // from the data completely so the packet is sent without it. + // This way receiver chains that are on old versions of IBC will be able to process the packet + + callbackRaw := metadata[types.IBCCallbackKey] // This will be used later. + delete(metadata, types.IBCCallbackKey) + bzMetadata, err := json.Marshal(metadata) + if err != nil { + return sdkerrors.Wrap(err, "Send packet with callback error") + } + stringMetadata := string(bzMetadata) + if stringMetadata == "{}" { + data.Memo = "" + } else { + data.Memo = stringMetadata + } + dataBytes, err := json.Marshal(data) + if err != nil { + return sdkerrors.Wrap(err, "Send packet with callback error") + } + + packetWithoutCallbackMemo := channeltypes.Packet{ + Sequence: concretePacket.Sequence, + SourcePort: concretePacket.SourcePort, + SourceChannel: concretePacket.SourceChannel, + DestinationPort: concretePacket.DestinationPort, + DestinationChannel: concretePacket.DestinationChannel, + Data: dataBytes, + TimeoutTimestamp: concretePacket.TimeoutTimestamp, + TimeoutHeight: concretePacket.TimeoutHeight, + } + + err = i.channel.SendPacket(ctx, chanCap, packetWithoutCallbackMemo) + if err != nil { + return err + } + + // Make sure the callback contract is a string and a valid bech32 addr. If it isn't, ignore this packet + contract, ok := callbackRaw.(string) + if !ok { + return nil + } + _, err = sdk.AccAddressFromBech32(contract) + if err != nil { + return nil + } + + h.ibcHooksKeeper.StorePacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence(), contract) + return nil +} + +func (h WasmHooks) OnAcknowledgementPacketOverride(im IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) error { + err := im.App.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) + if err != nil { + return err + } + + if !h.ProperlyConfigured() { + // Not configured. Return from the underlying implementation + return nil + } + + contract := h.ibcHooksKeeper.GetPacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence()) + if contract == "" { + // No callback configured + return nil + } + + contractAddr, err := sdk.AccAddressFromBech32(contract) + if err != nil { + return sdkerrors.Wrap(err, "Ack callback error") // The callback configured is not a beck32. Error out + } + + success := "false" + if !osmoutils.IsAckError(acknowledgement) { + success = "true" + } + + // Notify the sender that the ack has been received + ackAsJson, err := json.Marshal(acknowledgement) + if err != nil { + // If the ack is not a json object, error + return err + } + + sudoMsg := []byte(fmt.Sprintf( + `{"receive_ack": {"channel": "%s", "sequence": %d, "ack": %s, "success": %s}}`, + packet.SourceChannel, packet.Sequence, ackAsJson, success)) + _, err = h.ContractKeeper.Sudo(ctx, contractAddr, sudoMsg) + if err != nil { + // error processing the callback + return sdkerrors.Wrap(err, "Ack callback error") + } + h.ibcHooksKeeper.DeletePacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence()) + return nil +} diff --git a/x/ibc-rate-limit/testutil/wasm.go b/x/ibc-rate-limit/testutil/wasm.go index 41d624da74a..4f235410806 100644 --- a/x/ibc-rate-limit/testutil/wasm.go +++ b/x/ibc-rate-limit/testutil/wasm.go @@ -59,10 +59,9 @@ func (chain *TestChain) InstantiateRLContract(suite *suite.Suite, quotas string) return addr } -func (chain *TestChain) InstantiateContract(suite *suite.Suite, msg string) sdk.AccAddress { +func (chain *TestChain) InstantiateContract(suite *suite.Suite, msg string, codeID uint64) sdk.AccAddress { osmosisApp := chain.GetOsmosisApp() contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper) - codeID := uint64(1) creator := osmosisApp.AccountKeeper.GetModuleAddress(govtypes.ModuleName) addr, _, err := contractKeeper.Instantiate(chain.GetContext(), codeID, creator, creator, []byte(msg), "contract", nil) suite.Require().NoError(err)